The canonical IV is directly tied to a loop region. To directly ensure there's a single, unique canonical IV, directly define it by the region. Depends on https://github.com/llvm/llvm-project/pull/161589. PR: https://github.com/llvm/llvm-project/pull/156262
510 lines
17 KiB
C++
510 lines
17 KiB
C++
//===- VPlanValue.h - Represent Values in Vectorizer Plan -----------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
/// This file contains the declarations of the entities induced by Vectorization
|
|
/// Plans, e.g. the instructions the VPlan intends to generate if executed.
|
|
/// VPlan models the following entities:
|
|
/// VPValue VPUser VPDef
|
|
/// | |
|
|
/// VPInstruction
|
|
/// These are documented in docs/VectorizationPlan.rst.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_TRANSFORMS_VECTORIZE_VPLAN_VALUE_H
|
|
#define LLVM_TRANSFORMS_VECTORIZE_VPLAN_VALUE_H
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/TinyPtrVector.h"
|
|
#include "llvm/ADT/iterator_range.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/DebugLoc.h"
|
|
#include "llvm/Support/Casting.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
|
|
namespace llvm {
|
|
|
|
// Forward declarations.
|
|
class raw_ostream;
|
|
class Type;
|
|
class Value;
|
|
class VPDef;
|
|
class VPSlotTracker;
|
|
class VPUser;
|
|
class VPRecipeBase;
|
|
class VPPhiAccessors;
|
|
class VPRegionValue;
|
|
class VPRegionBlock;
|
|
|
|
/// This is the base class of the VPlan Def/Use graph, used for modeling the
|
|
/// data flow into, within and out of the VPlan. VPValues can stand for live-ins
|
|
/// coming from the input IR, symbolic values and values defined by recipes.
|
|
class LLVM_ABI_FOR_TEST VPValue {
|
|
friend struct VPIRValue;
|
|
friend struct VPSymbolicValue;
|
|
friend class VPRecipeValue;
|
|
friend class VPRegionValue;
|
|
|
|
const unsigned char SubclassID; ///< Subclass identifier (for isa/dyn_cast).
|
|
|
|
SmallVector<VPUser *, 1> Users;
|
|
|
|
/// Hold the underlying Value, if any, attached to this VPValue.
|
|
Value *UnderlyingVal;
|
|
|
|
VPValue(const unsigned char SC, Value *UV = nullptr)
|
|
: SubclassID(SC), UnderlyingVal(UV) {}
|
|
|
|
// DESIGN PRINCIPLE: Access to the underlying IR must be strictly limited to
|
|
// the front-end and back-end of VPlan so that the middle-end is as
|
|
// independent as possible of the underlying IR. We grant access to the
|
|
// underlying IR using friendship. In that way, we should be able to use VPlan
|
|
// for multiple underlying IRs (Polly?) by providing a new VPlan front-end,
|
|
// back-end and analysis information for the new IR.
|
|
|
|
public:
|
|
/// Return the underlying Value attached to this VPValue.
|
|
Value *getUnderlyingValue() const { return UnderlyingVal; }
|
|
|
|
/// Return the underlying IR value for a VPIRValue.
|
|
Value *getLiveInIRValue() const;
|
|
|
|
/// An enumeration for keeping track of the concrete subclass of VPValue that
|
|
/// are actually instantiated.
|
|
enum {
|
|
VPVIRValueSC, /// A live-in VPValue wrapping an IR Value.
|
|
VPVSymbolicSC, /// A symbolic live-in VPValue without IR backing.
|
|
VPVRecipeValueSC, /// A VPValue defined by a recipe.
|
|
VPRegionValueSC, /// A VPValue sub-class that is defined by a region, like
|
|
/// the canonical IV of a loop region.
|
|
};
|
|
|
|
VPValue(const VPValue &) = delete;
|
|
VPValue &operator=(const VPValue &) = delete;
|
|
|
|
virtual ~VPValue() {
|
|
assert(Users.empty() && "trying to delete a VPValue with remaining users");
|
|
}
|
|
|
|
/// \return an ID for the concrete type of this object.
|
|
/// This is used to implement the classof checks. This should not be used
|
|
/// for any other purpose, as the values may change as LLVM evolves.
|
|
unsigned getVPValueID() const { return SubclassID; }
|
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
|
void printAsOperand(raw_ostream &OS, VPSlotTracker &Tracker) const;
|
|
void print(raw_ostream &OS, VPSlotTracker &Tracker) const;
|
|
|
|
/// Dump the value to stderr (for debugging).
|
|
void dump() const;
|
|
#endif
|
|
|
|
/// Assert that this VPValue has not been materialized, if it is a
|
|
/// VPSymbolicValue.
|
|
void assertNotMaterialized() const;
|
|
|
|
unsigned getNumUsers() const {
|
|
if (Users.empty())
|
|
return 0;
|
|
assertNotMaterialized();
|
|
return Users.size();
|
|
}
|
|
void addUser(VPUser &User) {
|
|
assertNotMaterialized();
|
|
Users.push_back(&User);
|
|
}
|
|
|
|
/// Remove a single \p User from the list of users.
|
|
void removeUser(VPUser &User) {
|
|
assertNotMaterialized();
|
|
// The same user can be added multiple times, e.g. because the same VPValue
|
|
// is used twice by the same VPUser. Remove a single one.
|
|
auto *I = find(Users, &User);
|
|
if (I != Users.end())
|
|
Users.erase(I);
|
|
}
|
|
|
|
typedef SmallVectorImpl<VPUser *>::iterator user_iterator;
|
|
typedef SmallVectorImpl<VPUser *>::const_iterator const_user_iterator;
|
|
typedef iterator_range<user_iterator> user_range;
|
|
typedef iterator_range<const_user_iterator> const_user_range;
|
|
|
|
user_iterator user_begin() {
|
|
assertNotMaterialized();
|
|
return Users.begin();
|
|
}
|
|
const_user_iterator user_begin() const {
|
|
assertNotMaterialized();
|
|
return Users.begin();
|
|
}
|
|
user_iterator user_end() {
|
|
assertNotMaterialized();
|
|
return Users.end();
|
|
}
|
|
const_user_iterator user_end() const {
|
|
assertNotMaterialized();
|
|
return Users.end();
|
|
}
|
|
user_range users() { return user_range(user_begin(), user_end()); }
|
|
const_user_range users() const {
|
|
return const_user_range(user_begin(), user_end());
|
|
}
|
|
|
|
/// Returns true if the value has more than one unique user.
|
|
bool hasMoreThanOneUniqueUser() const {
|
|
if (getNumUsers() == 0)
|
|
return false;
|
|
|
|
// Check if all users match the first user.
|
|
auto Current = std::next(user_begin());
|
|
while (Current != user_end() && *user_begin() == *Current)
|
|
Current++;
|
|
return Current != user_end();
|
|
}
|
|
|
|
bool hasOneUse() const { return getNumUsers() == 1; }
|
|
|
|
/// Return the single user of this value, or nullptr if there is not exactly
|
|
/// one user.
|
|
VPUser *getSingleUser() { return hasOneUse() ? *user_begin() : nullptr; }
|
|
const VPUser *getSingleUser() const {
|
|
return hasOneUse() ? *user_begin() : nullptr;
|
|
}
|
|
|
|
void replaceAllUsesWith(VPValue *New);
|
|
|
|
/// Go through the uses list for this VPValue and make each use point to \p
|
|
/// New if the callback ShouldReplace returns true for the given use specified
|
|
/// by a pair of (VPUser, the use index).
|
|
void replaceUsesWithIf(
|
|
VPValue *New,
|
|
llvm::function_ref<bool(VPUser &U, unsigned Idx)> ShouldReplace);
|
|
|
|
/// Returns the recipe defining this VPValue or nullptr if it is not defined
|
|
/// by a recipe, i.e. is a live-in.
|
|
VPRecipeBase *getDefiningRecipe();
|
|
const VPRecipeBase *getDefiningRecipe() const;
|
|
|
|
/// Returns true if this VPValue is defined by a recipe.
|
|
bool hasDefiningRecipe() const { return getDefiningRecipe(); }
|
|
|
|
/// Returns true if the VPValue is defined outside any loop.
|
|
bool isDefinedOutsideLoopRegions() const;
|
|
|
|
// Set \p Val as the underlying Value of this VPValue.
|
|
void setUnderlyingValue(Value *Val) {
|
|
assert(!UnderlyingVal && "Underlying Value is already set.");
|
|
UnderlyingVal = Val;
|
|
}
|
|
};
|
|
|
|
/// VPValues defined by a VPRegionBlock, like the canonical IV.
|
|
class VPRegionValue : public VPValue {
|
|
VPRegionBlock *DefiningRegion;
|
|
Type *Ty;
|
|
DebugLoc DL;
|
|
|
|
public:
|
|
VPRegionValue(Type *Ty, DebugLoc DL, VPRegionBlock *Region)
|
|
: VPValue(VPValue::VPRegionValueSC), DefiningRegion(Region), Ty(Ty),
|
|
DL(DL) {}
|
|
|
|
~VPRegionValue() override = default;
|
|
|
|
/// Returns the region that defines this value.
|
|
VPRegionBlock *getDefiningRegion() const { return DefiningRegion; }
|
|
|
|
/// Returns the type of the VPRegionValue.
|
|
Type *getType() const { return Ty; }
|
|
|
|
/// Returns the debug location of the VPRegionValue.
|
|
DebugLoc getDebugLoc() const { return DL; }
|
|
|
|
static inline bool classof(const VPValue *V) {
|
|
return V->getVPValueID() == VPValue::VPRegionValueSC;
|
|
}
|
|
};
|
|
|
|
LLVM_ABI_FOR_TEST raw_ostream &operator<<(raw_ostream &OS,
|
|
const VPRecipeBase &R);
|
|
|
|
/// A VPValue representing a live-in from the input IR or a constant. It wraps
|
|
/// an underlying IR Value.
|
|
struct VPIRValue : public VPValue {
|
|
VPIRValue(Value *UV) : VPValue(VPVIRValueSC, UV) {
|
|
assert(UV && "VPIRValue requires an underlying IR value");
|
|
}
|
|
|
|
/// Returns the underlying IR value.
|
|
Value *getValue() const { return getUnderlyingValue(); }
|
|
|
|
/// Returns the type of the underlying IR value.
|
|
Type *getType() const;
|
|
|
|
static bool classof(const VPValue *V) {
|
|
return V->getVPValueID() == VPVIRValueSC;
|
|
}
|
|
};
|
|
|
|
/// An overlay on VPIRValue for VPValues that wrap a ConstantInt. Provides
|
|
/// convenient accessors for the underlying constant.
|
|
struct VPConstantInt : public VPIRValue {
|
|
VPConstantInt(ConstantInt *CI) : VPIRValue(CI) {}
|
|
|
|
static bool classof(const VPValue *V) {
|
|
return isa<VPIRValue>(V) && isa<ConstantInt>(V->getUnderlyingValue());
|
|
}
|
|
|
|
bool isOne() const { return getAPInt().isOne(); }
|
|
|
|
bool isZero() const { return getAPInt().isZero(); }
|
|
|
|
const APInt &getAPInt() const {
|
|
return cast<ConstantInt>(getValue())->getValue();
|
|
}
|
|
|
|
unsigned getBitWidth() const { return getAPInt().getBitWidth(); }
|
|
|
|
uint64_t getZExtValue() const { return getAPInt().getZExtValue(); }
|
|
};
|
|
|
|
/// A symbolic live-in VPValue, used for values like vector trip count, VF, and
|
|
/// VFxUF.
|
|
struct VPSymbolicValue : public VPValue {
|
|
VPSymbolicValue() : VPValue(VPVSymbolicSC, nullptr) {}
|
|
|
|
static bool classof(const VPValue *V) {
|
|
return V->getVPValueID() == VPVSymbolicSC;
|
|
}
|
|
|
|
/// Returns true if this symbolic value has been materialized.
|
|
bool isMaterialized() const { return Materialized; }
|
|
|
|
/// Mark this symbolic value as materialized.
|
|
void markMaterialized() {
|
|
assert(!Materialized && "VPSymbolicValue already materialized");
|
|
Materialized = true;
|
|
}
|
|
|
|
private:
|
|
/// Track whether this symbolic value has been materialized (replaced).
|
|
/// After materialization, accessing users should trigger an assertion.
|
|
bool Materialized = false;
|
|
};
|
|
|
|
/// A VPValue defined by a recipe that produces one or more values.
|
|
class VPRecipeValue : public VPValue {
|
|
friend class VPValue;
|
|
friend class VPDef;
|
|
|
|
/// Pointer to the VPRecipeBase that defines this VPValue.
|
|
VPRecipeBase *Def;
|
|
|
|
#if !defined(NDEBUG)
|
|
/// Returns true if this VPRecipeValue is defined by \p D.
|
|
/// NOTE: Only used by VPDef to assert that VPRecipeValues added/removed from
|
|
/// /p D are associated with its VPRecipeBase,
|
|
bool isDefinedBy(const VPDef *D) const;
|
|
#endif
|
|
|
|
public:
|
|
LLVM_ABI_FOR_TEST VPRecipeValue(VPRecipeBase *Def, Value *UV = nullptr);
|
|
|
|
LLVM_ABI_FOR_TEST virtual ~VPRecipeValue();
|
|
|
|
static bool classof(const VPValue *V) {
|
|
return V->getVPValueID() == VPVRecipeValueSC;
|
|
}
|
|
};
|
|
|
|
/// This class augments VPValue with operands which provide the inverse def-use
|
|
/// edges from VPValue's users to their defs.
|
|
class VPUser {
|
|
/// Grant access to removeOperand for VPPhiAccessors, the only supported user.
|
|
friend class VPPhiAccessors;
|
|
|
|
SmallVector<VPValue *, 2> Operands;
|
|
|
|
/// Removes the operand at index \p Idx. This also removes the VPUser from the
|
|
/// use-list of the operand.
|
|
void removeOperand(unsigned Idx) {
|
|
getOperand(Idx)->removeUser(*this);
|
|
Operands.erase(Operands.begin() + Idx);
|
|
}
|
|
|
|
protected:
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
|
/// Print the operands to \p O.
|
|
void printOperands(raw_ostream &O, VPSlotTracker &SlotTracker) const;
|
|
#endif
|
|
|
|
VPUser(ArrayRef<VPValue *> Operands) {
|
|
for (VPValue *Operand : Operands)
|
|
addOperand(Operand);
|
|
}
|
|
|
|
public:
|
|
VPUser() = delete;
|
|
VPUser(const VPUser &) = delete;
|
|
VPUser &operator=(const VPUser &) = delete;
|
|
virtual ~VPUser() {
|
|
for (VPValue *Op : operands())
|
|
Op->removeUser(*this);
|
|
}
|
|
|
|
void addOperand(VPValue *Operand) {
|
|
Operands.push_back(Operand);
|
|
Operand->addUser(*this);
|
|
}
|
|
|
|
unsigned getNumOperands() const { return Operands.size(); }
|
|
inline VPValue *getOperand(unsigned N) const {
|
|
assert(N < Operands.size() && "Operand index out of bounds");
|
|
return Operands[N];
|
|
}
|
|
|
|
void setOperand(unsigned I, VPValue *New) {
|
|
Operands[I]->removeUser(*this);
|
|
Operands[I] = New;
|
|
New->addUser(*this);
|
|
}
|
|
|
|
/// Swap operands of the VPUser. It must have exactly 2 operands.
|
|
void swapOperands() {
|
|
assert(Operands.size() == 2 && "must have 2 operands to swap");
|
|
std::swap(Operands[0], Operands[1]);
|
|
}
|
|
|
|
/// Replaces all uses of \p From in the VPUser with \p To.
|
|
void replaceUsesOfWith(VPValue *From, VPValue *To);
|
|
|
|
typedef SmallVectorImpl<VPValue *>::iterator operand_iterator;
|
|
typedef SmallVectorImpl<VPValue *>::const_iterator const_operand_iterator;
|
|
typedef iterator_range<operand_iterator> operand_range;
|
|
typedef iterator_range<const_operand_iterator> const_operand_range;
|
|
|
|
operand_iterator op_begin() { return Operands.begin(); }
|
|
const_operand_iterator op_begin() const { return Operands.begin(); }
|
|
operand_iterator op_end() { return Operands.end(); }
|
|
const_operand_iterator op_end() const { return Operands.end(); }
|
|
operand_range operands() { return operand_range(op_begin(), op_end()); }
|
|
const_operand_range operands() const {
|
|
return const_operand_range(op_begin(), op_end());
|
|
}
|
|
|
|
/// Returns true if the VPUser uses scalars of operand \p Op. Conservatively
|
|
/// returns if only first (scalar) lane is used, as default.
|
|
virtual bool usesScalars(const VPValue *Op) const {
|
|
assert(is_contained(operands(), Op) &&
|
|
"Op must be an operand of the recipe");
|
|
return usesFirstLaneOnly(Op);
|
|
}
|
|
|
|
/// Returns true if the VPUser only uses the first lane of operand \p Op.
|
|
/// Conservatively returns false.
|
|
virtual bool usesFirstLaneOnly(const VPValue *Op) const {
|
|
assert(is_contained(operands(), Op) &&
|
|
"Op must be an operand of the recipe");
|
|
return false;
|
|
}
|
|
|
|
/// Returns true if the VPUser only uses the first part of operand \p Op.
|
|
/// Conservatively returns false.
|
|
virtual bool usesFirstPartOnly(const VPValue *Op) const {
|
|
assert(is_contained(operands(), Op) &&
|
|
"Op must be an operand of the recipe");
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/// This class augments a recipe with a set of VPValues defined by the recipe.
|
|
/// It allows recipes to define zero, one or multiple VPValues. A VPDef owns
|
|
/// the VPValues it defines and is responsible for deleting its defined values.
|
|
/// Single-value VPDefs that also inherit from VPValue must make sure to inherit
|
|
/// from VPDef before VPValue.
|
|
class VPDef {
|
|
friend class VPRecipeValue;
|
|
|
|
/// The VPValues defined by this VPDef.
|
|
TinyPtrVector<VPRecipeValue *> DefinedValues;
|
|
|
|
/// Add \p V as a defined value by this VPDef.
|
|
void addDefinedValue(VPRecipeValue *V) {
|
|
assert(V->isDefinedBy(this) &&
|
|
"can only add VPValue already linked with this VPDef");
|
|
DefinedValues.push_back(V);
|
|
}
|
|
|
|
/// Remove \p V from the values defined by this VPDef. \p V must be a defined
|
|
/// value of this VPDef.
|
|
void removeDefinedValue(VPRecipeValue *V) {
|
|
assert(V->isDefinedBy(this) &&
|
|
"can only remove VPValue linked with this VPDef");
|
|
assert(is_contained(DefinedValues, V) &&
|
|
"VPValue to remove must be in DefinedValues");
|
|
llvm::erase(DefinedValues, V);
|
|
V->Def = nullptr;
|
|
}
|
|
|
|
public:
|
|
VPDef() {}
|
|
|
|
virtual ~VPDef() {
|
|
for (VPRecipeValue *D : to_vector(DefinedValues)) {
|
|
assert(D->isDefinedBy(this) &&
|
|
"all defined VPValues should point to the containing VPDef");
|
|
assert(D->getNumUsers() == 0 &&
|
|
"all defined VPValues should have no more users");
|
|
delete D;
|
|
}
|
|
}
|
|
|
|
/// Returns the only VPValue defined by the VPDef. Can only be called for
|
|
/// VPDefs with a single defined value.
|
|
VPValue *getVPSingleValue() {
|
|
assert(DefinedValues.size() == 1 && "must have exactly one defined value");
|
|
assert(DefinedValues[0] && "defined value must be non-null");
|
|
return DefinedValues[0];
|
|
}
|
|
const VPValue *getVPSingleValue() const {
|
|
assert(DefinedValues.size() == 1 && "must have exactly one defined value");
|
|
assert(DefinedValues[0] && "defined value must be non-null");
|
|
return DefinedValues[0];
|
|
}
|
|
|
|
/// Returns the VPValue with index \p I defined by the VPDef.
|
|
VPValue *getVPValue(unsigned I) {
|
|
assert(DefinedValues[I] && "defined value must be non-null");
|
|
return DefinedValues[I];
|
|
}
|
|
const VPValue *getVPValue(unsigned I) const {
|
|
assert(DefinedValues[I] && "defined value must be non-null");
|
|
return DefinedValues[I];
|
|
}
|
|
|
|
/// Returns an ArrayRef of the values defined by the VPDef.
|
|
ArrayRef<VPRecipeValue *> definedValues() { return DefinedValues; }
|
|
/// Returns an ArrayRef of the values defined by the VPDef.
|
|
ArrayRef<VPRecipeValue *> definedValues() const { return DefinedValues; }
|
|
|
|
/// Returns the number of values defined by the VPDef.
|
|
unsigned getNumDefinedValues() const { return DefinedValues.size(); }
|
|
};
|
|
|
|
inline void VPValue::assertNotMaterialized() const {
|
|
assert((!isa<VPSymbolicValue>(this) ||
|
|
!cast<VPSymbolicValue>(this)->isMaterialized()) &&
|
|
"accessing materialized symbolic value");
|
|
}
|
|
|
|
} // namespace llvm
|
|
|
|
#endif // LLVM_TRANSFORMS_VECTORIZE_VPLAN_VALUE_H
|