//===- 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"); if (getTWiden() != Other.getTWiden()) return false; if (getTWiden()) return getSEW() == Other.getSEW(); 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