clang 20.0.0 (based on r547379) from build 12806354. Bug: http://b/379133546 Test: N/A Change-Id: I2eb8938af55d809de674be63cb30cf27e801862b Upstream-Commit: ad834e67b1105d15ef907f6255d4c96e8e733f57
347 lines
12 KiB
C++
347 lines
12 KiB
C++
//===------ 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 */
|