[llvm] [RISCV] Separate the analysis part of RISCVInsertVSETVLI. (PR #149574)

Min-Yih Hsu via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 21 09:59:22 PDT 2025


================
@@ -0,0 +1,620 @@
+//===- RISCVVectorConfigAnalysis ------------------------------------------===//
+//
+// 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 is the RISCV analysis of vector unit config.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_RISCV_RISCVVCONFIGANALYSIS_H
+#define LLVM_LIB_TARGET_RISCV_RISCVVCONFIGANALYSIS_H
+
+#include "RISCV.h"
+#include "RISCVSubtarget.h"
+#include "llvm/CodeGen/LiveIntervals.h"
+#include "llvm/CodeGen/LiveStacks.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachinePassManager.h"
+#include <queue>
+#include <vector>
+using namespace llvm;
+
+namespace llvm {
+/// 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;
+
+  // Return true if any part of VTYPE was used
+  bool usedVTYPE() const {
+    return SEW || LMUL || SEWLMULRatio || TailPolicy || MaskPolicy || VILL;
+  }
+
+  // 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;
+  }
+
+  // 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;
+  }
+
+#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 << "}";
+  }
+#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
+
+/// 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 : uint8_t {
+    Uninitialized,
+    AVLIsReg,
+    AVLIsImm,
+    AVLIsVLMAX,
+    Unknown, // AVL and VTYPE are fully unknown
+  } State = 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;
+
+public:
+  VSETVLIInfo()
+      : AVLImm(0), TailAgnostic(false), MaskAgnostic(false),
+        SEWLMULRatioOnly(false) {}
+
+  static VSETVLIInfo getUnknown() {
+    VSETVLIInfo Info;
+    Info.setUnknown();
+    return Info;
+  }
+
+  bool isValid() const { return State != Uninitialized; }
+  void setUnknown() { State = Unknown; }
+  bool isUnknown() const { return State == Unknown; }
+
+  void setAVLRegDef(const VNInfo *VNInfo, Register AVLReg) {
+    assert(AVLReg.isVirtual());
+    AVLRegDef.ValNo = VNInfo;
+    AVLRegDef.DefReg = AVLReg;
+    State = AVLIsReg;
+  }
+
+  void setAVLImm(unsigned Imm) {
+    AVLImm = Imm;
+    State = AVLIsImm;
+  }
+
+  void setAVLVLMAX() { State = AVLIsVLMAX; }
+
+  bool hasAVLImm() const { return State == AVLIsImm; }
+  bool hasAVLReg() const { return State == AVLIsReg; }
+  bool hasAVLVLMAX() const { return State == 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());
+    }
+  }
+
+  unsigned getSEW() const { return SEW; }
+  RISCVVType::VLMUL getVLMUL() const { return VLMul; }
+  bool getTailAgnostic() const { return TailAgnostic; }
+  bool getMaskAgnostic() const { return MaskAgnostic; }
+
+  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(isValid() && !isUnknown() &&
+           "Can't set VTYPE for uninitialized or unknown");
+    VLMul = RISCVVType::getVLMUL(VType);
+    SEW = RISCVVType::getSEW(VType);
+    TailAgnostic = RISCVVType::isTailAgnostic(VType);
+    MaskAgnostic = RISCVVType::isMaskAgnostic(VType);
+  }
+  void setVTYPE(RISCVVType::VLMUL L, unsigned S, bool TA, bool MA) {
+    assert(isValid() && !isUnknown() &&
+           "Can't set VTYPE for uninitialized or unknown");
+    VLMul = L;
+    SEW = S;
+    TailAgnostic = TA;
+    MaskAgnostic = MA;
+  }
+
+  void setVLMul(RISCVVType::VLMUL VLMul) { this->VLMul = VLMul; }
+
+  unsigned encodeVTYPE() const {
+    assert(isValid() && !isUnknown() && !SEWLMULRatioOnly &&
+           "Can't encode VTYPE for uninitialized or unknown");
+    return RISCVVType::encodeVTYPE(VLMul, SEW, TailAgnostic, MaskAgnostic);
+  }
+
+  bool hasSEWLMULRatioOnly() const { return SEWLMULRatioOnly; }
+
+  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) ==
+           std::tie(Other.VLMul, Other.SEW, Other.TailAgnostic,
+                    Other.MaskAgnostic);
+  }
+
+  unsigned getSEWLMULRatio() const {
+    assert(isValid() && !isUnknown() &&
+           "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 {
----------------
mshockwave wrote:

ditto move to .cpp file

https://github.com/llvm/llvm-project/pull/149574


More information about the llvm-commits mailing list