//===------ VirtualInstruction.cpp ------------------------------*- C++ -*-===//
 
//
 
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 
// See https://llvm.org/LICENSE.txt for license information.
 
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
//
 
//===----------------------------------------------------------------------===//
 
//
 
// Tools for determining which instructions are within a statement and the
 
// nature of their operands.
 
//
 
//===----------------------------------------------------------------------===//
 
 
 
#ifndef POLLY_SUPPORT_VIRTUALINSTRUCTION_H
 
#define POLLY_SUPPORT_VIRTUALINSTRUCTION_H
 
 
 
#include "polly/ScopInfo.h"
 
 
 
namespace polly {
 
using llvm::User;
 
 
 
/// Determine the nature of a value's use within a statement.
 
///
 
/// These are not always representable by llvm::Use. For instance, scalar write
 
/// MemoryAccesses do use a value, but are not associated with an instruction's
 
/// argument.
 
///
 
/// Despite its name it is not tied to virtual instructions (although it works
 
/// fine with them), but to promote consistent handling of values used in
 
/// statements.
 
class VirtualUse final {
 
public:
 
  /// The different types of uses. Handling usually differentiates a lot between
 
  /// these; one can use a switch to handle each case (and get warned by the
 
  /// compiler if one is not handled).
 
  enum UseKind {
 
    // An llvm::Constant.
 
    Constant,
 
 
 
    // An llvm::BasicBlock.
 
    Block,
 
 
 
    // A value that can be generated using ScopExpander.
 
    Synthesizable,
 
 
 
    // A load that always reads the same value throughout the SCoP (address and
 
    // the value located there a SCoP-invariant) and has been hoisted in front
 
    // of the SCoP.
 
    Hoisted,
 
 
 
    // Definition before the SCoP and not synthesizable. Can be an instruction
 
    // outside the SCoP, a function argument or a global value. Whether there is
 
    // a scalar MemoryAccess in this statement for reading it depends on the
 
    // -polly-analyze-read-only-scalars switch.
 
    ReadOnly,
 
 
 
    // A definition within the same statement. No MemoryAccess between
 
    // definition and use are necessary.
 
    Intra,
 
 
 
    // Definition in another statement. There is a scalar MemoryAccess that
 
    // makes it available in this statement.
 
    Inter
 
  };
 
 
 
private:
 
  /// The statement where a value is used.
 
  ScopStmt *User;
 
 
 
  /// The value that is used.
 
  Value *Val;
 
 
 
  /// The type of value use.
 
  UseKind Kind;
 
 
 
  /// The value represented as llvm::SCEV expression.
 
  const SCEV *ScevExpr;
 
 
 
  /// If this is an inter-statement (or read-only) use, contains the
 
  /// MemoryAccess that makes the value available in this statement. In case of
 
  /// intra-statement uses, can contain a MemoryKind::Array access. In all other
 
  /// cases, it is a nullptr.
 
  MemoryAccess *InputMA;
 
 
 
  VirtualUse(ScopStmt *User, Value *Val, UseKind Kind, const SCEV *ScevExpr,
 
             MemoryAccess *InputMA)
 
      : User(User), Val(Val), Kind(Kind), ScevExpr(ScevExpr), InputMA(InputMA) {
 
  }
 
 
 
public:
 
  /// Get a VirtualUse for an llvm::Use.
 
  ///
 
  /// @param S       The Scop object.
 
  /// @param U       The llvm::Use the get information for.
 
  /// @param LI      The LoopInfo analysis. Needed to determine whether the
 
  ///                value is synthesizable.
 
  /// @param Virtual Whether to ignore existing MemoryAccess.
 
  ///
 
  /// @return The VirtualUse representing the same use as @p U.
 
  static VirtualUse create(Scop *S, const Use &U, LoopInfo *LI, bool Virtual);
 
 
 
  /// Get a VirtualUse for uses within statements.
 
  ///
 
  /// It is assumed that the user is not a PHINode. Such uses are always
 
  /// VirtualUse::Inter unless in a regions statement.
 
  ///
 
  /// @param S         The Scop object.
 
  /// @param UserStmt  The statement in which @p Val is used. Can be nullptr, in
 
  ///                  which case it assumed that the statement has been
 
  ///                  removed, which is only possible if no instruction in it
 
  ///                  had side-effects or computes a value used by another
 
  ///                  statement.
 
  /// @param UserScope Loop scope in which the value is used. Needed to
 
  ///                  determine whether the value is synthesizable.
 
  /// @param Val       The value being used.
 
  /// @param Virtual   Whether to use (and prioritize over instruction location)
 
  ///                  information about MemoryAccesses.
 
  ///
 
  /// @return A VirtualUse object that gives information about @p Val's use in
 
