blob: da844e317abf32031f5bcb9092e505cc5f2ea02f [file] [edit]
//===- RISCVVSETVLIInfoAnalysis.h - VSETVLI Info Analysis -----------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file implements an analysis of the vtype/vl information that is needed
// by RISCVInsertVSETVLI pass and others.
//
//===----------------------------------------------------------------------===//
#include "RISCV.h"
#include "RISCVSubtarget.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/CodeGen/LiveDebugVariables.h"
#include "llvm/CodeGen/LiveIntervals.h"
#include "llvm/CodeGen/LiveStacks.h"
namespace llvm {
namespace RISCV {
/// Which subfields of VL or VTYPE have values we need to preserve?
struct DemandedFields {
// Some unknown property of VL is used. If demanded, must preserve entire
// value.
bool VLAny = false;
// Only zero vs non-zero is used. If demanded, can change non-zero values.
bool VLZeroness = false;
// What properties of SEW we need to preserve.
enum : uint8_t {
SEWEqual = 3, // The exact value of SEW needs to be preserved.
SEWGreaterThanOrEqualAndLessThan64 =
2, // SEW can be changed as long as it's greater
// than or equal to the original value, but must be less
// than 64.
SEWGreaterThanOrEqual = 1, // SEW can be changed as long as it's greater
// than or equal to the original value.
SEWNone = 0 // We don't need to preserve SEW at all.
} SEW = SEWNone;
enum : uint8_t {
LMULEqual = 2, // The exact value of LMUL needs to be preserved.
LMULLessThanOrEqualToM1 = 1, // We can use any LMUL <= M1.
LMULNone = 0 // We don't need to preserve LMUL at all.
} LMUL = LMULNone;
bool SEWLMULRatio = false;
bool TailPolicy = false;
bool MaskPolicy = false;
// If this is true, we demand that VTYPE is set to some legal state, i.e. that
// vill is unset.
bool VILL = false;
bool TWiden = false;
bool AltFmt = false;
// Return true if any part of VTYPE was used
bool usedVTYPE() const {
return SEW || LMUL || SEWLMULRatio || TailPolicy || MaskPolicy || VILL ||
TWiden || AltFmt;
}
// Return true if any property of VL was used
bool usedVL() { return VLAny || VLZeroness; }
// Mark all VTYPE subfields and properties as demanded
void demandVTYPE() {
SEW = SEWEqual;
LMUL = LMULEqual;
SEWLMULRatio = true;
TailPolicy = true;
MaskPolicy = true;
VILL = true;
TWiden = true;
AltFmt = true;
}
// Mark all VL properties as demanded
void demandVL() {
VLAny = true;
VLZeroness = true;
}
static DemandedFields all() {
DemandedFields DF;
DF.demandVTYPE();
DF.demandVL();
return DF;
}
// Make this the result of demanding both the fields in this and B.
void doUnion(const DemandedFields &B) {
VLAny |= B.VLAny;
VLZeroness |= B.VLZeroness;
SEW = std::max(SEW, B.SEW);
LMUL = std::max(LMUL, B.LMUL);
SEWLMULRatio |= B.SEWLMULRatio;
TailPolicy |= B.TailPolicy;
MaskPolicy |= B.MaskPolicy;
VILL |= B.VILL;
AltFmt |= B.AltFmt;
TWiden |= B.TWiden;
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
/// Support for debugging, callable in GDB: V->dump()
LLVM_DUMP_METHOD void dump() const {
print(dbgs());
dbgs() << "\n";
}
/// Implement operator<<.
void print(raw_ostream &OS) const {
OS << "{";
OS << "VLAny=" << VLAny << ", ";
OS << "VLZeroness=" << VLZeroness << ", ";
OS << "SEW=";
switch (SEW) {
case SEWEqual:
OS << "SEWEqual";
break;
case SEWGreaterThanOrEqual:
OS << "SEWGreaterThanOrEqual";
break;
case SEWGreaterThanOrEqualAndLessThan64:
OS << "SEWGreaterThanOrEqualAndLessThan64";
break;
case SEWNone:
OS << "SEWNone";
break;
};
OS << ", ";
OS << "LMUL=";
switch (LMUL) {
case LMULEqual:
OS << "LMULEqual";
break;
case LMULLessThanOrEqualToM1:
OS << "LMULLessThanOrEqualToM1";
break;
case LMULNone:
OS << "LMULNone";
break;
};
OS << ", ";
OS << "SEWLMULRatio=" << SEWLMULRatio << ", ";
OS << "TailPolicy=" << TailPolicy << ", ";
OS << "MaskPolicy=" << MaskPolicy << ", ";
OS << "VILL=" << VILL << ", ";
OS << "AltFmt=" << AltFmt << ", ";
OS << "TWiden=" << TWiden;
OS << "}";
}
#endif
};
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_ATTRIBUTE_USED
inline raw_ostream &operator<<(raw_ostream &OS, const DemandedFields &DF) {
DF.print(OS);
return OS;
}
#endif
bool areCompatibleVTYPEs(uint64_t CurVType, uint64_t NewVType,
const DemandedFields &Used);
/// Return the fields and properties demanded by the provided instruction.
DemandedFields getDemanded(const MachineInstr &MI, const RISCVSubtarget *ST);
/// Defines the abstract state with which the forward dataflow models the
/// values of the VL and VTYPE registers after insertion.
class VSETVLIInfo {
struct AVLDef {
// Every AVLDef should have a VNInfo, unless we're running without
// LiveIntervals in which case this will be nullptr.
const VNInfo *ValNo;
Register DefReg;
};
union {
AVLDef AVLRegDef;
unsigned AVLImm;
};
enum class AVLState : uint8_t {
Uninitialized,
AVLIsReg,
AVLIsImm,
AVLIsVLMAX,
Unknown, // AVL and VTYPE are fully unknown
} State = AVLState::Uninitialized;
// Fields from VTYPE.
RISCVVType::VLMUL VLMul = RISCVVType::LMUL_1;
uint8_t SEW = 0;
uint8_t TailAgnostic : 1;
uint8_t MaskAgnostic : 1;
uint8_t SEWLMULRatioOnly : 1;
uint8_t AltFmt : 1;
uint8_t TWiden : 3;
public:
VSETVLIInfo()
: AVLImm(0), TailAgnostic(false), MaskAgnostic(false),
SEWLMULRatioOnly(false), AltFmt(false), TWiden(0) {}
static VSETVLIInfo getUnknown() {
VSETVLIInfo Info;
Info.setUnknown();
return Info;
}
bool isValid() const { return State != AVLState::Uninitialized; }
void setUnknown() { State = AVLState::Unknown; }
bool isUnknown() const { return State == AVLState::Unknown; }
bool isKnown() const { return isValid() && !isUnknown(); }
void setAVLRegDef(const VNInfo *VNInfo, Register AVLReg) {
assert(AVLReg.isVirtual());
AVLRegDef.ValNo = VNInfo;
AVLRegDef.DefReg = AVLReg;
State = AVLState::AVLIsReg;
}
void setAVLImm(unsigned Imm) {
AVLImm = Imm;
State = AVLState::AVLIsImm;
}
void setAVLVLMAX() { State = AVLState::AVLIsVLMAX; }
bool hasAVLImm() const { return State == AVLState::AVLIsImm; }
bool hasAVLReg() const { return State == AVLState::AVLIsReg; }
bool hasAVLVLMAX() const { return State == AVLState::AVLIsVLMAX; }
Register getAVLReg() const {
assert(hasAVLReg() && AVLRegDef.DefReg.isVirtual());
return AVLRegDef.DefReg;
}
unsigned getAVLImm() const {
assert(hasAVLImm());
return AVLImm;
}
const VNInfo *getAVLVNInfo() const {
assert(hasAVLReg());
return AVLRegDef.ValNo;
}
// Most AVLIsReg infos will have a single defining MachineInstr, unless it was
// a PHI node. In that case getAVLVNInfo()->def will point to the block
// boundary slot and this will return nullptr. If LiveIntervals isn't
// available, nullptr is also returned.
const MachineInstr *getAVLDefMI(const LiveIntervals *LIS) const {
assert(hasAVLReg());
if (!LIS || getAVLVNInfo()->isPHIDef())
return nullptr;
auto *MI = LIS->getInstructionFromIndex(getAVLVNInfo()->def);
assert(MI);
return MI;
}
void setAVL(const VSETVLIInfo &Info) {
assert(Info.isValid());
if (Info.isUnknown())
setUnknown();
else if (Info.hasAVLReg())
setAVLRegDef(Info.getAVLVNInfo(), Info.getAVLReg());
else if (Info.hasAVLVLMAX())
setAVLVLMAX();
else {
assert(Info.hasAVLImm());
setAVLImm(Info.getAVLImm());
}
}
bool hasSEWLMULRatioOnly() const {
assert(isKnown() && "Can't use VTYPE for uninitialized or unknown");
return SEWLMULRatioOnly;
}
unsigned getSEW() const {
assert(isKnown() && !hasSEWLMULRatioOnly() &&
"Can't use VTYPE for uninitialized or unknown");
return SEW;
}
RISCVVType::VLMUL getVLMUL() const {
assert(isKnown() && !hasSEWLMULRatioOnly() &&
"Can't use VTYPE for uninitialized or unknown");
return VLMul;
}
bool getTailAgnostic() const {
assert(isKnown() && "Can't use VTYPE for uninitialized or unknown");
return TailAgnostic;
}
bool getMaskAgnostic() const {
assert(isKnown() && "Can't use VTYPE for uninitialized or unknown");
return MaskAgnostic;
}
bool getAltFmt() const {
assert(isKnown() && "Can't use VTYPE for uninitialized or unknown");
return AltFmt;
}
unsigned getTWiden() const {
assert(isKnown() && "Can't use VTYPE for uninitialized or unknown");
return TWiden;
}
bool hasNonZeroAVL(const LiveIntervals *LIS) const {
if (hasAVLImm())
return getAVLImm() > 0;
if (hasAVLReg()) {
if (auto *DefMI = getAVLDefMI(LIS))
return RISCVInstrInfo::isNonZeroLoadImmediate(*DefMI);
}
if (hasAVLVLMAX())
return true;
return false;
}
bool hasEquallyZeroAVL(const VSETVLIInfo &Other,
const LiveIntervals *LIS) const {
if (hasSameAVL(Other))
return true;
return (hasNonZeroAVL(LIS) && Other.hasNonZeroAVL(LIS));
}
bool hasSameAVLLatticeValue(const VSETVLIInfo &Other) const {
if (hasAVLReg() && Other.hasAVLReg()) {
assert(!getAVLVNInfo() == !Other.getAVLVNInfo() &&
"we either have intervals or we don't");
if (!getAVLVNInfo())
return getAVLReg() == Other.getAVLReg();
return getAVLVNInfo()->id == Other.getAVLVNInfo()->id &&
getAVLReg() == Other.getAVLReg();
}
if (hasAVLImm() && Other.hasAVLImm())
return getAVLImm() == Other.getAVLImm();
if (hasAVLVLMAX())
return Other.hasAVLVLMAX() && hasSameVLMAX(Other);
return false;
}
// Return true if the two lattice values are guaranteed to have
// the same AVL value at runtime.
bool hasSameAVL(const VSETVLIInfo &Other) const {
// Without LiveIntervals, we don't know which instruction defines a
// register. Since a register may be redefined, this means all AVLIsReg
// states must be treated as possibly distinct.
if (hasAVLReg() && Other.hasAVLReg()) {
assert(!getAVLVNInfo() == !Other.getAVLVNInfo() &&
"we either have intervals or we don't");
if (!getAVLVNInfo())
return false;
}
return hasSameAVLLatticeValue(Other);
}
void setVTYPE(unsigned VType) {
assert(isKnown() && "Can't set VTYPE for uninitialized or unknown");
VLMul = RISCVVType::getVLMUL(VType);
SEW = RISCVVType::getSEW(VType);
TailAgnostic = RISCVVType::isTailAgnostic(VType);
MaskAgnostic = RISCVVType::isMaskAgnostic(VType);
AltFmt = RISCVVType::isAltFmt(VType);
TWiden =
RISCVVType::hasXSfmmWiden(VType) ? RISCVVType::getXSfmmWiden(VType) : 0;
}
void setVTYPE(RISCVVType::VLMUL L, unsigned S, bool TA, bool MA, bool Altfmt,
unsigned W) {
assert(isKnown() && "Can't set VTYPE for uninitialized or unknown");
VLMul = L;
SEW = S;
TailAgnostic = TA;
MaskAgnostic = MA;
AltFmt = Altfmt;
TWiden = W;
}
void setAltFmt(bool AF) { AltFmt = AF; }
void setVLMul(RISCVVType::VLMUL VLMul) { this->VLMul = VLMul; }
unsigned encodeVTYPE() const {
assert(isKnown() && !SEWLMULRatioOnly &&
"Can't encode VTYPE for uninitialized or unknown");
if (TWiden != 0)
return RISCVVType::encodeXSfmmVType(SEW, TWiden, AltFmt);
return RISCVVType::encodeVTYPE(VLMul, SEW, TailAgnostic, MaskAgnostic,
AltFmt);
}
bool hasSameVTYPE(const VSETVLIInfo &Other) const {
assert(isValid() && Other.isValid() &&
"Can't compare invalid VSETVLIInfos");
assert(!isUnknown() && !Other.isUnknown() &&
"Can't compare VTYPE in unknown state");
assert(!SEWLMULRatioOnly && !Other.SEWLMULRatioOnly &&
"Can't compare when only LMUL/SEW ratio is valid.");
return std::tie(VLMul, SEW, TailAgnostic, MaskAgnostic, AltFmt, TWiden) ==
std::tie(Other.VLMul, Other.SEW, Other.TailAgnostic,
Other.MaskAgnostic, Other.AltFmt, Other.TWiden);
}
unsigned getSEWLMULRatio() const {
assert(isKnown() && "Can't use VTYPE for uninitialized or unknown");
return RISCVVType::getSEWLMULRatio(SEW, VLMul);
}
// Check if the VTYPE for these two VSETVLIInfos produce the same VLMAX.
// Note that having the same VLMAX ensures that both share the same
// function from AVL to VL; that is, they must produce the same VL value
// for any given AVL value.
bool hasSameVLMAX(const VSETVLIInfo &Other) const {
assert(isValid() && Other.isValid() &&
"Can't compare invalid VSETVLIInfos");
assert(!isUnknown() && !Other.isUnknown() &&
"Can't compare VTYPE in unknown state");
return getSEWLMULRatio() == Other.getSEWLMULRatio();
}
bool hasCompatibleVTYPE(const DemandedFields &Used,
const VSETVLIInfo &Require) const;
// Determine whether the vector instructions requirements represented by
// Require are compatible with the previous vsetvli instruction represented
// by this. MI is the instruction whose requirements we're considering.
bool isCompatible(const DemandedFields &Used, const VSETVLIInfo &Require,
const LiveIntervals *LIS) const {
assert(isValid() && Require.isValid() &&
"Can't compare invalid VSETVLIInfos");
// Nothing is compatible with Unknown.
if (isUnknown() || Require.isUnknown())
return false;
// If only our VLMAX ratio is valid, then this isn't compatible.
if (SEWLMULRatioOnly || Require.SEWLMULRatioOnly)
return false;
if (Used.VLAny && !(hasSameAVL(Require) && hasSameVLMAX(Require)))
return false;
if (Used.VLZeroness && !hasEquallyZeroAVL(Require, LIS))
return false;
return hasCompatibleVTYPE(Used, Require);
}
bool operator==(const VSETVLIInfo &Other) const {
// Uninitialized is only equal to another Uninitialized.
if (!isValid())
return !Other.isValid();
if (!Other.isValid())
return !isValid();
// Unknown is only equal to another Unknown.
if (isUnknown())
return Other.isUnknown();
if (Other.isUnknown())
return isUnknown();
if (!hasSameAVLLatticeValue(Other))
return false;
// If the SEWLMULRatioOnly bits are different, then they aren't equal.
if (SEWLMULRatioOnly != Other.SEWLMULRatioOnly)
return false;
// If only the VLMAX is valid, check that it is the same.
if (SEWLMULRatioOnly)
return hasSameVLMAX(Other);
// If the full VTYPE is valid, check that it is the same.
return hasSameVTYPE(Other);
}
bool operator!=(const VSETVLIInfo &Other) const { return !(*this == Other); }
// Calculate the VSETVLIInfo visible to a block assuming this and Other are
// both predecessors.
VSETVLIInfo intersect(const VSETVLIInfo &Other) const {
// If the new value isn't valid, ignore it.
if (!Other.isValid())
return *this;
// If this value isn't valid, this must be the first predecessor, use it.
if (!isValid())
return Other;
// If either is unknown, the result is unknown.
if (isUnknown() || Other.isUnknown())
return VSETVLIInfo::getUnknown();
// If we have an exact, match return this.
if (*this == Other)
return *this;
// Not an exact match, but maybe the AVL and VLMAX are the same. If so,
// return an SEW/LMUL ratio only value.
if (hasSameAVL(Other) && hasSameVLMAX(Other)) {
VSETVLIInfo MergeInfo = *this;
MergeInfo.SEWLMULRatioOnly = true;
return MergeInfo;
}
// Otherwise the result is unknown.
return VSETVLIInfo::getUnknown();
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
/// Support for debugging, callable in GDB: V->dump()
LLVM_DUMP_METHOD void dump() const {
print(dbgs());
dbgs() << "\n";
}
/// Implement operator<<.
/// @{
void print(raw_ostream &OS) const {
OS << '{';
switch (State) {
case AVLState::Uninitialized:
OS << "Uninitialized";
break;
case AVLState::Unknown:
OS << "unknown";
break;
case AVLState::AVLIsReg:
OS << "AVLReg=" << llvm::printReg(getAVLReg());
break;
case AVLState::AVLIsImm:
OS << "AVLImm=" << (unsigned)AVLImm;
break;
case AVLState::AVLIsVLMAX:
OS << "AVLVLMAX";
break;
}
if (isKnown()) {
OS << ", ";
unsigned LMul;
bool Fractional;
std::tie(LMul, Fractional) = decodeVLMUL(VLMul);
OS << "VLMul=m";
if (Fractional)
OS << 'f';
OS << LMul << ", "
<< "SEW=e" << (unsigned)SEW << ", "
<< "TailAgnostic=" << (bool)TailAgnostic << ", "
<< "MaskAgnostic=" << (bool)MaskAgnostic << ", "
<< "SEWLMULRatioOnly=" << (bool)SEWLMULRatioOnly << ", "
<< "TWiden=" << (unsigned)TWiden << ", "
<< "AltFmt=" << (bool)AltFmt;
}
OS << '}';
}
#endif
};
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_ATTRIBUTE_USED
inline raw_ostream &operator<<(raw_ostream &OS, const VSETVLIInfo &V) {
V.print(OS);
return OS;
}
#endif
class RISCVVSETVLIInfoAnalysis {
const RISCVSubtarget *ST;
// Possibly null!
LiveIntervals *LIS;
public:
RISCVVSETVLIInfoAnalysis() = default;
RISCVVSETVLIInfoAnalysis(const RISCVSubtarget *ST, LiveIntervals *LIS)
: ST(ST), LIS(LIS) {}
VSETVLIInfo getInfoForVSETVLI(const MachineInstr &MI) const;
VSETVLIInfo computeInfoForInstr(const MachineInstr &MI) const;
private:
void forwardVSETVLIAVL(VSETVLIInfo &Info) const;
};
} // namespace RISCV
} // namespace llvm