#ifndef LLVM_TRANSFORMS_VECTORIZE_VPLAN_H
#define LLVM_TRANSFORMS_VECTORIZE_VPLAN_H
#include "VPlanValue.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/GraphTraits.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Twine.h"
#include "llvm/ADT/ilist.h"
#include "llvm/ADT/ilist_node.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/VectorUtils.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/FMF.h"
#include "llvm/Transforms/Utils/LoopVersioning.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <string>
namespace llvm {
class BasicBlock;
class DominatorTree;
class InductionDescriptor;
class InnerLoopVectorizer;
class IRBuilderBase;
class LoopInfo;
class raw_ostream;
class RecurrenceDescriptor;
class Value;
class VPBasicBlock;
class VPRegionBlock;
class VPlan;
class VPReplicateRecipe;
class VPlanSlp;
Value *getRuntimeVF(IRBuilderBase &B, Type *Ty, ElementCount VF);
Value *createStepForVF(IRBuilderBase &B, Type *Ty, ElementCount VF,
                       int64_t Step);
struct VFRange {
    const ElementCount Start;
    ElementCount End;
  bool isEmpty() const {
    return End.getKnownMinValue() <= Start.getKnownMinValue();
  }
  VFRange(const ElementCount &Start, const ElementCount &End)
      : Start(Start), End(End) {
    assert(Start.isScalable() == End.isScalable() &&
           "Both Start and End should have the same scalable flag");
    assert(isPowerOf2_32(Start.getKnownMinValue()) &&
           "Expected Start to be a power of 2");
  }
};
using VPlanPtr = std::unique_ptr<VPlan>;
class VPLane {
public:
    enum class Kind : uint8_t {
            First,
                    ScalableLast
  };
private:
    unsigned Lane;
    Kind LaneKind;
public:
  VPLane(unsigned Lane, Kind LaneKind) : Lane(Lane), LaneKind(LaneKind) {}
  static VPLane getFirstLane() { return VPLane(0, VPLane::Kind::First); }
  static VPLane getLastLaneForVF(const ElementCount &VF) {
    unsigned LaneOffset = VF.getKnownMinValue() - 1;
    Kind LaneKind;
    if (VF.isScalable())
                  LaneKind = VPLane::Kind::ScalableLast;
    else
      LaneKind = VPLane::Kind::First;
    return VPLane(LaneOffset, LaneKind);
  }
      unsigned getKnownLane() const {
    assert(LaneKind == Kind::First);
    return Lane;
  }
      Value *getAsRuntimeExpr(IRBuilderBase &Builder, const ElementCount &VF) const;
    Kind getKind() const { return LaneKind; }
    bool isFirstLane() const { return Lane == 0 && LaneKind == Kind::First; }
    unsigned mapToCacheIndex(const ElementCount &VF) const {
    switch (LaneKind) {
    case VPLane::Kind::ScalableLast:
      assert(VF.isScalable() && Lane < VF.getKnownMinValue());
      return VF.getKnownMinValue() + Lane;
    default:
      assert(Lane < VF.getKnownMinValue());
      return Lane;
    }
  }
      static unsigned getNumCachedLanes(const ElementCount &VF) {
    return VF.getKnownMinValue() * (VF.isScalable() ? 2 : 1);
  }
};
struct VPIteration {
    unsigned Part;
  VPLane Lane;
  VPIteration(unsigned Part, unsigned Lane,
              VPLane::Kind Kind = VPLane::Kind::First)
      : Part(Part), Lane(Lane, Kind) {}
  VPIteration(unsigned Part, const VPLane &Lane) : Part(Part), Lane(Lane) {}
  bool isFirstIteration() const { return Part == 0 && Lane.isFirstLane(); }
};
struct VPTransformState {
  VPTransformState(ElementCount VF, unsigned UF, LoopInfo *LI,
                   DominatorTree *DT, IRBuilderBase &Builder,
                   InnerLoopVectorizer *ILV, VPlan *Plan)
      : VF(VF), UF(UF), LI(LI), DT(DT), Builder(Builder), ILV(ILV), Plan(Plan),
        LVer(nullptr) {}
    ElementCount VF;
  unsigned UF;
        Optional<VPIteration> Instance;
  struct DataState {
                typedef SmallVector<Value *, 2> PerPartValuesTy;
    DenseMap<VPValue *, PerPartValuesTy> PerPartOutput;
    using ScalarsPerPartValuesTy = SmallVector<SmallVector<Value *, 4>, 2>;
    DenseMap<VPValue *, ScalarsPerPartValuesTy> PerPartScalars;
  } Data;
            Value *get(VPValue *Def, unsigned Part);
    Value *get(VPValue *Def, const VPIteration &Instance);
  bool hasVectorValue(VPValue *Def, unsigned Part) {
    auto I = Data.PerPartOutput.find(Def);
    return I != Data.PerPartOutput.end() && Part < I->second.size() &&
           I->second[Part];
  }
  bool hasAnyVectorValue(VPValue *Def) const {
    return Data.PerPartOutput.find(Def) != Data.PerPartOutput.end();
  }
  bool hasScalarValue(VPValue *Def, VPIteration Instance) {
    auto I = Data.PerPartScalars.find(Def);
    if (I == Data.PerPartScalars.end())
      return false;
    unsigned CacheIdx = Instance.Lane.mapToCacheIndex(VF);
    return Instance.Part < I->second.size() &&
           CacheIdx < I->second[Instance.Part].size() &&
           I->second[Instance.Part][CacheIdx];
  }
    void set(VPValue *Def, Value *V, unsigned Part) {
    if (!Data.PerPartOutput.count(Def)) {
      DataState::PerPartValuesTy Entry(UF);
      Data.PerPartOutput[Def] = Entry;
    }
    Data.PerPartOutput[Def][Part] = V;
  }
    void reset(VPValue *Def, Value *V, unsigned Part) {
    auto Iter = Data.PerPartOutput.find(Def);
    assert(Iter != Data.PerPartOutput.end() &&
           "need to overwrite existing value");
    Iter->second[Part] = V;
  }
    void set(VPValue *Def, Value *V, const VPIteration &Instance) {
    auto Iter = Data.PerPartScalars.insert({Def, {}});
    auto &PerPartVec = Iter.first->second;
    while (PerPartVec.size() <= Instance.Part)
      PerPartVec.emplace_back();
    auto &Scalars = PerPartVec[Instance.Part];
    unsigned CacheIdx = Instance.Lane.mapToCacheIndex(VF);
    while (Scalars.size() <= CacheIdx)
      Scalars.push_back(nullptr);
    assert(!Scalars[CacheIdx] && "should overwrite existing value");
    Scalars[CacheIdx] = V;
  }
    void reset(VPValue *Def, Value *V, const VPIteration &Instance) {
    auto Iter = Data.PerPartScalars.find(Def);
    assert(Iter != Data.PerPartScalars.end() &&
           "need to overwrite existing value");
    assert(Instance.Part < Iter->second.size() &&
           "need to overwrite existing value");
    unsigned CacheIdx = Instance.Lane.mapToCacheIndex(VF);
    assert(CacheIdx < Iter->second[Instance.Part].size() &&
           "need to overwrite existing value");
    Iter->second[Instance.Part][CacheIdx] = V;
  }
            void addNewMetadata(Instruction *To, const Instruction *Orig);
            void addMetadata(Instruction *To, Instruction *From);
      void addMetadata(ArrayRef<Value *> To, Instruction *From);
    void setDebugLocFromInst(const Value *V);
      struct CFGState {
        VPBasicBlock *PrevVPBB = nullptr;
            BasicBlock *PrevBB = nullptr;
            BasicBlock *ExitBB = nullptr;
            SmallDenseMap<VPBasicBlock *, BasicBlock *> VPBB2IRBB;
    CFGState() = default;
            BasicBlock *getPreheaderBBFor(VPRecipeBase *R);
  } CFG;
    LoopInfo *LI;
    DominatorTree *DT;
    IRBuilderBase &Builder;
  VPValue2ValueTy VPValue2Value;
    Value *CanonicalIV = nullptr;
    InnerLoopVectorizer *ILV;
    VPlan *Plan;
      SmallPtrSet<VPRecipeBase *, 16> MayGeneratePoisonRecipes;
    Loop *CurrentVectorLoop = nullptr;
            std::unique_ptr<LoopVersioning> LVer;
};
class VPBlockBase {
  friend class VPBlockUtils;
  const unsigned char SubclassID; 
    std::string Name;
      VPRegionBlock *Parent = nullptr;
    SmallVector<VPBlockBase *, 1> Predecessors;
    SmallVector<VPBlockBase *, 1> Successors;
      VPlan *Plan = nullptr;
    void appendSuccessor(VPBlockBase *Successor) {
    assert(Successor && "Cannot add nullptr successor!");
    Successors.push_back(Successor);
  }
    void appendPredecessor(VPBlockBase *Predecessor) {
    assert(Predecessor && "Cannot add nullptr predecessor!");
    Predecessors.push_back(Predecessor);
  }
    void removePredecessor(VPBlockBase *Predecessor) {
    auto Pos = find(Predecessors, Predecessor);
    assert(Pos && "Predecessor does not exist");
    Predecessors.erase(Pos);
  }
    void removeSuccessor(VPBlockBase *Successor) {
    auto Pos = find(Successors, Successor);
    assert(Pos && "Successor does not exist");
    Successors.erase(Pos);
  }
protected:
  VPBlockBase(const unsigned char SC, const std::string &N)
      : SubclassID(SC), Name(N) {}
public:
          using VPBlockTy = enum { VPBasicBlockSC, VPRegionBlockSC };
  using VPBlocksTy = SmallVectorImpl<VPBlockBase *>;
  virtual ~VPBlockBase() = default;
  const std::string &getName() const { return Name; }
  void setName(const Twine &newName) { Name = newName.str(); }
        unsigned getVPBlockID() const { return SubclassID; }
  VPRegionBlock *getParent() { return Parent; }
  const VPRegionBlock *getParent() const { return Parent; }
    VPlan *getPlan();
  const VPlan *getPlan() const;
      void setPlan(VPlan *ParentPlan);
  void setParent(VPRegionBlock *P) { Parent = P; }
        const VPBasicBlock *getEntryBasicBlock() const;
  VPBasicBlock *getEntryBasicBlock();
        const VPBasicBlock *getExitingBasicBlock() const;
  VPBasicBlock *getExitingBasicBlock();
  const VPBlocksTy &getSuccessors() const { return Successors; }
  VPBlocksTy &getSuccessors() { return Successors; }
  iterator_range<VPBlockBase **> successors() { return Successors; }
  const VPBlocksTy &getPredecessors() const { return Predecessors; }
  VPBlocksTy &getPredecessors() { return Predecessors; }
      VPBlockBase *getSingleSuccessor() const {
    return (Successors.size() == 1 ? *Successors.begin() : nullptr);
  }
      VPBlockBase *getSinglePredecessor() const {
    return (Predecessors.size() == 1 ? *Predecessors.begin() : nullptr);
  }
  size_t getNumSuccessors() const { return Successors.size(); }
  size_t getNumPredecessors() const { return Predecessors.size(); }
          VPBlockBase *getEnclosingBlockWithSuccessors();
        VPBlockBase *getEnclosingBlockWithPredecessors();
              const VPBlocksTy &getHierarchicalSuccessors() {
    return getEnclosingBlockWithSuccessors()->getSuccessors();
  }
      VPBlockBase *getSingleHierarchicalSuccessor() {
    return getEnclosingBlockWithSuccessors()->getSingleSuccessor();
  }
              const VPBlocksTy &getHierarchicalPredecessors() {
    return getEnclosingBlockWithPredecessors()->getPredecessors();
  }
      VPBlockBase *getSingleHierarchicalPredecessor() {
    return getEnclosingBlockWithPredecessors()->getSinglePredecessor();
  }
        void setOneSuccessor(VPBlockBase *Successor) {
    assert(Successors.empty() && "Setting one successor when others exist.");
    appendSuccessor(Successor);
  }
          void setTwoSuccessors(VPBlockBase *IfTrue, VPBlockBase *IfFalse) {
    assert(Successors.empty() && "Setting two successors when others exist.");
    appendSuccessor(IfTrue);
    appendSuccessor(IfFalse);
  }
        void setPredecessors(ArrayRef<VPBlockBase *> NewPreds) {
    assert(Predecessors.empty() && "Block predecessors already set.");
    for (auto *Pred : NewPreds)
      appendPredecessor(Pred);
  }
    void clearPredecessors() { Predecessors.clear(); }
    void clearSuccessors() { Successors.clear(); }
      virtual void execute(struct VPTransformState *State) = 0;
    static void deleteCFG(VPBlockBase *Entry);
    bool isLegalToHoistInto() {
            return true;
  }
      virtual void dropAllReferences(VPValue *NewValue) = 0;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
  void printAsOperand(raw_ostream &OS, bool PrintType) const {
    OS << getName();
  }
              virtual void print(raw_ostream &O, const Twine &Indent,
                     VPSlotTracker &SlotTracker) const = 0;
    void print(raw_ostream &O) const {
    VPSlotTracker SlotTracker(getPlan());
    print(O, "", SlotTracker);
  }
      void printSuccessors(raw_ostream &O, const Twine &Indent) const;
    LLVM_DUMP_METHOD void dump() const { print(dbgs()); }
#endif
};
class VPLiveOut : public VPUser {
  PHINode *Phi;
public:
  VPLiveOut(PHINode *Phi, VPValue *Op)
      : VPUser({Op}, VPUser::VPUserID::LiveOut), Phi(Phi) {}
            void fixPhi(VPlan &Plan, VPTransformState &State);
    bool usesScalars(const VPValue *Op) const override {
    assert(is_contained(operands(), Op) &&
           "Op must be an operand of the recipe");
    return true;
  }
  PHINode *getPhi() const { return Phi; }
};
class VPRecipeBase : public ilist_node_with_parent<VPRecipeBase, VPBasicBlock>,
                     public VPDef,
                     public VPUser {
  friend VPBasicBlock;
  friend class VPBlockUtils;
    VPBasicBlock *Parent = nullptr;
public:
  VPRecipeBase(const unsigned char SC, ArrayRef<VPValue *> Operands)
      : VPDef(SC), VPUser(Operands, VPUser::VPUserID::Recipe) {}
  template <typename IterT>
  VPRecipeBase(const unsigned char SC, iterator_range<IterT> Operands)
      : VPDef(SC), VPUser(Operands, VPUser::VPUserID::Recipe) {}
  virtual ~VPRecipeBase() = default;
    VPBasicBlock *getParent() { return Parent; }
  const VPBasicBlock *getParent() const { return Parent; }
      virtual void execute(struct VPTransformState &State) = 0;
      void insertBefore(VPRecipeBase *InsertPos);
      void insertBefore(VPBasicBlock &BB, iplist<VPRecipeBase>::iterator IP);
      void insertAfter(VPRecipeBase *InsertPos);
      void moveAfter(VPRecipeBase *MovePos);
        void moveBefore(VPBasicBlock &BB, iplist<VPRecipeBase>::iterator I);
      void removeFromParent();
        iplist<VPRecipeBase>::iterator eraseFromParent();
      Instruction *getUnderlyingInstr() {
    return cast<Instruction>(getVPSingleValue()->getUnderlyingValue());
  }
  const Instruction *getUnderlyingInstr() const {
    return cast<Instruction>(getVPSingleValue()->getUnderlyingValue());
  }
    static inline bool classof(const VPDef *D) {
        return true;
  }
  static inline bool classof(const VPUser *U) {
    return U->getVPUserID() == VPUser::VPUserID::Recipe;
  }
    bool mayHaveSideEffects() const;
    bool isPhi() const {
    return getVPDefID() >= VPFirstPHISC && getVPDefID() <= VPLastPHISC;
  }
    bool mayReadFromMemory() const;
    bool mayWriteToMemory() const;
    bool mayReadOrWriteMemory() const {
    return mayReadFromMemory() || mayWriteToMemory();
  }
};
inline bool VPUser::classof(const VPDef *Def) {
  return Def->getVPDefID() == VPRecipeBase::VPInstructionSC ||
         Def->getVPDefID() == VPRecipeBase::VPWidenSC ||
         Def->getVPDefID() == VPRecipeBase::VPWidenCallSC ||
         Def->getVPDefID() == VPRecipeBase::VPWidenSelectSC ||
         Def->getVPDefID() == VPRecipeBase::VPWidenGEPSC ||
         Def->getVPDefID() == VPRecipeBase::VPBlendSC ||
         Def->getVPDefID() == VPRecipeBase::VPInterleaveSC ||
         Def->getVPDefID() == VPRecipeBase::VPReplicateSC ||
         Def->getVPDefID() == VPRecipeBase::VPReductionSC ||
         Def->getVPDefID() == VPRecipeBase::VPBranchOnMaskSC ||
         Def->getVPDefID() == VPRecipeBase::VPWidenMemoryInstructionSC;
}
class VPInstruction : public VPRecipeBase, public VPValue {
  friend class VPlanSlp;
public:
    enum {
    FirstOrderRecurrenceSplice =
        Instruction::OtherOpsEnd + 1,                                           Not,
    ICmpULE,
    SLPLoad,
    SLPStore,
    ActiveLaneMask,
    CanonicalIVIncrement,
    CanonicalIVIncrementNUW,
            CanonicalIVIncrementForPart,
    CanonicalIVIncrementForPartNUW,
    BranchOnCount,
    BranchOnCond
  };
private:
  typedef unsigned char OpcodeTy;
  OpcodeTy Opcode;
  FastMathFlags FMF;
  DebugLoc DL;
    const std::string Name;
      void generateInstruction(VPTransformState &State, unsigned Part);
protected:
  void setUnderlyingInstr(Instruction *I) { setUnderlyingValue(I); }
public:
  VPInstruction(unsigned Opcode, ArrayRef<VPValue *> Operands, DebugLoc DL,
                const Twine &Name = "")
      : VPRecipeBase(VPRecipeBase::VPInstructionSC, Operands),
        VPValue(VPValue::VPVInstructionSC, nullptr, this), Opcode(Opcode),
        DL(DL), Name(Name.str()) {}
  VPInstruction(unsigned Opcode, std::initializer_list<VPValue *> Operands,
                DebugLoc DL = {}, const Twine &Name = "")
      : VPInstruction(Opcode, ArrayRef<VPValue *>(Operands), DL, Name) {}
    static inline bool classof(const VPValue *V) {
    return V->getVPValueID() == VPValue::VPVInstructionSC;
  }
  VPInstruction *clone() const {
    SmallVector<VPValue *, 2> Operands(operands());
    return new VPInstruction(Opcode, Operands, DL, Name);
  }
    static inline bool classof(const VPDef *R) {
    return R->getVPDefID() == VPRecipeBase::VPInstructionSC;
  }
      static inline bool classof(const VPUser *U) {
    auto *R = dyn_cast<VPRecipeBase>(U);
    return R && R->getVPDefID() == VPRecipeBase::VPInstructionSC;
  }
  static inline bool classof(const VPRecipeBase *R) {
    return R->getVPDefID() == VPRecipeBase::VPInstructionSC;
  }
  unsigned getOpcode() const { return Opcode; }
        void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
    LLVM_DUMP_METHOD void dump() const;
#endif
    bool mayWriteToMemory() const {
            return Opcode == Instruction::Store || Opcode == Instruction::Call ||
           Opcode == Instruction::Invoke || Opcode == SLPStore;
  }
  bool hasResult() const {
            switch (getOpcode()) {
    case Instruction::Ret:
    case Instruction::Br:
    case Instruction::Store:
    case Instruction::Switch:
    case Instruction::IndirectBr:
    case Instruction::Resume:
    case Instruction::CatchRet:
    case Instruction::Unreachable:
    case Instruction::Fence:
    case Instruction::AtomicRMW:
    case VPInstruction::BranchOnCond:
    case VPInstruction::BranchOnCount:
      return false;
    default:
      return true;
    }
  }
    void setFastMathFlags(FastMathFlags FMFNew);
    bool onlyFirstLaneUsed(const VPValue *Op) const override {
    assert(is_contained(operands(), Op) &&
           "Op must be an operand of the recipe");
    if (getOperand(0) != Op)
      return false;
    switch (getOpcode()) {
    default:
      return false;
    case VPInstruction::ActiveLaneMask:
    case VPInstruction::CanonicalIVIncrement:
    case VPInstruction::CanonicalIVIncrementNUW:
    case VPInstruction::CanonicalIVIncrementForPart:
    case VPInstruction::CanonicalIVIncrementForPartNUW:
    case VPInstruction::BranchOnCount:
      return true;
    };
    llvm_unreachable("switch should return");
  }
};
class VPWidenRecipe : public VPRecipeBase, public VPValue {
public:
  template <typename IterT>
  VPWidenRecipe(Instruction &I, iterator_range<IterT> Operands)
      : VPRecipeBase(VPRecipeBase::VPWidenSC, Operands),
        VPValue(VPValue::VPVWidenSC, &I, this) {}
  ~VPWidenRecipe() override = default;
    static inline bool classof(const VPDef *D) {
    return D->getVPDefID() == VPRecipeBase::VPWidenSC;
  }
  static inline bool classof(const VPValue *V) {
    return V->getVPValueID() == VPValue::VPVWidenSC;
  }
    void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
};
class VPWidenCallRecipe : public VPRecipeBase, public VPValue {
public:
  template <typename IterT>
  VPWidenCallRecipe(CallInst &I, iterator_range<IterT> CallArguments)
      : VPRecipeBase(VPRecipeBase::VPWidenCallSC, CallArguments),
        VPValue(VPValue::VPVWidenCallSC, &I, this) {}
  ~VPWidenCallRecipe() override = default;
    static inline bool classof(const VPDef *D) {
    return D->getVPDefID() == VPRecipeBase::VPWidenCallSC;
  }
    void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
};
class VPWidenSelectRecipe : public VPRecipeBase, public VPValue {
    bool InvariantCond;
public:
  template <typename IterT>
  VPWidenSelectRecipe(SelectInst &I, iterator_range<IterT> Operands,
                      bool InvariantCond)
      : VPRecipeBase(VPRecipeBase::VPWidenSelectSC, Operands),
        VPValue(VPValue::VPVWidenSelectSC, &I, this),
        InvariantCond(InvariantCond) {}
  ~VPWidenSelectRecipe() override = default;
    static inline bool classof(const VPDef *D) {
    return D->getVPDefID() == VPRecipeBase::VPWidenSelectSC;
  }
    void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
};
class VPWidenGEPRecipe : public VPRecipeBase, public VPValue {
  bool IsPtrLoopInvariant;
  SmallBitVector IsIndexLoopInvariant;
public:
  template <typename IterT>
  VPWidenGEPRecipe(GetElementPtrInst *GEP, iterator_range<IterT> Operands)
      : VPRecipeBase(VPRecipeBase::VPWidenGEPSC, Operands),
        VPValue(VPWidenGEPSC, GEP, this),
        IsIndexLoopInvariant(GEP->getNumIndices(), false) {}
  template <typename IterT>
  VPWidenGEPRecipe(GetElementPtrInst *GEP, iterator_range<IterT> Operands,
                   Loop *OrigLoop)
      : VPRecipeBase(VPRecipeBase::VPWidenGEPSC, Operands),
        VPValue(VPValue::VPVWidenGEPSC, GEP, this),
        IsIndexLoopInvariant(GEP->getNumIndices(), false) {
    IsPtrLoopInvariant = OrigLoop->isLoopInvariant(GEP->getPointerOperand());
    for (auto Index : enumerate(GEP->indices()))
      IsIndexLoopInvariant[Index.index()] =
          OrigLoop->isLoopInvariant(Index.value().get());
  }
  ~VPWidenGEPRecipe() override = default;
    static inline bool classof(const VPDef *D) {
    return D->getVPDefID() == VPRecipeBase::VPWidenGEPSC;
  }
    void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
};
class VPWidenIntOrFpInductionRecipe : public VPRecipeBase, public VPValue {
  PHINode *IV;
  const InductionDescriptor &IndDesc;
  bool NeedsVectorIV;
public:
  VPWidenIntOrFpInductionRecipe(PHINode *IV, VPValue *Start, VPValue *Step,
                                const InductionDescriptor &IndDesc,
                                bool NeedsVectorIV)
      : VPRecipeBase(VPWidenIntOrFpInductionSC, {Start, Step}),
        VPValue(IV, this), IV(IV), IndDesc(IndDesc),
        NeedsVectorIV(NeedsVectorIV) {}
  VPWidenIntOrFpInductionRecipe(PHINode *IV, VPValue *Start, VPValue *Step,
                                const InductionDescriptor &IndDesc,
                                TruncInst *Trunc, bool NeedsVectorIV)
      : VPRecipeBase(VPWidenIntOrFpInductionSC, {Start, Step}),
        VPValue(Trunc, this), IV(IV), IndDesc(IndDesc),
        NeedsVectorIV(NeedsVectorIV) {}
  ~VPWidenIntOrFpInductionRecipe() override = default;
    static inline bool classof(const VPDef *D) {
    return D->getVPDefID() == VPRecipeBase::VPWidenIntOrFpInductionSC;
  }
      void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
    VPValue *getStartValue() { return getOperand(0); }
  const VPValue *getStartValue() const { return getOperand(0); }
    VPValue *getStepValue() { return getOperand(1); }
  const VPValue *getStepValue() const { return getOperand(1); }
      TruncInst *getTruncInst() {
    return dyn_cast_or_null<TruncInst>(getVPValue(0)->getUnderlyingValue());
  }
  const TruncInst *getTruncInst() const {
    return dyn_cast_or_null<TruncInst>(getVPValue(0)->getUnderlyingValue());
  }
  PHINode *getPHINode() { return IV; }
    const InductionDescriptor &getInductionDescriptor() const { return IndDesc; }
      bool isCanonical() const;
    const Type *getScalarType() const {
    const TruncInst *TruncI = getTruncInst();
    return TruncI ? TruncI->getType() : IV->getType();
  }
    bool needsVectorIV() const { return NeedsVectorIV; }
};
class VPHeaderPHIRecipe : public VPRecipeBase, public VPValue {
protected:
  VPHeaderPHIRecipe(unsigned char VPVID, unsigned char VPDefID, PHINode *Phi,
                    VPValue *Start = nullptr)
      : VPRecipeBase(VPDefID, {}), VPValue(VPVID, Phi, this) {
    if (Start)
      addOperand(Start);
  }
public:
  ~VPHeaderPHIRecipe() override = default;
    static inline bool classof(const VPRecipeBase *B) {
    return B->getVPDefID() == VPRecipeBase::VPCanonicalIVPHISC ||
           B->getVPDefID() == VPRecipeBase::VPActiveLaneMaskPHISC ||
           B->getVPDefID() == VPRecipeBase::VPFirstOrderRecurrencePHISC ||
           B->getVPDefID() == VPRecipeBase::VPReductionPHISC ||
           B->getVPDefID() == VPRecipeBase::VPWidenIntOrFpInductionSC ||
           B->getVPDefID() == VPRecipeBase::VPWidenPHISC;
  }
  static inline bool classof(const VPValue *V) {
    return V->getVPValueID() == VPValue::VPVCanonicalIVPHISC ||
           V->getVPValueID() == VPValue::VPVActiveLaneMaskPHISC ||
           V->getVPValueID() == VPValue::VPVFirstOrderRecurrencePHISC ||
           V->getVPValueID() == VPValue::VPVReductionPHISC ||
           V->getVPValueID() == VPValue::VPVWidenIntOrFpInductionSC ||
           V->getVPValueID() == VPValue::VPVWidenPHISC;
  }
    void execute(VPTransformState &State) override = 0;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override = 0;
#endif
    VPValue *getStartValue() {
    return getNumOperands() == 0 ? nullptr : getOperand(0);
  }
  VPValue *getStartValue() const {
    return getNumOperands() == 0 ? nullptr : getOperand(0);
  }
    VPValue *getBackedgeValue() {
    return getOperand(1);
  }
      VPRecipeBase *getBackedgeRecipe() {
    return cast<VPRecipeBase>(getBackedgeValue()->getDef());
  }
};
class VPWidenPointerInductionRecipe : public VPHeaderPHIRecipe {
  const InductionDescriptor &IndDesc;
        ScalarEvolution &SE;
  bool IsScalarAfterVectorization;
public:
      VPWidenPointerInductionRecipe(PHINode *Phi, VPValue *Start,
                                const InductionDescriptor &IndDesc,
                                ScalarEvolution &SE,
                                bool IsScalarAfterVectorization)
      : VPHeaderPHIRecipe(VPVWidenPointerInductionSC, VPWidenPointerInductionSC,
                          Phi),
        IndDesc(IndDesc), SE(SE),
        IsScalarAfterVectorization(IsScalarAfterVectorization) {
    addOperand(Start);
  }
  ~VPWidenPointerInductionRecipe() override = default;
    static inline bool classof(const VPRecipeBase *B) {
    return B->getVPDefID() == VPRecipeBase::VPWidenPointerInductionSC;
  }
  static inline bool classof(const VPHeaderPHIRecipe *R) {
    return R->getVPDefID() == VPRecipeBase::VPWidenPointerInductionSC;
  }
  static inline bool classof(const VPValue *V) {
    return V->getVPValueID() == VPValue::VPVWidenPointerInductionSC;
  }
    void execute(VPTransformState &State) override;
    bool onlyScalarsGenerated(ElementCount VF);
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
};
class VPWidenPHIRecipe : public VPHeaderPHIRecipe {
    SmallVector<VPBasicBlock *, 2> IncomingBlocks;
public:
    VPWidenPHIRecipe(PHINode *Phi, VPValue *Start = nullptr)
      : VPHeaderPHIRecipe(VPVWidenPHISC, VPWidenPHISC, Phi) {
    if (Start)
      addOperand(Start);
  }
  ~VPWidenPHIRecipe() override = default;
    static inline bool classof(const VPRecipeBase *B) {
    return B->getVPDefID() == VPRecipeBase::VPWidenPHISC;
  }
  static inline bool classof(const VPHeaderPHIRecipe *R) {
    return R->getVPDefID() == VPRecipeBase::VPWidenPHISC;
  }
  static inline bool classof(const VPValue *V) {
    return V->getVPValueID() == VPValue::VPVWidenPHISC;
  }
    void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
    void addIncoming(VPValue *IncomingV, VPBasicBlock *IncomingBlock) {
    addOperand(IncomingV);
    IncomingBlocks.push_back(IncomingBlock);
  }
    VPBasicBlock *getIncomingBlock(unsigned I) { return IncomingBlocks[I]; }
    VPValue *getIncomingValue(unsigned I) { return getOperand(I); }
};
struct VPFirstOrderRecurrencePHIRecipe : public VPHeaderPHIRecipe {
  VPFirstOrderRecurrencePHIRecipe(PHINode *Phi, VPValue &Start)
      : VPHeaderPHIRecipe(VPVFirstOrderRecurrencePHISC,
                          VPFirstOrderRecurrencePHISC, Phi, &Start) {}
    static inline bool classof(const VPRecipeBase *R) {
    return R->getVPDefID() == VPRecipeBase::VPFirstOrderRecurrencePHISC;
  }
  static inline bool classof(const VPHeaderPHIRecipe *R) {
    return R->getVPDefID() == VPRecipeBase::VPFirstOrderRecurrencePHISC;
  }
  static inline bool classof(const VPValue *V) {
    return V->getVPValueID() == VPValue::VPVFirstOrderRecurrencePHISC;
  }
  void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
};
class VPReductionPHIRecipe : public VPHeaderPHIRecipe {
    const RecurrenceDescriptor &RdxDesc;
    bool IsInLoop;
    bool IsOrdered;
public:
      VPReductionPHIRecipe(PHINode *Phi, const RecurrenceDescriptor &RdxDesc,
                       VPValue &Start, bool IsInLoop = false,
                       bool IsOrdered = false)
      : VPHeaderPHIRecipe(VPVReductionPHISC, VPReductionPHISC, Phi, &Start),
        RdxDesc(RdxDesc), IsInLoop(IsInLoop), IsOrdered(IsOrdered) {
    assert((!IsOrdered || IsInLoop) && "IsOrdered requires IsInLoop");
  }
  ~VPReductionPHIRecipe() override = default;
    static inline bool classof(const VPRecipeBase *R) {
    return R->getVPDefID() == VPRecipeBase::VPReductionPHISC;
  }
  static inline bool classof(const VPHeaderPHIRecipe *R) {
    return R->getVPDefID() == VPRecipeBase::VPReductionPHISC;
  }
  static inline bool classof(const VPValue *V) {
    return V->getVPValueID() == VPValue::VPVReductionPHISC;
  }
    void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
  const RecurrenceDescriptor &getRecurrenceDescriptor() const {
    return RdxDesc;
  }
    bool isOrdered() const { return IsOrdered; }
    bool isInLoop() const { return IsInLoop; }
};
class VPBlendRecipe : public VPRecipeBase, public VPValue {
  PHINode *Phi;
public:
        VPBlendRecipe(PHINode *Phi, ArrayRef<VPValue *> Operands)
      : VPRecipeBase(VPBlendSC, Operands),
        VPValue(VPValue::VPVBlendSC, Phi, this), Phi(Phi) {
    assert(Operands.size() > 0 &&
           ((Operands.size() == 1) || (Operands.size() % 2 == 0)) &&
           "Expected either a single incoming value or a positive even number "
           "of operands");
  }
    static inline bool classof(const VPDef *D) {
    return D->getVPDefID() == VPRecipeBase::VPBlendSC;
  }
      unsigned getNumIncomingValues() const { return (getNumOperands() + 1) / 2; }
    VPValue *getIncomingValue(unsigned Idx) const { return getOperand(Idx * 2); }
    VPValue *getMask(unsigned Idx) const { return getOperand(Idx * 2 + 1); }
    void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
    bool onlyFirstLaneUsed(const VPValue *Op) const override {
    assert(is_contained(operands(), Op) &&
           "Op must be an operand of the recipe");
            return all_of(users(),
                  [this](VPUser *U) { return U->onlyFirstLaneUsed(this); });
  }
};
class VPInterleaveRecipe : public VPRecipeBase {
  const InterleaveGroup<Instruction> *IG;
  bool HasMask = false;
public:
  VPInterleaveRecipe(const InterleaveGroup<Instruction> *IG, VPValue *Addr,
                     ArrayRef<VPValue *> StoredValues, VPValue *Mask)
      : VPRecipeBase(VPInterleaveSC, {Addr}), IG(IG) {
    for (unsigned i = 0; i < IG->getFactor(); ++i)
      if (Instruction *I = IG->getMember(i)) {
        if (I->getType()->isVoidTy())
          continue;
        new VPValue(I, this);
      }
    for (auto *SV : StoredValues)
      addOperand(SV);
    if (Mask) {
      HasMask = true;
      addOperand(Mask);
    }
  }
  ~VPInterleaveRecipe() override = default;
    static inline bool classof(const VPDef *D) {
    return D->getVPDefID() == VPRecipeBase::VPInterleaveSC;
  }
    VPValue *getAddr() const {
    return getOperand(0);   }
      VPValue *getMask() const {
        return HasMask ? getOperand(getNumOperands() - 1) : nullptr;
  }
      ArrayRef<VPValue *> getStoredValues() const {
            return ArrayRef<VPValue *>(op_begin(), getNumOperands())
        .slice(1, getNumStoreOperands());
  }
    void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
  const InterleaveGroup<Instruction> *getInterleaveGroup() { return IG; }
      unsigned getNumStoreOperands() const {
    return getNumOperands() - (HasMask ? 2 : 1);
  }
    bool onlyFirstLaneUsed(const VPValue *Op) const override {
    assert(is_contained(operands(), Op) &&
           "Op must be an operand of the recipe");
    return Op == getAddr() && all_of(getStoredValues(), [Op](VPValue *StoredV) {
             return Op != StoredV;
           });
  }
};
class VPReductionRecipe : public VPRecipeBase, public VPValue {
    const RecurrenceDescriptor *RdxDesc;
    const TargetTransformInfo *TTI;
public:
  VPReductionRecipe(const RecurrenceDescriptor *R, Instruction *I,
                    VPValue *ChainOp, VPValue *VecOp, VPValue *CondOp,
                    const TargetTransformInfo *TTI)
      : VPRecipeBase(VPRecipeBase::VPReductionSC, {ChainOp, VecOp}),
        VPValue(VPValue::VPVReductionSC, I, this), RdxDesc(R), TTI(TTI) {
    if (CondOp)
      addOperand(CondOp);
  }
  ~VPReductionRecipe() override = default;
    static inline bool classof(const VPValue *V) {
    return V->getVPValueID() == VPValue::VPVReductionSC;
  }
    void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
    VPValue *getChainOp() const { return getOperand(0); }
    VPValue *getVecOp() const { return getOperand(1); }
    VPValue *getCondOp() const {
    return getNumOperands() > 2 ? getOperand(2) : nullptr;
  }
};
class VPReplicateRecipe : public VPRecipeBase, public VPValue {
    bool IsUniform;
    bool IsPredicated;
    bool AlsoPack;
public:
  template <typename IterT>
  VPReplicateRecipe(Instruction *I, iterator_range<IterT> Operands,
                    bool IsUniform, bool IsPredicated = false)
      : VPRecipeBase(VPReplicateSC, Operands), VPValue(VPVReplicateSC, I, this),
        IsUniform(IsUniform), IsPredicated(IsPredicated) {
                        AlsoPack = IsPredicated && !I->use_empty();
  }
  ~VPReplicateRecipe() override = default;
    static inline bool classof(const VPDef *D) {
    return D->getVPDefID() == VPRecipeBase::VPReplicateSC;
  }
  static inline bool classof(const VPValue *V) {
    return V->getVPValueID() == VPValue::VPVReplicateSC;
  }
        void execute(VPTransformState &State) override;
  void setAlsoPack(bool Pack) { AlsoPack = Pack; }
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
  bool isUniform() const { return IsUniform; }
  bool isPacked() const { return AlsoPack; }
  bool isPredicated() const { return IsPredicated; }
    bool onlyFirstLaneUsed(const VPValue *Op) const override {
    assert(is_contained(operands(), Op) &&
           "Op must be an operand of the recipe");
    return isUniform();
  }
    bool usesScalars(const VPValue *Op) const override {
    assert(is_contained(operands(), Op) &&
           "Op must be an operand of the recipe");
    return true;
  }
};
class VPBranchOnMaskRecipe : public VPRecipeBase {
public:
  VPBranchOnMaskRecipe(VPValue *BlockInMask)
      : VPRecipeBase(VPBranchOnMaskSC, {}) {
    if (BlockInMask)       addOperand(BlockInMask);
  }
    static inline bool classof(const VPDef *D) {
    return D->getVPDefID() == VPRecipeBase::VPBranchOnMaskSC;
  }
      void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override {
    O << Indent << "BRANCH-ON-MASK ";
    if (VPValue *Mask = getMask())
      Mask->printAsOperand(O, SlotTracker);
    else
      O << " All-One";
  }
#endif
      VPValue *getMask() const {
    assert(getNumOperands() <= 1 && "should have either 0 or 1 operands");
        return getNumOperands() == 1 ? getOperand(0) : nullptr;
  }
    bool usesScalars(const VPValue *Op) const override {
    assert(is_contained(operands(), Op) &&
           "Op must be an operand of the recipe");
    return true;
  }
};
class VPPredInstPHIRecipe : public VPRecipeBase, public VPValue {
public:
      VPPredInstPHIRecipe(VPValue *PredV)
      : VPRecipeBase(VPPredInstPHISC, PredV),
        VPValue(VPValue::VPVPredInstPHI, nullptr, this) {}
  ~VPPredInstPHIRecipe() override = default;
    static inline bool classof(const VPDef *D) {
    return D->getVPDefID() == VPRecipeBase::VPPredInstPHISC;
  }
    void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
    bool usesScalars(const VPValue *Op) const override {
    assert(is_contained(operands(), Op) &&
           "Op must be an operand of the recipe");
    return true;
  }
};
class VPWidenMemoryInstructionRecipe : public VPRecipeBase {
  Instruction &Ingredient;
    bool Consecutive;
    bool Reverse;
  void setMask(VPValue *Mask) {
    if (!Mask)
      return;
    addOperand(Mask);
  }
  bool isMasked() const {
    return isStore() ? getNumOperands() == 3 : getNumOperands() == 2;
  }
public:
  VPWidenMemoryInstructionRecipe(LoadInst &Load, VPValue *Addr, VPValue *Mask,
                                 bool Consecutive, bool Reverse)
      : VPRecipeBase(VPWidenMemoryInstructionSC, {Addr}), Ingredient(Load),
        Consecutive(Consecutive), Reverse(Reverse) {
    assert((Consecutive || !Reverse) && "Reverse implies consecutive");
    new VPValue(VPValue::VPVMemoryInstructionSC, &Load, this);
    setMask(Mask);
  }
  VPWidenMemoryInstructionRecipe(StoreInst &Store, VPValue *Addr,
                                 VPValue *StoredValue, VPValue *Mask,
                                 bool Consecutive, bool Reverse)
      : VPRecipeBase(VPWidenMemoryInstructionSC, {Addr, StoredValue}),
        Ingredient(Store), Consecutive(Consecutive), Reverse(Reverse) {
    assert((Consecutive || !Reverse) && "Reverse implies consecutive");
    setMask(Mask);
  }
    static inline bool classof(const VPDef *D) {
    return D->getVPDefID() == VPRecipeBase::VPWidenMemoryInstructionSC;
  }
    VPValue *getAddr() const {
    return getOperand(0);   }
      VPValue *getMask() const {
        return isMasked() ? getOperand(getNumOperands() - 1) : nullptr;
  }
    bool isStore() const { return isa<StoreInst>(Ingredient); }
    VPValue *getStoredValue() const {
    assert(isStore() && "Stored value only available for store instructions");
    return getOperand(1);   }
    bool isConsecutive() const { return Consecutive; }
      bool isReverse() const { return Reverse; }
    void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
    bool onlyFirstLaneUsed(const VPValue *Op) const override {
    assert(is_contained(operands(), Op) &&
           "Op must be an operand of the recipe");
                return Op == getAddr() && isConsecutive() &&
           (!isStore() || Op != getStoredValue());
  }
  Instruction &getIngredient() const { return Ingredient; }
};
class VPExpandSCEVRecipe : public VPRecipeBase, public VPValue {
  const SCEV *Expr;
  ScalarEvolution &SE;
public:
  VPExpandSCEVRecipe(const SCEV *Expr, ScalarEvolution &SE)
      : VPRecipeBase(VPExpandSCEVSC, {}), VPValue(nullptr, this), Expr(Expr),
        SE(SE) {}
  ~VPExpandSCEVRecipe() override = default;
    static inline bool classof(const VPDef *D) {
    return D->getVPDefID() == VPExpandSCEVSC;
  }
    void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
  const SCEV *getSCEV() const { return Expr; }
};
class VPCanonicalIVPHIRecipe : public VPHeaderPHIRecipe {
  DebugLoc DL;
public:
  VPCanonicalIVPHIRecipe(VPValue *StartV, DebugLoc DL)
      : VPHeaderPHIRecipe(VPValue::VPVCanonicalIVPHISC, VPCanonicalIVPHISC,
                          nullptr, StartV),
        DL(DL) {}
  ~VPCanonicalIVPHIRecipe() override = default;
    static inline bool classof(const VPDef *D) {
    return D->getVPDefID() == VPCanonicalIVPHISC;
  }
  static inline bool classof(const VPHeaderPHIRecipe *D) {
    return D->getVPDefID() == VPCanonicalIVPHISC;
  }
  static inline bool classof(const VPValue *V) {
    return V->getVPValueID() == VPValue::VPVCanonicalIVPHISC;
  }
    void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
    const Type *getScalarType() const {
    return getOperand(0)->getLiveInIRValue()->getType();
  }
    bool onlyFirstLaneUsed(const VPValue *Op) const override {
    assert(is_contained(operands(), Op) &&
           "Op must be an operand of the recipe");
    return true;
  }
};
class VPActiveLaneMaskPHIRecipe : public VPHeaderPHIRecipe {
  DebugLoc DL;
public:
  VPActiveLaneMaskPHIRecipe(VPValue *StartMask, DebugLoc DL)
      : VPHeaderPHIRecipe(VPValue::VPVActiveLaneMaskPHISC,
                          VPActiveLaneMaskPHISC, nullptr, StartMask),
        DL(DL) {}
  ~VPActiveLaneMaskPHIRecipe() override = default;
    static inline bool classof(const VPDef *D) {
    return D->getVPDefID() == VPActiveLaneMaskPHISC;
  }
  static inline bool classof(const VPHeaderPHIRecipe *D) {
    return D->getVPDefID() == VPActiveLaneMaskPHISC;
  }
  static inline bool classof(const VPValue *V) {
    return V->getVPValueID() == VPValue::VPVActiveLaneMaskPHISC;
  }
    void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
};
class VPWidenCanonicalIVRecipe : public VPRecipeBase, public VPValue {
public:
  VPWidenCanonicalIVRecipe(VPCanonicalIVPHIRecipe *CanonicalIV)
      : VPRecipeBase(VPWidenCanonicalIVSC, {CanonicalIV}),
        VPValue(VPValue::VPVWidenCanonicalIVSC, nullptr, this) {}
  ~VPWidenCanonicalIVRecipe() override = default;
    static inline bool classof(const VPDef *D) {
    return D->getVPDefID() == VPRecipeBase::VPWidenCanonicalIVSC;
  }
      static inline bool classof(const VPUser *U) {
    auto *R = dyn_cast<VPRecipeBase>(U);
    return R && R->getVPDefID() == VPRecipeBase::VPWidenCanonicalIVSC;
  }
  static inline bool classof(const VPRecipeBase *R) {
    return R->getVPDefID() == VPRecipeBase::VPWidenCanonicalIVSC;
  }
        void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
    const Type *getScalarType() const {
    return cast<VPCanonicalIVPHIRecipe>(getOperand(0)->getDef())
        ->getScalarType();
  }
};
class VPScalarIVStepsRecipe : public VPRecipeBase, public VPValue {
    Type *Ty;
    Type *TruncToTy;
  const InductionDescriptor &IndDesc;
public:
  VPScalarIVStepsRecipe(Type *Ty, const InductionDescriptor &IndDesc,
                        VPValue *CanonicalIV, VPValue *Start, VPValue *Step,
                        Type *TruncToTy)
      : VPRecipeBase(VPScalarIVStepsSC, {CanonicalIV, Start, Step}),
        VPValue(nullptr, this), Ty(Ty), TruncToTy(TruncToTy), IndDesc(IndDesc) {
  }
  ~VPScalarIVStepsRecipe() override = default;
    static inline bool classof(const VPDef *D) {
    return D->getVPDefID() == VPRecipeBase::VPScalarIVStepsSC;
  }
      static inline bool classof(const VPUser *U) {
    auto *R = dyn_cast<VPRecipeBase>(U);
    return R && R->getVPDefID() == VPRecipeBase::VPScalarIVStepsSC;
  }
  static inline bool classof(const VPRecipeBase *R) {
    return R->getVPDefID() == VPRecipeBase::VPScalarIVStepsSC;
  }
    void execute(VPTransformState &State) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
#endif
      bool isCanonical() const;
  VPCanonicalIVPHIRecipe *getCanonicalIV() const;
  VPValue *getStartValue() const { return getOperand(1); }
  VPValue *getStepValue() const { return getOperand(2); }
    bool onlyFirstLaneUsed(const VPValue *Op) const override {
    assert(is_contained(operands(), Op) &&
           "Op must be an operand of the recipe");
    return true;
  }
};
class VPBasicBlock : public VPBlockBase {
public:
  using RecipeListTy = iplist<VPRecipeBase>;
private:
    RecipeListTy Recipes;
public:
  VPBasicBlock(const Twine &Name = "", VPRecipeBase *Recipe = nullptr)
      : VPBlockBase(VPBasicBlockSC, Name.str()) {
    if (Recipe)
      appendRecipe(Recipe);
  }
  ~VPBasicBlock() override {
    while (!Recipes.empty())
      Recipes.pop_back();
  }
    using iterator = RecipeListTy::iterator;
  using const_iterator = RecipeListTy::const_iterator;
  using reverse_iterator = RecipeListTy::reverse_iterator;
  using const_reverse_iterator = RecipeListTy::const_reverse_iterator;
        inline iterator begin() { return Recipes.begin(); }
  inline const_iterator begin() const { return Recipes.begin(); }
  inline iterator end() { return Recipes.end(); }
  inline const_iterator end() const { return Recipes.end(); }
  inline reverse_iterator rbegin() { return Recipes.rbegin(); }
  inline const_reverse_iterator rbegin() const { return Recipes.rbegin(); }
  inline reverse_iterator rend() { return Recipes.rend(); }
  inline const_reverse_iterator rend() const { return Recipes.rend(); }
  inline size_t size() const { return Recipes.size(); }
  inline bool empty() const { return Recipes.empty(); }
  inline const VPRecipeBase &front() const { return Recipes.front(); }
  inline VPRecipeBase &front() { return Recipes.front(); }
  inline const VPRecipeBase &back() const { return Recipes.back(); }
  inline VPRecipeBase &back() { return Recipes.back(); }
    RecipeListTy &getRecipeList() { return Recipes; }
    static RecipeListTy VPBasicBlock::*getSublistAccess(VPRecipeBase *) {
    return &VPBasicBlock::Recipes;
  }
    static inline bool classof(const VPBlockBase *V) {
    return V->getVPBlockID() == VPBlockBase::VPBasicBlockSC;
  }
  void insert(VPRecipeBase *Recipe, iterator InsertPt) {
    assert(Recipe && "No recipe to append.");
    assert(!Recipe->Parent && "Recipe already in VPlan");
    Recipe->Parent = this;
    Recipes.insert(InsertPt, Recipe);
  }
      void appendRecipe(VPRecipeBase *Recipe) { insert(Recipe, end()); }
      void execute(struct VPTransformState *State) override;
    iterator getFirstNonPhi();
    iterator_range<iterator> phis() {
    return make_range(begin(), getFirstNonPhi());
  }
  void dropAllReferences(VPValue *NewValue) override;
        VPBasicBlock *splitAt(iterator SplitAt);
  VPRegionBlock *getEnclosingLoopRegion();
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
            void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
  using VPBlockBase::print; #endif
      VPRecipeBase *getTerminator();
  const VPRecipeBase *getTerminator() const;
    bool isExiting() const;
private:
      BasicBlock *createEmptyBasicBlock(VPTransformState::CFGState &CFG);
};
class VPRegionBlock : public VPBlockBase {
    VPBlockBase *Entry;
      VPBlockBase *Exiting;
      bool IsReplicator;
public:
  VPRegionBlock(VPBlockBase *Entry, VPBlockBase *Exiting,
                const std::string &Name = "", bool IsReplicator = false)
      : VPBlockBase(VPRegionBlockSC, Name), Entry(Entry), Exiting(Exiting),
        IsReplicator(IsReplicator) {
    assert(Entry->getPredecessors().empty() && "Entry block has predecessors.");
    assert(Exiting->getSuccessors().empty() && "Exit block has successors.");
    Entry->setParent(this);
    Exiting->setParent(this);
  }
  VPRegionBlock(const std::string &Name = "", bool IsReplicator = false)
      : VPBlockBase(VPRegionBlockSC, Name), Entry(nullptr), Exiting(nullptr),
        IsReplicator(IsReplicator) {}
  ~VPRegionBlock() override {
    if (Entry) {
      VPValue DummyValue;
      Entry->dropAllReferences(&DummyValue);
      deleteCFG(Entry);
    }
  }
    static inline bool classof(const VPBlockBase *V) {
    return V->getVPBlockID() == VPBlockBase::VPRegionBlockSC;
  }
  const VPBlockBase *getEntry() const { return Entry; }
  VPBlockBase *getEntry() { return Entry; }
      void setEntry(VPBlockBase *EntryBlock) {
    assert(EntryBlock->getPredecessors().empty() &&
           "Entry block cannot have predecessors.");
    Entry = EntryBlock;
    EntryBlock->setParent(this);
  }
          VPBlockBase &front() const { return *Entry; }
  const VPBlockBase *getExiting() const { return Exiting; }
  VPBlockBase *getExiting() { return Exiting; }
      void setExiting(VPBlockBase *ExitingBlock) {
    assert(ExitingBlock->getSuccessors().empty() &&
           "Exit block cannot have successors.");
    Exiting = ExitingBlock;
    ExitingBlock->setParent(this);
  }
    VPBasicBlock *getPreheaderVPBB() {
    assert(!isReplicator() && "should only get pre-header of loop regions");
    return getSinglePredecessor()->getExitingBasicBlock();
  }
      bool isReplicator() const { return IsReplicator; }
      void execute(struct VPTransformState *State) override;
  void dropAllReferences(VPValue *NewValue) override;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
              void print(raw_ostream &O, const Twine &Indent,
             VPSlotTracker &SlotTracker) const override;
  using VPBlockBase::print; #endif
};
template <> struct GraphTraits<VPBlockBase *> {
  using NodeRef = VPBlockBase *;
  using ChildIteratorType = SmallVectorImpl<VPBlockBase *>::iterator;
  static NodeRef getEntryNode(NodeRef N) { return N; }
  static inline ChildIteratorType child_begin(NodeRef N) {
    return N->getSuccessors().begin();
  }
  static inline ChildIteratorType child_end(NodeRef N) {
    return N->getSuccessors().end();
  }
};
template <> struct GraphTraits<const VPBlockBase *> {
  using NodeRef = const VPBlockBase *;
  using ChildIteratorType = SmallVectorImpl<VPBlockBase *>::const_iterator;
  static NodeRef getEntryNode(NodeRef N) { return N; }
  static inline ChildIteratorType child_begin(NodeRef N) {
    return N->getSuccessors().begin();
  }
  static inline ChildIteratorType child_end(NodeRef N) {
    return N->getSuccessors().end();
  }
};
template <> struct GraphTraits<Inverse<VPBlockBase *>> {
  using NodeRef = VPBlockBase *;
  using ChildIteratorType = SmallVectorImpl<VPBlockBase *>::iterator;
  static NodeRef getEntryNode(Inverse<NodeRef> B) { return B.Graph; }
  static inline ChildIteratorType child_begin(NodeRef N) {
    return N->getPredecessors().begin();
  }
  static inline ChildIteratorType child_end(NodeRef N) {
    return N->getPredecessors().end();
  }
};
template <>
struct GraphTraits<VPRegionBlock *> : public GraphTraits<VPBlockBase *> {
  using GraphRef = VPRegionBlock *;
  using nodes_iterator = df_iterator<NodeRef>;
  static NodeRef getEntryNode(GraphRef N) { return N->getEntry(); }
  static nodes_iterator nodes_begin(GraphRef N) {
    return nodes_iterator::begin(N->getEntry());
  }
  static nodes_iterator nodes_end(GraphRef N) {
            return nodes_iterator::end(N);
  }
};
template <>
struct GraphTraits<const VPRegionBlock *>
    : public GraphTraits<const VPBlockBase *> {
  using GraphRef = const VPRegionBlock *;
  using nodes_iterator = df_iterator<NodeRef>;
  static NodeRef getEntryNode(GraphRef N) { return N->getEntry(); }
  static nodes_iterator nodes_begin(GraphRef N) {
    return nodes_iterator::begin(N->getEntry());
  }
  static nodes_iterator nodes_end(GraphRef N) {
            return nodes_iterator::end(N);
  }
};
template <>
struct GraphTraits<Inverse<VPRegionBlock *>>
    : public GraphTraits<Inverse<VPBlockBase *>> {
  using GraphRef = VPRegionBlock *;
  using nodes_iterator = df_iterator<NodeRef>;
  static NodeRef getEntryNode(Inverse<GraphRef> N) {
    return N.Graph->getExiting();
  }
  static nodes_iterator nodes_begin(GraphRef N) {
    return nodes_iterator::begin(N->getExiting());
  }
  static nodes_iterator nodes_end(GraphRef N) {
            return nodes_iterator::end(N);
  }
};
template <typename BlockPtrTy>
class VPAllSuccessorsIterator
    : public iterator_facade_base<VPAllSuccessorsIterator<BlockPtrTy>,
                                  std::forward_iterator_tag, VPBlockBase> {
  BlockPtrTy Block;
          size_t SuccessorIdx;
  static BlockPtrTy getBlockWithSuccs(BlockPtrTy Current) {
    while (Current && Current->getNumSuccessors() == 0)
      Current = Current->getParent();
    return Current;
  }
      template <typename T1> static T1 deref(T1 Block, unsigned SuccIdx) {
    if (auto *R = dyn_cast<VPRegionBlock>(Block)) {
      if (SuccIdx == 0)
        return R->getEntry();
      SuccIdx--;
    }
        return getBlockWithSuccs(Block)->getSuccessors()[SuccIdx];
  }
public:
  VPAllSuccessorsIterator(BlockPtrTy Block, size_t Idx = 0)
      : Block(Block), SuccessorIdx(Idx) {}
  VPAllSuccessorsIterator(const VPAllSuccessorsIterator &Other)
      : Block(Other.Block), SuccessorIdx(Other.SuccessorIdx) {}
  VPAllSuccessorsIterator &operator=(const VPAllSuccessorsIterator &R) {
    Block = R.Block;
    SuccessorIdx = R.SuccessorIdx;
    return *this;
  }
  static VPAllSuccessorsIterator end(BlockPtrTy Block) {
    BlockPtrTy ParentWithSuccs = getBlockWithSuccs(Block);
    unsigned NumSuccessors = ParentWithSuccs
                                 ? ParentWithSuccs->getNumSuccessors()
                                 : Block->getNumSuccessors();
    if (auto *R = dyn_cast<VPRegionBlock>(Block))
      return {R, NumSuccessors + 1};
    return {Block, NumSuccessors};
  }
  bool operator==(const VPAllSuccessorsIterator &R) const {
    return Block == R.Block && SuccessorIdx == R.SuccessorIdx;
  }
  const VPBlockBase *operator*() const { return deref(Block, SuccessorIdx); }
  BlockPtrTy operator*() { return deref(Block, SuccessorIdx); }
  VPAllSuccessorsIterator &operator++() {
    SuccessorIdx++;
    return *this;
  }
  VPAllSuccessorsIterator operator++(int X) {
    VPAllSuccessorsIterator Orig = *this;
    SuccessorIdx++;
    return Orig;
  }
};
template <typename BlockTy> class VPBlockRecursiveTraversalWrapper {
  BlockTy Entry;
public:
  VPBlockRecursiveTraversalWrapper(BlockTy Entry) : Entry(Entry) {}
  BlockTy getEntry() { return Entry; }
};
template <>
struct GraphTraits<VPBlockRecursiveTraversalWrapper<VPBlockBase *>> {
  using NodeRef = VPBlockBase *;
  using ChildIteratorType = VPAllSuccessorsIterator<VPBlockBase *>;
  static NodeRef
  getEntryNode(VPBlockRecursiveTraversalWrapper<VPBlockBase *> N) {
    return N.getEntry();
  }
  static inline ChildIteratorType child_begin(NodeRef N) {
    return ChildIteratorType(N);
  }
  static inline ChildIteratorType child_end(NodeRef N) {
    return ChildIteratorType::end(N);
  }
};
template <>
struct GraphTraits<VPBlockRecursiveTraversalWrapper<const VPBlockBase *>> {
  using NodeRef = const VPBlockBase *;
  using ChildIteratorType = VPAllSuccessorsIterator<const VPBlockBase *>;
  static NodeRef
  getEntryNode(VPBlockRecursiveTraversalWrapper<const VPBlockBase *> N) {
    return N.getEntry();
  }
  static inline ChildIteratorType child_begin(NodeRef N) {
    return ChildIteratorType(N);
  }
  static inline ChildIteratorType child_end(NodeRef N) {
    return ChildIteratorType::end(N);
  }
};
class VPlan {
  friend class VPlanPrinter;
  friend class VPSlotTracker;
    VPBlockBase *Entry;
    SmallSetVector<ElementCount, 2> VFs;
    std::string Name;
      DenseMap<Value *, VPValue *> VPExternalDefs;
      VPValue *TripCount = nullptr;
      VPValue *BackedgeTakenCount = nullptr;
    VPValue VectorTripCount;
      Value2VPValueTy Value2VPValue;
      SmallVector<VPValue *, 16> VPValuesToFree;
      bool Value2VPValueEnabled = true;
    MapVector<PHINode *, VPLiveOut *> LiveOuts;
public:
  VPlan(VPBlockBase *Entry = nullptr) : Entry(Entry) {
    if (Entry)
      Entry->setPlan(this);
  }
  ~VPlan() {
    clearLiveOuts();
    if (Entry) {
      VPValue DummyValue;
      for (VPBlockBase *Block : depth_first(Entry))
        Block->dropAllReferences(&DummyValue);
      VPBlockBase::deleteCFG(Entry);
    }
    for (VPValue *VPV : VPValuesToFree)
      delete VPV;
    if (TripCount)
      delete TripCount;
    if (BackedgeTakenCount)
      delete BackedgeTakenCount;
    for (auto &P : VPExternalDefs)
      delete P.second;
  }
    void prepareToExecute(Value *TripCount, Value *VectorTripCount,
                        Value *CanonicalIVStartValue, VPTransformState &State,
                        bool IsEpilogueVectorization);
    void execute(struct VPTransformState *State);
  VPBlockBase *getEntry() { return Entry; }
  const VPBlockBase *getEntry() const { return Entry; }
  VPBlockBase *setEntry(VPBlockBase *Block) {
    Entry = Block;
    Block->setPlan(this);
    return Entry;
  }
    VPValue *getOrCreateTripCount() {
    if (!TripCount)
      TripCount = new VPValue();
    return TripCount;
  }
    VPValue *getOrCreateBackedgeTakenCount() {
    if (!BackedgeTakenCount)
      BackedgeTakenCount = new VPValue();
    return BackedgeTakenCount;
  }
    VPValue &getVectorTripCount() { return VectorTripCount; }
      void disableValue2VPValue() { Value2VPValueEnabled = false; }
  void addVF(ElementCount VF) { VFs.insert(VF); }
  bool hasVF(ElementCount VF) { return VFs.count(VF); }
  const std::string &getName() const { return Name; }
  void setName(const Twine &newName) { Name = newName.str(); }
    VPValue *getOrAddExternalDef(Value *V) {
    auto I = VPExternalDefs.insert({V, nullptr});
    if (I.second)
      I.first->second = new VPValue(V);
    return I.first->second;
  }
  void addVPValue(Value *V) {
    assert(Value2VPValueEnabled &&
           "IR value to VPValue mapping may be out of date!");
    assert(V && "Trying to add a null Value to VPlan");
    assert(!Value2VPValue.count(V) && "Value already exists in VPlan");
    VPValue *VPV = new VPValue(V);
    Value2VPValue[V] = VPV;
    VPValuesToFree.push_back(VPV);
  }
  void addVPValue(Value *V, VPValue *VPV) {
    assert(Value2VPValueEnabled && "Value2VPValue mapping may be out of date!");
    assert(V && "Trying to add a null Value to VPlan");
    assert(!Value2VPValue.count(V) && "Value already exists in VPlan");
    Value2VPValue[V] = VPV;
  }
      VPValue *getVPValue(Value *V, bool OverrideAllowed = false) {
    assert((OverrideAllowed || isa<Constant>(V) || Value2VPValueEnabled) &&
           "Value2VPValue mapping may be out of date!");
    assert(V && "Trying to get the VPValue of a null Value");
    assert(Value2VPValue.count(V) && "Value does not exist in VPlan");
    return Value2VPValue[V];
  }
        VPValue *getOrAddVPValue(Value *V, bool OverrideAllowed = false) {
    assert((OverrideAllowed || isa<Constant>(V) || Value2VPValueEnabled) &&
           "Value2VPValue mapping may be out of date!");
    assert(V && "Trying to get or add the VPValue of a null Value");
    if (!Value2VPValue.count(V))
      addVPValue(V);
    return getVPValue(V);
  }
  void removeVPValueFor(Value *V) {
    assert(Value2VPValueEnabled &&
           "IR value to VPValue mapping may be out of date!");
    Value2VPValue.erase(V);
  }
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void print(raw_ostream &O) const;
    void printDOT(raw_ostream &O) const;
    LLVM_DUMP_METHOD void dump() const;
#endif
      iterator_range<mapped_iterator<Use *, std::function<VPValue *(Value *)>>>
  mapToVPValues(User::op_range Operands) {
    std::function<VPValue *(Value *)> Fn = [this](Value *Op) {
      return getOrAddVPValue(Op);
    };
    return map_range(Operands, Fn);
  }
    bool isUniformAfterVectorization(VPValue *VPV) const {
    auto RepR = dyn_cast_or_null<VPReplicateRecipe>(VPV->getDef());
    return !VPV->getDef() || (RepR && RepR->isUniform());
  }
    VPRegionBlock *getVectorLoopRegion() {
    return cast<VPRegionBlock>(getEntry()->getSingleSuccessor());
  }
  const VPRegionBlock *getVectorLoopRegion() const {
    return cast<VPRegionBlock>(getEntry()->getSingleSuccessor());
  }
    VPCanonicalIVPHIRecipe *getCanonicalIV() {
    VPBasicBlock *EntryVPBB = getVectorLoopRegion()->getEntryBasicBlock();
    if (EntryVPBB->empty()) {
            EntryVPBB = cast<VPBasicBlock>(EntryVPBB->getSingleSuccessor());
    }
    return cast<VPCanonicalIVPHIRecipe>(&*EntryVPBB->begin());
  }
      VPActiveLaneMaskPHIRecipe *getActiveLaneMaskPhi();
  void addLiveOut(PHINode *PN, VPValue *V);
  void clearLiveOuts() {
    for (auto &KV : LiveOuts)
      delete KV.second;
    LiveOuts.clear();
  }
  void removeLiveOut(PHINode *PN) {
    delete LiveOuts[PN];
    LiveOuts.erase(PN);
  }
  const MapVector<PHINode *, VPLiveOut *> &getLiveOuts() const {
    return LiveOuts;
  }
private:
      static void updateDominatorTree(DominatorTree *DT, BasicBlock *LoopLatchBB,
                                  BasicBlock *LoopPreHeaderBB,
                                  BasicBlock *LoopExitBB);
};
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
class VPlanPrinter {
  raw_ostream &OS;
  const VPlan &Plan;
  unsigned Depth = 0;
  unsigned TabWidth = 2;
  std::string Indent;
  unsigned BID = 0;
  SmallDenseMap<const VPBlockBase *, unsigned> BlockID;
  VPSlotTracker SlotTracker;
    void bumpIndent(int b) { Indent = std::string((Depth += b) * TabWidth, ' '); }
    void dumpBlock(const VPBlockBase *Block);
      void dumpEdges(const VPBlockBase *Block);
      void dumpBasicBlock(const VPBasicBlock *BasicBlock);
    void dumpRegion(const VPRegionBlock *Region);
  unsigned getOrCreateBID(const VPBlockBase *Block) {
    return BlockID.count(Block) ? BlockID[Block] : BlockID[Block] = BID++;
  }
  Twine getOrCreateName(const VPBlockBase *Block);
  Twine getUID(const VPBlockBase *Block);
    void drawEdge(const VPBlockBase *From, const VPBlockBase *To, bool Hidden,
                const Twine &Label);
public:
  VPlanPrinter(raw_ostream &O, const VPlan &P)
      : OS(O), Plan(P), SlotTracker(&P) {}
  LLVM_DUMP_METHOD void dump();
};
struct VPlanIngredient {
  const Value *V;
  VPlanIngredient(const Value *V) : V(V) {}
  void print(raw_ostream &O) const;
};
inline raw_ostream &operator<<(raw_ostream &OS, const VPlanIngredient &I) {
  I.print(OS);
  return OS;
}
inline raw_ostream &operator<<(raw_ostream &OS, const VPlan &Plan) {
  Plan.print(OS);
  return OS;
}
#endif
class VPBlockUtils {
public:
  VPBlockUtils() = delete;
            static void insertBlockAfter(VPBlockBase *NewBlock, VPBlockBase *BlockPtr) {
    assert(NewBlock->getSuccessors().empty() &&
           NewBlock->getPredecessors().empty() &&
           "Can't insert new block with predecessors or successors.");
    NewBlock->setParent(BlockPtr->getParent());
    SmallVector<VPBlockBase *> Succs(BlockPtr->successors());
    for (VPBlockBase *Succ : Succs) {
      disconnectBlocks(BlockPtr, Succ);
      connectBlocks(NewBlock, Succ);
    }
    connectBlocks(BlockPtr, NewBlock);
  }
              static void insertTwoBlocksAfter(VPBlockBase *IfTrue, VPBlockBase *IfFalse,
                                   VPBlockBase *BlockPtr) {
    assert(IfTrue->getSuccessors().empty() &&
           "Can't insert IfTrue with successors.");
    assert(IfFalse->getSuccessors().empty() &&
           "Can't insert IfFalse with successors.");
    BlockPtr->setTwoSuccessors(IfTrue, IfFalse);
    IfTrue->setPredecessors({BlockPtr});
    IfFalse->setPredecessors({BlockPtr});
    IfTrue->setParent(BlockPtr->getParent());
    IfFalse->setParent(BlockPtr->getParent());
  }
          static void connectBlocks(VPBlockBase *From, VPBlockBase *To) {
    assert((From->getParent() == To->getParent()) &&
           "Can't connect two block with different parents");
    assert(From->getNumSuccessors() < 2 &&
           "Blocks can't have more than two successors.");
    From->appendSuccessor(To);
    To->appendPredecessor(From);
  }
      static void disconnectBlocks(VPBlockBase *From, VPBlockBase *To) {
    assert(To && "Successor to disconnect is null.");
    From->removeSuccessor(To);
    To->removePredecessor(From);
  }
        static VPBasicBlock *tryToMergeBlockIntoPredecessor(VPBlockBase *Block) {
    auto *VPBB = dyn_cast<VPBasicBlock>(Block);
    auto *PredVPBB =
        dyn_cast_or_null<VPBasicBlock>(Block->getSinglePredecessor());
    if (!VPBB || !PredVPBB || PredVPBB->getNumSuccessors() != 1)
      return nullptr;
    for (VPRecipeBase &R : make_early_inc_range(*VPBB))
      R.moveBefore(*PredVPBB, PredVPBB->end());
    VPBlockUtils::disconnectBlocks(PredVPBB, VPBB);
    auto *ParentRegion = cast<VPRegionBlock>(Block->getParent());
    if (ParentRegion->getExiting() == Block)
      ParentRegion->setExiting(PredVPBB);
    SmallVector<VPBlockBase *> Successors(Block->successors());
    for (auto *Succ : Successors) {
      VPBlockUtils::disconnectBlocks(Block, Succ);
      VPBlockUtils::connectBlocks(PredVPBB, Succ);
    }
    delete Block;
    return PredVPBB;
  }
      template <typename BlockTy, typename T>
  static auto blocksOnly(const T &Range) {
        using BaseTy =
        typename std::conditional<std::is_const<BlockTy>::value,
                                  const VPBlockBase, VPBlockBase>::type;
            auto Mapped =
        map_range(Range, [](BaseTy *Block) -> BaseTy & { return *Block; });
    auto Filter = make_filter_range(
        Mapped, [](BaseTy &Block) { return isa<BlockTy>(&Block); });
    return map_range(Filter, [](BaseTy &Block) -> BlockTy * {
      return cast<BlockTy>(&Block);
    });
  }
};
class VPInterleavedAccessInfo {
  DenseMap<VPInstruction *, InterleaveGroup<VPInstruction> *>
      InterleaveGroupMap;
      using Old2NewTy = DenseMap<InterleaveGroup<Instruction> *,
                             InterleaveGroup<VPInstruction> *>;
      void visitRegion(VPRegionBlock *Region, Old2NewTy &Old2New,
                   InterleavedAccessInfo &IAI);
      void visitBlock(VPBlockBase *Block, Old2NewTy &Old2New,
                  InterleavedAccessInfo &IAI);
public:
  VPInterleavedAccessInfo(VPlan &Plan, InterleavedAccessInfo &IAI);
  ~VPInterleavedAccessInfo() {
    SmallPtrSet<InterleaveGroup<VPInstruction> *, 4> DelSet;
        for (auto &I : InterleaveGroupMap)
      DelSet.insert(I.second);
    for (auto *Ptr : DelSet)
      delete Ptr;
  }
        InterleaveGroup<VPInstruction> *
  getInterleaveGroup(VPInstruction *Instr) const {
    return InterleaveGroupMap.lookup(Instr);
  }
};
class VPlanSlp {
  enum class OpMode { Failed, Load, Opcode };
      struct BundleDenseMapInfo {
    static SmallVector<VPValue *, 4> getEmptyKey() {
      return {reinterpret_cast<VPValue *>(-1)};
    }
    static SmallVector<VPValue *, 4> getTombstoneKey() {
      return {reinterpret_cast<VPValue *>(-2)};
    }
    static unsigned getHashValue(const SmallVector<VPValue *, 4> &V) {
      return static_cast<unsigned>(hash_combine_range(V.begin(), V.end()));
    }
    static bool isEqual(const SmallVector<VPValue *, 4> &LHS,
                        const SmallVector<VPValue *, 4> &RHS) {
      return LHS == RHS;
    }
  };
    DenseMap<SmallVector<VPValue *, 4>, VPInstruction *, BundleDenseMapInfo>
      BundleToCombined;
  VPInterleavedAccessInfo &IAI;
      const VPBasicBlock &BB;
    bool CompletelySLP = true;
    unsigned WidestBundleBits = 0;
  using MultiNodeOpTy =
      typename std::pair<VPInstruction *, SmallVector<VPValue *, 4>>;
          SmallVector<MultiNodeOpTy, 4> MultiNodeOps;
    bool MultiNodeActive = false;
    bool areVectorizable(ArrayRef<VPValue *> Operands) const;
    void addCombined(ArrayRef<VPValue *> Operands, VPInstruction *New);
    VPInstruction *markFailed();
      SmallVector<MultiNodeOpTy, 4> reorderMultiNodeOps();
        std::pair<OpMode, VPValue *> getBest(OpMode Mode, VPValue *Last,
                                       SmallPtrSetImpl<VPValue *> &Candidates,
                                       VPInterleavedAccessInfo &IAI);
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    void dumpBundle(ArrayRef<VPValue *> Values);
#endif
public:
  VPlanSlp(VPInterleavedAccessInfo &IAI, VPBasicBlock &BB) : IAI(IAI), BB(BB) {}
  ~VPlanSlp() = default;
      VPInstruction *buildGraph(ArrayRef<VPValue *> Operands);
    unsigned getWidestBundleBits() const { return WidestBundleBits; }
    bool isCompletelySLP() const { return CompletelySLP; }
};
namespace vputils {
bool onlyFirstLaneUsed(VPValue *Def);
VPValue *getOrCreateVPValueForSCEVExpr(VPlan &Plan, const SCEV *Expr,
                                       ScalarEvolution &SE);
} 
} 
#endif