  ///         @p UserStmt.
 
  static VirtualUse create(Scop *S, ScopStmt *UserStmt, Loop *UserScope,
 
                           Value *Val, bool Virtual);
 
 
 
  static VirtualUse create(ScopStmt *UserStmt, Loop *UserScope, Value *Val,
 
                           bool Virtual) {
 
    return create(UserStmt->getParent(), UserStmt, UserScope, Val, Virtual);
 
  }
 
 
 
  bool isConstant() const { return Kind == Constant; }
 
  bool isBlock() const { return Kind == Block; }
 
  bool isSynthesizable() const { return Kind == Synthesizable; }
 
  bool isHoisted() const { return Kind == Hoisted; }
 
  bool isReadOnly() const { return Kind == ReadOnly; }
 
  bool isIntra() const { return Kind == Intra; }
 
  bool isInter() const { return Kind == Inter; }
 
 
 
  /// Return user statement.
 
  ScopStmt *getUser() const { return User; }
 
 
 
  /// Return the used value.
 
  llvm::Value *getValue() const { return Val; }
 
 
 
  /// Return the type of use.
 
  UseKind getKind() const { return Kind; }
 
 
 
  /// Return the ScalarEvolution representation of @p Val.
 
  const SCEV *getScevExpr() const { return ScevExpr; }
 
 
 
  /// Return the MemoryAccess that makes the value available in this statement,
 
  /// if any.
 
  MemoryAccess *getMemoryAccess() const { return InputMA; }
 
 
 
  /// Print a description of this object.
 
  ///
 
  /// @param OS           Stream to print to.
 
  /// @param Reproducible If true, ensures that the output is stable between
 
  ///                     runs and is suitable to check in regression tests.
 
  ///                     This excludes printing e.g. pointer values. If false,
 
  ///                     the output should not be used for regression tests,
 
  ///                     but may contain more information useful in debugger
 
  ///                     sessions.
 
  void print(raw_ostream &OS, bool Reproducible = true) const;
 
 
 
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
 
  void dump() const;
 
#endif
 
};
 
 
 
/// An iterator for virtual operands.
 
class VirtualOperandIterator final {
 
  friend class VirtualInstruction;
 
  friend class VirtualUse;
 
 
 
  using Self = VirtualOperandIterator;
 
 
 
  ScopStmt *User;
 
  User::op_iterator U;
 
 
 
  VirtualOperandIterator(ScopStmt *User, User::op_iterator U)
 
      : User(User), U(U) {}
 
 
 
public:
 
  using iterator_category = std::forward_iterator_tag;
 
  using value_type = VirtualUse;
 
  using difference_type = std::ptrdiff_t;
 
  using pointer = value_type *;
 
  using reference = value_type &;
 
 
 
  inline bool operator==(const Self &that) const {
 
    assert(this->User == that.User);
 
    return this->U == that.U;
 
  }
 
 
 
  inline bool operator!=(const Self &that) const {
 
    assert(this->User == that.User);
 
    return this->U != that.U;
 
  }
 
 
 
  VirtualUse operator*() const {
 
    return VirtualUse::create(User, User->getSurroundingLoop(), U->get(), true);
 
  }
 
 
 
  Use *operator->() const { return U; }
 
 
 
  Self &operator++() {
 
    U++;
 
    return *this;
 
  }
 
 
 
  Self operator++(int) {
 
    Self tmp = *this;
 
    ++*this;
 
    return tmp;
 
  }
 
};
 
 
 
/// This class represents a "virtual instruction", an instruction in a ScopStmt,
 
/// effectively a ScopStmt/Instruction-pair.
 
///
 
/// An instructions can be moved between statements (e.g. to avoid a scalar
 
/// dependency) and even can be contained in multiple statements (for instance,
 
/// to recompute a value instead of transferring it), hence 'virtual'. This
 
/// class is required to represent such instructions that are not in their
 
/// 'physical' location anymore.
 
///
 
/// A statement can currently not contain the same instructions multiple times
 
/// (that is, from different loop iterations). Therefore, a
 
/// ScopStmt/Instruction-pair uniquely identifies a virtual instructions.
 
/// ScopStmt::getInstruction() can contain the same instruction multiple times,
 
/// but they necessarily compute the same value.
 
class VirtualInstruction final {
 
  friend class VirtualOperandIterator;
 
  friend struct llvm::DenseMapInfo<VirtualInstruction>;
 
 
 
private:
 
  /// The statement this virtual instruction is in.
 
  ScopStmt *Stmt = nullptr;
 
 
 
  /// The instruction of a statement.
 
  Instruction *Inst = nullptr;
 
 
 
public:
 
  VirtualInstruction() {}
 
 
 
  /// Create a new virtual instruction of an instruction @p Inst in @p Stmt.
 
  VirtualInstruction(ScopStmt *Stmt, Instruction *Inst)
 
      : Stmt(Stmt), Inst(Inst) {
 
    assert(Stmt && Inst);
 
  }
 
 
 
  VirtualOperandIterator operand_begin() const {
 
    return VirtualOperandIterator(Stmt, Inst->op_begin());
 
  }
 
 
 
  VirtualOperandIterator operand_end() const {
 
    return VirtualOperandIterator(Stmt, Inst->op_end());
 
  }
 
 
 
  /// Returns a list of virtual operands.
 
  ///
 
  /// Virtual operands, like virtual instructions, need to encode the ScopStmt
 
  /// they are in.
 
  llvm::iterator_range<VirtualOperandIterator> operands() const {
 
    return {operand_begin(), operand_end()};
 
  }
 
 
 
  /// Return the SCoP everything is contained in.
 
  Scop *getScop() const { return Stmt->getParent(); }
 
 
 
  /// Return the ScopStmt this virtual instruction is in.
 
  ScopStmt *getStmt() const { return Stmt; }
 
 
 
  /// Return the instruction in the statement.
 
  Instruction *getInstruction() const { return Inst; }
 
 
 
  /// Print a description of this object.
 
  ///
 
  /// @param OS           Stream to print to.
 
  /// @param Reproducible If true, ensures that the output is stable between
 
  ///                     runs and is suitable for checks in regression tests.
 
  ///                     This excludes printing e.g., pointer values. If false,
 
  ///                     the output should not be used for regression tests,
 
  ///                     but may contain more information useful in debugger
 
  ///                     sessions.
 
  void print(raw_ostream &OS, bool Reproducible = true) const;
 
 
 
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
 
  void dump() const;
 
#endif
 
};
 
 
 
static inline bool operator==(VirtualInstruction LHS, VirtualInstruction RHS) {
 
  return LHS.getStmt() == RHS.getStmt() &&
 
         LHS.getInstruction() == RHS.getInstruction();
 
}
 
 
 
/// Find all reachable instructions and accesses.
 
///
 
/// @param S              The SCoP to find everything reachable in.
 
/// @param LI             LoopInfo required for analysis.
 
/// @param UsedInsts[out] Receives all reachable instructions.
 
/// @param UsedAccs[out]  Receives all reachable accesses.
 
/// @param OnlyLocal      If non-nullptr, activates local mode: The SCoP is
 
///                       assumed to consist only of this statement and is
 
///                       conservatively correct. Does not require walking the
 
///                       whole SCoP.
 
void markReachable(Scop *S, LoopInfo *LI,
 
                   DenseSet<VirtualInstruction> &UsedInsts,
 
                   DenseSet<MemoryAccess *> &UsedAccs,
 
                   ScopStmt *OnlyLocal = nullptr);
 
} // namespace polly
 
 
 
namespace llvm {
 
/// Support VirtualInstructions in llvm::DenseMaps.
 
template <> struct DenseMapInfo<polly::VirtualInstruction> {
 
public:
 
  static bool isEqual(polly::VirtualInstruction LHS,
 
                      polly::VirtualInstruction RHS) {
 
    return DenseMapInfo<polly::ScopStmt *>::isEqual(LHS.getStmt(),
 
                                                    RHS.getStmt()) &&
 
           DenseMapInfo<Instruction *>::isEqual(LHS.getInstruction(),
 
                                                RHS.getInstruction());
 
  }
 
 
 
  static polly::VirtualInstruction getTombstoneKey() {
 
    polly::VirtualInstruction TombstoneKey;
 
    TombstoneKey.Stmt = DenseMapInfo<polly::ScopStmt *>::getTombstoneKey();
 
    TombstoneKey.Inst = DenseMapInfo<Instruction *>::getTombstoneKey();
 
    return TombstoneKey;
 
  }
 
 
 
  static polly::VirtualInstruction getEmptyKey() {
 
    polly::VirtualInstruction EmptyKey;
 
    EmptyKey.Stmt = DenseMapInfo<polly::ScopStmt *>::getEmptyKey();
 
    EmptyKey.Inst = DenseMapInfo<Instruction *>::getEmptyKey();
 
    return EmptyKey;
 
  }
 
 
 
  static unsigned getHashValue(polly::VirtualInstruction Val) {
 
    return DenseMapInfo<std::pair<polly::ScopStmt *, Instruction *>>::
 
        getHashValue(std::make_pair(Val.getStmt(), Val.getInstruction()));
 
  }
 
};
 
} // namespace llvm
 
 
 
#endif /* POLLY_SUPPORT_VIRTUALINSTRUCTION_H */