[llvm-branch-commits] [llvm] [GVN] More restructuring of `GVN.h` to reduce its size (NFC) (PR #211541)

Momchil Velikov via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Jul 23 05:56:50 PDT 2026


https://github.com/momchil-velikov created https://github.com/llvm/llvm-project/pull/211541

* `GVNPass` left as an interface for the pass manager. Actuall `GVNPass` moved under the name `GVNPassImpl` to `GVN.cpp`.
* Various helper types moved out of `GVNPassImpl` and into an anonymous namespace in `GVN.cpp`

>From a30a4522486b2442c672118a0db61ad49263ef64 Mon Sep 17 00:00:00 2001
From: Momchil Velikov <momchil.velikov at arm.com>
Date: Thu, 23 Jul 2026 11:10:42 +0100
Subject: [PATCH] [GVN] More restructuring of `GVN.h` to reduce its size (NFC)

* `GVNPass` left as an interface for the pass manager.
  Actually `GVNPass` moved under the name `GVNPassImpl` to `GVN.cpp`.
* Various helper types moved out of `GVNPassImpl` and into an anonymous
  namespace in `GVN.cpp`
---
 llvm/include/llvm/Transforms/Scalar/GVN.h |  190 +---
 llvm/lib/Transforms/Scalar/GVN.cpp        | 1076 +++++++++++++--------
 2 files changed, 656 insertions(+), 610 deletions(-)

diff --git a/llvm/include/llvm/Transforms/Scalar/GVN.h b/llvm/include/llvm/Transforms/Scalar/GVN.h
index f5816313bf214..08af9e2f8eeae 100644
--- a/llvm/include/llvm/Transforms/Scalar/GVN.h
+++ b/llvm/include/llvm/Transforms/Scalar/GVN.h
@@ -123,45 +123,18 @@ struct GVNOptions {
 ///
 /// FIXME: We should have a good summary of the GVN algorithm implemented by
 /// this particular pass here.
+class GVNPassImpl;
 class GVNPass : public OptionalPassInfoMixin<GVNPass> {
-public:
-  struct AvailableValue;
-  struct AvailableValueInBlock;
-  struct ReachingMemVal;
-  struct DependencyBlockInfo;
-
-  friend class GVNValueTable;
-  friend class GVNLegacyPass;
-
-private:
-  GVNOptions Options;
-  MemoryDependenceResults *MD = nullptr;
-  DominatorTree *DT = nullptr;
-  const TargetLibraryInfo *TLI = nullptr;
-  AssumptionCache *AC = nullptr;
-  SetVector<BasicBlock *> DeadBlocks;
-  OptimizationRemarkEmitter *ORE = nullptr;
-  ImplicitControlFlowTracking *ICF = nullptr;
-  LoopInfo *LI = nullptr;
-  AAResults *AA = nullptr;
-  MemorySSAUpdater *MSSAU = nullptr;
-  GVNValueTable VN;
-  GVNLeaderMap LeaderTable;
-
-  // Map the block to reversed postorder traversal number. It is used to
-  // find back edge easily.
-  DenseMap<AssertingVH<BasicBlock>, uint32_t> BlockRPONumber;
-
-  // This is set 'true' initially and also when new blocks have been added to
-  // the function being analyzed. This boolean is used to control the updating
-  // of BlockRPONumber prior to accessing the contents of BlockRPONumber.
-  bool InvalidBlockRPONumbers = true;
-
-  // List of critical edges to be split between iterations.
-  SmallVector<std::pair<Instruction *, unsigned>, 4> ToSplit;
+  std::unique_ptr<GVNPassImpl> Impl;
 
 public:
-  GVNPass(GVNOptions Options = {}) : Options(Options) {}
+  GVNPass(GVNOptions Options = {});
+  ~GVNPass();
+
+  GVNPass(const GVNPass &) = delete;
+  GVNPass(GVNPass &&);
+  GVNPass &operator=(const GVNPass &) = delete;
+  GVNPass &operator=(GVNPass &&);
 
   /// Run the pass over the function.
   LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
@@ -169,151 +142,6 @@ class GVNPass : public OptionalPassInfoMixin<GVNPass> {
   LLVM_ABI void
   printPipeline(raw_ostream &OS,
                 function_ref<StringRef(StringRef)> MapClassName2PassName);
-
-private:
-  DominatorTree &getDominatorTree() const { return *DT; }
-  AAResults *getAliasAnalysis() const { return VN.getAliasAnalysis(); }
-  MemoryDependenceResults &getMemDep() const { return *MD; }
-
-  bool isScalarPREEnabled() const;
-  bool isLoadPREEnabled() const;
-  bool isLoadInLoopPREEnabled() const;
-  bool isLoadPRESplitBackedgeEnabled() const;
-  bool isMemDepEnabled() const;
-  bool isMemorySSAEnabled() const;
-
-  using LoadDepVect = SmallVector<NonLocalDepResult, 64>;
-  using AvailValInBlkVect = SmallVector<AvailableValueInBlock, 64>;
-  using UnavailBlkVect = SmallVector<BasicBlock *, 64>;
-
-  using DependencyBlockSet = DenseMap<BasicBlock *, DependencyBlockInfo>;
-
-  /// Given a select-dependency for the load (the load address is a select of
-  /// \p TrueAddr and \p FalseAddr guarded by \p Cond), determine whether a
-  /// value is available by finding dominating values for both addresses.  If
-  /// so, the load can be rematerialized as a select of those two values.
-  std::optional<AvailableValue>
-  analyzeSelectAvailability(LoadInst *Load, Value *Cond, Value *TrueAddr,
-                            Value *FalseAddr, Instruction *From);
-
-  /// Given a local dependency (Def or Clobber) determine if a value is
-  /// available for the load.
-  std::optional<AvailableValue>
-  analyzeLoadAvailability(LoadInst *Load, const ReachingMemVal &Dep,
-                          Value *Address);
-
-  /// Given a list of non-local dependencies, determine if a value is
-  /// available for the load in each specified block.  If it is, add it to
-  /// ValuesPerBlock.  If not, add it to UnavailableBlocks.
-  void analyzeLoadAvailability(LoadInst *Load,
-                               SmallVectorImpl<ReachingMemVal> &Deps,
-                               AvailValInBlkVect &ValuesPerBlock,
-                               UnavailBlkVect &UnavailableBlocks);
-
-  /// Given a critical edge from Pred to LoadBB, find a load instruction
-  /// which is identical to Load from another successor of Pred.
-  LoadInst *findLoadToHoistIntoPred(BasicBlock *Pred, BasicBlock *LoadBB,
-                                    LoadInst *Load);
-
-  /// Eliminates partially redundant \p Load, replacing it with \p
-  /// AvailableLoads (connected by Phis if needed).
-  void eliminatePartiallyRedundantLoad(
-      LoadInst *Load, AvailValInBlkVect &ValuesPerBlock,
-      MapVector<BasicBlock *, Value *> &AvailableLoads,
-      MapVector<BasicBlock *, LoadInst *> *CriticalEdgePredAndLoad);
-
-  // Helper functions for d etermining load dependencies.
-  std::optional<GVNPass::ReachingMemVal> scanMemoryAccessesUsers(
-      const MemoryLocation &Loc, bool IsInvariantLoad, BasicBlock *BB,
-      const SmallVectorImpl<MemoryAccess *> &ClobbersList, MemorySSA &MSSA,
-      BatchAAResults &AA, LoadInst *L = nullptr);
-
-  std::optional<GVNPass::ReachingMemVal>
-  accessMayModifyLocation(MemoryAccess *ClobberMA, const MemoryLocation &Loc,
-                          bool IsInvariantLoad, BasicBlock *BB, MemorySSA &MSSA,
-                          BatchAAResults &AA);
-
-  bool collectPredecessors(BasicBlock *BB, const PHITransAddr &Addr,
-                           MemoryAccess *ClobberMA, DependencyBlockSet &Blocks,
-                           SmallVectorImpl<BasicBlock *> &Worklist);
-
-  void collectClobberList(SmallVectorImpl<MemoryAccess *> &Clobbers,
-                          BasicBlock *BB, const DependencyBlockInfo &StartInfo,
-                          const DependencyBlockSet &Blocks, MemorySSA &MSSA);
-
-  bool findReachingValuesForLoad(LoadInst *Inst,
-                                 SmallVectorImpl<ReachingMemVal> &Values,
-                                 MemorySSA &MSSA, AAResults &AA);
-
-  bool performLoadPRE(LoadInst *Load, AvailValInBlkVect &ValuesPerBlock,
-                      UnavailBlkVect &UnavailableBlocks);
-
-  /// Try to replace a load which executes on each loop iteraiton with Phi
-  /// translation of load in preheader and load(s) in conditionally executed
-  /// paths.
-  bool performLoopLoadPRE(LoadInst *Load, AvailValInBlkVect &ValuesPerBlock,
-                          UnavailBlkVect &UnavailableBlocks);
-
-  // Try to eliminate redundent loades with non-local dependencies.
-  bool processNonLocalLoad(LoadInst *L);
-  bool processNonLocalLoad(LoadInst *L, SmallVectorImpl<ReachingMemVal> &Deps);
-
-  /// Add any blocks determined to be unreachable by a conditional branch with a
-  /// constant condition to the dead blocks.
-  bool processFoldableCondBr(CondBrInst *BI);
-
-  /// Propagate equalities derived from llvm.assume intrinsics.
-  bool processAssumeIntrinsic(AssumeInst *II);
-
-  /// Try to eliminate redundant loads.
-  bool processLoad(LoadInst *L);
-
-  /// Try to eliminate masked loads which have loaded from
-  /// masked stores with the same mask.
-  bool processMaskedLoad(IntrinsicInst *I);
-
-  /// Propagate value of a condition to blocks dominated by "then" and "else"
-  /// edges, as well as certains derived equalities.
-  bool
-  propagateEquality(Value *LHS, Value *RHS,
-                    const std::variant<BasicBlockEdge, Instruction *> &Root);
-
-  // Pass iteration helper functions.
-  bool processInstruction(Instruction *I);
-  bool processBlock(BasicBlock *BB);
-  bool iterateOnFunction(Function &F);
-
-  // Scalar PRE helper functions
-  bool performScalarPREInsertion(Instruction *Instr, BasicBlock *Pred,
-                                 BasicBlock *Curr, unsigned int ValNo);
-  bool performScalarPRE(Instruction *I);
-  bool performPRE(Function &F);
-
-  /// Main entry point for the GVN pass. Also used by the GVNLegacyPass.
-  bool runImpl(Function &F, AssumptionCache &RunAC, DominatorTree &RunDT,
-               const TargetLibraryInfo &RunTLI, AAResults &RunAA,
-               MemoryDependenceResults *RunMD, LoopInfo &LI,
-               OptimizationRemarkEmitter *ORE, MemorySSA *MSSA = nullptr);
-
-  // Other helper routines.
-
-  Value *findLeader(const BasicBlock *BB, uint32_t Num);
-  void cleanupGlobalSets();
-
-  void removeInstruction(Instruction *I);
-
-  /// This removes the specified instruction from
-  /// our various maps and marks it for deletion.
-  void salvageAndRemoveInstruction(Instruction *I);
-
-  void verifyRemoved(const Instruction *I) const;
-
-  bool splitCriticalEdges();
-  BasicBlock *splitCriticalEdges(BasicBlock *Pred, BasicBlock *Succ);
-
-  void addDeadBlock(BasicBlock *BB);
-  void assignValNumForDeadCode();
-  void assignBlockRPONumber(Function &F);
 };
 
 /// Create a legacy GVN pass.
diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp
index 35e21e0b63017..ce39ddabfd665 100644
--- a/llvm/lib/Transforms/Scalar/GVN.cpp
+++ b/llvm/lib/Transforms/Scalar/GVN.cpp
@@ -84,9 +84,6 @@ using namespace llvm;
 using namespace llvm::VNCoercion;
 using namespace PatternMatch;
 
-using AvailableValue = GVNPass::AvailableValue;
-using AvailableValueInBlock = GVNPass::AvailableValueInBlock;
-
 #define DEBUG_TYPE "gvn"
 
 STATISTIC(NumGVNInstr, "Number of instructions deleted");
@@ -191,202 +188,6 @@ template <> struct llvm::DenseMapInfo<GVNValueTable::Expression> {
   }
 };
 
-/// Represents a particular available value that we know how to materialize.
-/// Materialization of an AvailableValue never fails.  An AvailableValue is
-/// implicitly associated with a rematerialization point which is the
-/// location of the instruction from which it was formed.
-struct llvm::GVNPass::AvailableValue {
-  enum class ValType {
-    SimpleVal, // A simple offsetted value that is accessed.
-    LoadVal,   // A value produced by a load.
-    MemIntrin, // A memory intrinsic which is loaded from.
-    UndefVal,  // A UndefValue representing a value from dead block (which
-               // is not yet physically removed from the CFG).
-    SelectVal, // A pointer select which is loaded from and for which the load
-               // can be replace by a value select.
-  };
-
-  /// Val - The value that is live out of the block.
-  Value *Val;
-  /// Kind of the live-out value.
-  ValType Kind;
-
-  /// Offset - The byte offset in Val that is interesting for the load query.
-  unsigned Offset = 0;
-  /// V1, V2 - The dominating non-clobbered values of SelectVal.
-  Value *V1 = nullptr, *V2 = nullptr;
-
-  static AvailableValue get(Value *V, unsigned Offset = 0) {
-    AvailableValue Res;
-    Res.Val = V;
-    Res.Kind = ValType::SimpleVal;
-    Res.Offset = Offset;
-    return Res;
-  }
-
-  static AvailableValue getMI(MemIntrinsic *MI, unsigned Offset = 0) {
-    AvailableValue Res;
-    Res.Val = MI;
-    Res.Kind = ValType::MemIntrin;
-    Res.Offset = Offset;
-    return Res;
-  }
-
-  static AvailableValue getLoad(LoadInst *Load, unsigned Offset = 0) {
-    AvailableValue Res;
-    Res.Val = Load;
-    Res.Kind = ValType::LoadVal;
-    Res.Offset = Offset;
-    return Res;
-  }
-
-  static AvailableValue getUndef() {
-    AvailableValue Res;
-    Res.Val = nullptr;
-    Res.Kind = ValType::UndefVal;
-    Res.Offset = 0;
-    return Res;
-  }
-
-  static AvailableValue getSelect(Value *Cond, Value *V1, Value *V2) {
-    AvailableValue Res;
-    Res.Val = Cond;
-    Res.Kind = ValType::SelectVal;
-    Res.Offset = 0;
-    Res.V1 = V1;
-    Res.V2 = V2;
-    return Res;
-  }
-
-  bool isSimpleValue() const { return Kind == ValType::SimpleVal; }
-  bool isCoercedLoadValue() const { return Kind == ValType::LoadVal; }
-  bool isMemIntrinValue() const { return Kind == ValType::MemIntrin; }
-  bool isUndefValue() const { return Kind == ValType::UndefVal; }
-  bool isSelectValue() const { return Kind == ValType::SelectVal; }
-
-  Value *getSimpleValue() const {
-    assert(isSimpleValue() && "Wrong accessor");
-    return Val;
-  }
-
-  LoadInst *getCoercedLoadValue() const {
-    assert(isCoercedLoadValue() && "Wrong accessor");
-    return cast<LoadInst>(Val);
-  }
-
-  MemIntrinsic *getMemIntrinValue() const {
-    assert(isMemIntrinValue() && "Wrong accessor");
-    return cast<MemIntrinsic>(Val);
-  }
-
-  Value *getSelectCondition() const {
-    assert(isSelectValue() && "Wrong accessor");
-    return Val;
-  }
-
-  /// Emit code at the specified insertion point to adjust the value defined
-  /// here to the specified type. This handles various coercion cases.
-  Value *MaterializeAdjustedValue(LoadInst *Load, Instruction *InsertPt) const;
-};
-
-Value *AvailableValue::MaterializeAdjustedValue(LoadInst *Load,
-                                                Instruction *InsertPt) const {
-  Value *Res;
-  Type *LoadTy = Load->getType();
-  const DataLayout &DL = Load->getDataLayout();
-  if (isSimpleValue()) {
-    Res = getSimpleValue();
-    if (Res->getType() != LoadTy) {
-      Res = getValueForLoad(Res, Offset, LoadTy, InsertPt, Load->getFunction());
-
-      LLVM_DEBUG(dbgs() << "GVN COERCED NONLOCAL VAL:\nOffset: " << Offset
-                        << "  " << *getSimpleValue() << '\n'
-                        << *Res << '\n'
-                        << "\n\n\n");
-    }
-  } else if (isCoercedLoadValue()) {
-    LoadInst *CoercedLoad = getCoercedLoadValue();
-    if (CoercedLoad->getType() == LoadTy && Offset == 0) {
-      Res = CoercedLoad;
-      combineMetadataForCSE(CoercedLoad, Load, false);
-    } else {
-      Res = getValueForLoad(CoercedLoad, Offset, LoadTy, InsertPt,
-                            Load->getFunction());
-      // We are adding a new user for this load, for which the original
-      // metadata may not hold. Additionally, the new load may have a different
-      // size and type, so their metadata cannot be combined in any
-      // straightforward way.
-      // Drop all metadata that is not known to cause immediate UB on violation,
-      // unless the load has !noundef, in which case all metadata violations
-      // will be promoted to UB.
-      // !noalias and !alias.scope are kept: the load is not moved and still
-      // accesses the same memory, and these are independent of the load type
-      // and offset, so they remain valid for the coerced result.
-      if (!CoercedLoad->hasMetadata(LLVMContext::MD_noundef))
-        CoercedLoad->dropUnknownNonDebugMetadata(
-            {LLVMContext::MD_dereferenceable,
-             LLVMContext::MD_dereferenceable_or_null,
-             LLVMContext::MD_invariant_load, LLVMContext::MD_invariant_group,
-             LLVMContext::MD_alias_scope, LLVMContext::MD_noalias});
-      LLVM_DEBUG(dbgs() << "GVN COERCED NONLOCAL LOAD:\nOffset: " << Offset
-                        << "  " << *getCoercedLoadValue() << '\n'
-                        << *Res << '\n'
-                        << "\n\n\n");
-    }
-  } else if (isMemIntrinValue()) {
-    Res = getMemInstValueForLoad(getMemIntrinValue(), Offset, LoadTy, InsertPt,
-                                 DL);
-    LLVM_DEBUG(dbgs() << "GVN COERCED NONLOCAL MEM INTRIN:\nOffset: " << Offset
-                      << "  " << *getMemIntrinValue() << '\n'
-                      << *Res << '\n'
-                      << "\n\n\n");
-  } else if (isSelectValue()) {
-    // Introduce a new value select for a load from an eligible pointer select.
-    Value *Cond = getSelectCondition();
-    assert(V1 && V2 && "both value operands of the select must be present");
-    Res = SelectInst::Create(Cond, V1, V2, "", InsertPt->getIterator());
-    // We use the DebugLoc from the original load here, as this instruction
-    // materializes the value that would previously have been loaded.
-    cast<SelectInst>(Res)->setDebugLoc(Load->getDebugLoc());
-  } else {
-    llvm_unreachable("Should not materialize value from dead block");
-  }
-  assert(Res && "failed to materialize?");
-  return Res;
-}
-
-/// Represents an AvailableValue which can be rematerialized at the end of
-/// the associated BasicBlock.
-struct llvm::GVNPass::AvailableValueInBlock {
-  /// BB - The basic block in question.
-  BasicBlock *BB = nullptr;
-
-  /// AV - The actual available value.
-  AvailableValue AV;
-
-  static AvailableValueInBlock get(BasicBlock *BB, AvailableValue &&AV) {
-    AvailableValueInBlock Res;
-    Res.BB = BB;
-    Res.AV = std::move(AV);
-    return Res;
-  }
-
-  static AvailableValueInBlock get(BasicBlock *BB, Value *V,
-                                   unsigned Offset = 0) {
-    return get(BB, AvailableValue::get(V, Offset));
-  }
-
-  static AvailableValueInBlock getUndef(BasicBlock *BB) {
-    return get(BB, AvailableValue::getUndef());
-  }
-
-  /// Emit code at the end of this block to adjust the value defined here to
-  /// the specified type. This handles various coercion cases.
-  Value *MaterializeAdjustedValue(LoadInst *Load) const {
-    return AV.MaterializeAdjustedValue(Load, BB->getTerminator());
-  }
-};
-
 //===----------------------------------------------------------------------===//
 //                     ValueTable Internal Functions
 //===----------------------------------------------------------------------===//
@@ -1013,221 +814,565 @@ void GVNValueTable::erase(Value *V) {
     NumberingBB.erase(Num);
 }
 
-/// verifyRemoved - Verify that the value is removed from all internal data
-/// structures.
-void GVNValueTable::verifyRemoved(const Value *V) const {
-  assert(!ValueNumbering.contains(V) &&
-         "Inst still occurs in value numbering map!");
-}
+/// verifyRemoved - Verify that the value is removed from all internal data
+/// structures.
+void GVNValueTable::verifyRemoved(const Value *V) const {
+  assert(!ValueNumbering.contains(V) &&
+         "Inst still occurs in value numbering map!");
+}
+
+//===----------------------------------------------------------------------===//
+//                     LeaderMap External Functions
+//===----------------------------------------------------------------------===//
+
+/// Push a new Value to the LeaderTable onto the list for its value number.
+void GVNLeaderMap::insert(uint32_t N, Value *V, const BasicBlock *BB) {
+  const auto &[It, Inserted] = NumToLeaders.try_emplace(N, V, BB, nullptr);
+  if (!Inserted) {
+    // Key already exists: insert new node after the head.
+    auto *NewSlot = TableAllocator.Allocate<LeaderListNode>();
+    new (NewSlot) LeaderListNode(V, BB, It->second.Next);
+    It->second.Next = NewSlot;
+  }
+}
+
+/// Scan the list of values corresponding to a given
+/// value number, and remove the given instruction if encountered.
+void GVNLeaderMap::erase(uint32_t N, Instruction *I, const BasicBlock *BB) {
+  auto It = NumToLeaders.find(N);
+  if (It == NumToLeaders.end())
+    return;
+
+  LeaderListNode *Prev = nullptr;
+  LeaderListNode *Curr = &It->second;
+
+  while (Curr && (Curr->Entry.Val != I || Curr->Entry.BB != BB)) {
+    Prev = Curr;
+    Curr = Curr->Next;
+  }
+
+  if (!Curr)
+    return;
+
+  if (Prev) {
+    // Non-head node: unlink and destroy.
+    Prev->Next = Curr->Next;
+    Curr->~LeaderListNode();
+    TableAllocator.Deallocate<LeaderListNode>(Curr);
+  } else {
+    // Head node (stored by value in DenseMap).
+    if (!Curr->Next) {
+      // Only node; erase from map (DenseMap calls the destructor).
+      NumToLeaders.erase(It);
+    } else {
+      // Move second node's data into head, then destroy second node.
+      LeaderListNode *Next = Curr->Next;
+      Curr->Entry.Val = std::move(Next->Entry.Val);
+      Curr->Entry.BB = Next->Entry.BB;
+      Curr->Next = Next->Next;
+      Next->~LeaderListNode();
+      TableAllocator.Deallocate<LeaderListNode>(Next);
+    }
+  }
+}
+
+namespace {
+//===----------------------------------------------------------------------===//
+//                     Helper Dependency Information Classes
+//===----------------------------------------------------------------------===//
+
+enum class DepKind {
+  Other = 0, // Unknown value.
+  Def,       // Exactly overlapping locations.
+  Clobber,   // Reaching value superset of needed bits.
+  Select,    // Reaching value is a select of two reaching addresses.
+};
+
+// Describe a memory location value, such that there exists a path to a point
+// in the program, along which that memory location is not modified.
+struct ReachingMemVal {
+  DepKind Kind;
+  BasicBlock *Block;
+  const Value *Addr;
+  Instruction *Inst;
+  int32_t Offset;
+  // For DepKind::Select only: the condition and the two addresses referenced
+  // by the "true" and "false" side of the select-dependent load.
+  const Value *SelCond = nullptr;
+  const Value *SelTrueAddr = nullptr;
+  const Value *SelFalseAddr = nullptr;
+
+  static ReachingMemVal getUnknown(BasicBlock *BB, const Value *Addr,
+                                   Instruction *Inst = nullptr) {
+    return {DepKind::Other, BB, Addr, Inst, -1};
+  }
+
+  static ReachingMemVal getDef(const Value *Addr, Instruction *Inst) {
+    return {DepKind::Def, Inst->getParent(), Addr, Inst, -1};
+  }
+
+  static ReachingMemVal getClobber(const Value *Addr, Instruction *Inst,
+                                   int32_t Offset = -1) {
+    return {DepKind::Clobber, Inst->getParent(), Addr, Inst, Offset};
+  }
+
+  static ReachingMemVal getSelect(BasicBlock *BB, const Value *Cond,
+                                  const Value *TrueAddr,
+                                  const Value *FalseAddr) {
+    return {DepKind::Select, BB,       nullptr, nullptr, -1, Cond,
+            TrueAddr,        FalseAddr};
+  }
+};
+
+struct DependencyBlockInfo {
+  DependencyBlockInfo() = delete;
+  DependencyBlockInfo(const PHITransAddr &Addr, MemoryAccess *ClobberMA)
+      : Addr(Addr), InitialClobberMA(ClobberMA), ClobberMA(ClobberMA),
+        ForceUnknown(false), Visited(false) {}
+  PHITransAddr Addr;
+  MemoryAccess *InitialClobberMA;
+  MemoryAccess *ClobberMA;
+  std::optional<ReachingMemVal> MemVal;
+  bool ForceUnknown : 1;
+  bool Visited : 1;
+};
+
+enum class AvailabilityState : char {
+  /// We know the block *is not* fully available. This is a fixpoint.
+  Unavailable = 0,
+  /// We know the block *is* fully available. This is a fixpoint.
+  Available = 1,
+  /// We do not know whether the block is fully available or not,
+  /// but we are currently speculating that it will be.
+  /// If it would have turned out that the block was, in fact, not fully
+  /// available, this would have been cleaned up into an Unavailable.
+  SpeculativelyAvailable = 2,
+};
+
+/// Represents a particular available value that we know how to materialize.
+/// Materialization of an AvailableValue never fails.  An AvailableValue is
+/// implicitly associated with a rematerialization point which is the
+/// location of the instruction from which it was formed.
+struct AvailableValue {
+  enum class ValType {
+    SimpleVal, // A simple offsetted value that is accessed.
+    LoadVal,   // A value produced by a load.
+    MemIntrin, // A memory intrinsic which is loaded from.
+    UndefVal,  // A UndefValue representing a value from dead block (which
+               // is not yet physically removed from the CFG).
+    SelectVal, // A pointer select which is loaded from and for which the load
+               // can be replace by a value select.
+  };
+
+  /// Val - The value that is live out of the block.
+  Value *Val;
+  /// Kind of the live-out value.
+  ValType Kind;
+
+  /// Offset - The byte offset in Val that is interesting for the load query.
+  unsigned Offset = 0;
+  /// V1, V2 - The dominating non-clobbered values of SelectVal.
+  Value *V1 = nullptr, *V2 = nullptr;
+
+  static AvailableValue get(Value *V, unsigned Offset = 0) {
+    AvailableValue Res;
+    Res.Val = V;
+    Res.Kind = ValType::SimpleVal;
+    Res.Offset = Offset;
+    return Res;
+  }
+
+  static AvailableValue getMI(MemIntrinsic *MI, unsigned Offset = 0) {
+    AvailableValue Res;
+    Res.Val = MI;
+    Res.Kind = ValType::MemIntrin;
+    Res.Offset = Offset;
+    return Res;
+  }
+
+  static AvailableValue getLoad(LoadInst *Load, unsigned Offset = 0) {
+    AvailableValue Res;
+    Res.Val = Load;
+    Res.Kind = ValType::LoadVal;
+    Res.Offset = Offset;
+    return Res;
+  }
+
+  static AvailableValue getUndef() {
+    AvailableValue Res;
+    Res.Val = nullptr;
+    Res.Kind = ValType::UndefVal;
+    Res.Offset = 0;
+    return Res;
+  }
+
+  static AvailableValue getSelect(Value *Cond, Value *V1, Value *V2) {
+    AvailableValue Res;
+    Res.Val = Cond;
+    Res.Kind = ValType::SelectVal;
+    Res.Offset = 0;
+    Res.V1 = V1;
+    Res.V2 = V2;
+    return Res;
+  }
+
+  bool isSimpleValue() const { return Kind == ValType::SimpleVal; }
+  bool isCoercedLoadValue() const { return Kind == ValType::LoadVal; }
+  bool isMemIntrinValue() const { return Kind == ValType::MemIntrin; }
+  bool isUndefValue() const { return Kind == ValType::UndefVal; }
+  bool isSelectValue() const { return Kind == ValType::SelectVal; }
+
+  Value *getSimpleValue() const {
+    assert(isSimpleValue() && "Wrong accessor");
+    return Val;
+  }
+
+  LoadInst *getCoercedLoadValue() const {
+    assert(isCoercedLoadValue() && "Wrong accessor");
+    return cast<LoadInst>(Val);
+  }
+
+  MemIntrinsic *getMemIntrinValue() const {
+    assert(isMemIntrinValue() && "Wrong accessor");
+    return cast<MemIntrinsic>(Val);
+  }
+
+  Value *getSelectCondition() const {
+    assert(isSelectValue() && "Wrong accessor");
+    return Val;
+  }
+
+  /// Emit code at the specified insertion point to adjust the value defined
+  /// here to the specified type. This handles various coercion cases.
+  Value *MaterializeAdjustedValue(LoadInst *Load, Instruction *InsertPt) const;
+};
+
+Value *AvailableValue::MaterializeAdjustedValue(LoadInst *Load,
+                                                Instruction *InsertPt) const {
+  Value *Res;
+  Type *LoadTy = Load->getType();
+  const DataLayout &DL = Load->getDataLayout();
+  if (isSimpleValue()) {
+    Res = getSimpleValue();
+    if (Res->getType() != LoadTy) {
+      Res = getValueForLoad(Res, Offset, LoadTy, InsertPt, Load->getFunction());
+
+      LLVM_DEBUG(dbgs() << "GVN COERCED NONLOCAL VAL:\nOffset: " << Offset
+                        << "  " << *getSimpleValue() << '\n'
+                        << *Res << '\n'
+                        << "\n\n\n");
+    }
+  } else if (isCoercedLoadValue()) {
+    LoadInst *CoercedLoad = getCoercedLoadValue();
+    if (CoercedLoad->getType() == LoadTy && Offset == 0) {
+      Res = CoercedLoad;
+      combineMetadataForCSE(CoercedLoad, Load, false);
+    } else {
+      Res = getValueForLoad(CoercedLoad, Offset, LoadTy, InsertPt,
+                            Load->getFunction());
+      // We are adding a new user for this load, for which the original
+      // metadata may not hold. Additionally, the new load may have a different
+      // size and type, so their metadata cannot be combined in any
+      // straightforward way.
+      // Drop all metadata that is not known to cause immediate UB on violation,
+      // unless the load has !noundef, in which case all metadata violations
+      // will be promoted to UB.
+      // !noalias and !alias.scope are kept: the load is not moved and still
+      // accesses the same memory, and these are independent of the load type
+      // and offset, so they remain valid for the coerced result.
+      if (!CoercedLoad->hasMetadata(LLVMContext::MD_noundef))
+        CoercedLoad->dropUnknownNonDebugMetadata(
+            {LLVMContext::MD_dereferenceable,
+             LLVMContext::MD_dereferenceable_or_null,
+             LLVMContext::MD_invariant_load, LLVMContext::MD_invariant_group,
+             LLVMContext::MD_alias_scope, LLVMContext::MD_noalias});
+      LLVM_DEBUG(dbgs() << "GVN COERCED NONLOCAL LOAD:\nOffset: " << Offset
+                        << "  " << *getCoercedLoadValue() << '\n'
+                        << *Res << '\n'
+                        << "\n\n\n");
+    }
+  } else if (isMemIntrinValue()) {
+    Res = getMemInstValueForLoad(getMemIntrinValue(), Offset, LoadTy, InsertPt,
+                                 DL);
+    LLVM_DEBUG(dbgs() << "GVN COERCED NONLOCAL MEM INTRIN:\nOffset: " << Offset
+                      << "  " << *getMemIntrinValue() << '\n'
+                      << *Res << '\n'
+                      << "\n\n\n");
+  } else if (isSelectValue()) {
+    // Introduce a new value select for a load from an eligible pointer select.
+    Value *Cond = getSelectCondition();
+    assert(V1 && V2 && "both value operands of the select must be present");
+    Res = SelectInst::Create(Cond, V1, V2, "", InsertPt->getIterator());
+    // We use the DebugLoc from the original load here, as this instruction
+    // materializes the value that would previously have been loaded.
+    cast<SelectInst>(Res)->setDebugLoc(Load->getDebugLoc());
+  } else {
+    llvm_unreachable("Should not materialize value from dead block");
+  }
+  assert(Res && "failed to materialize?");
+  return Res;
+}
+
+/// Represents an AvailableValue which can be rematerialized at the end of
+/// the associated BasicBlock.
+struct AvailableValueInBlock {
+  /// BB - The basic block in question.
+  BasicBlock *BB = nullptr;
 
-//===----------------------------------------------------------------------===//
-//                     LeaderMap External Functions
-//===----------------------------------------------------------------------===//
+  /// AV - The actual available value.
+  AvailableValue AV;
 
-/// Push a new Value to the LeaderTable onto the list for its value number.
-void GVNLeaderMap::insert(uint32_t N, Value *V, const BasicBlock *BB) {
-  const auto &[It, Inserted] = NumToLeaders.try_emplace(N, V, BB, nullptr);
-  if (!Inserted) {
-    // Key already exists: insert new node after the head.
-    auto *NewSlot = TableAllocator.Allocate<LeaderListNode>();
-    new (NewSlot) LeaderListNode(V, BB, It->second.Next);
-    It->second.Next = NewSlot;
+  static AvailableValueInBlock get(BasicBlock *BB, AvailableValue &&AV) {
+    AvailableValueInBlock Res;
+    Res.BB = BB;
+    Res.AV = std::move(AV);
+    return Res;
   }
-}
-
-/// Scan the list of values corresponding to a given
-/// value number, and remove the given instruction if encountered.
-void GVNLeaderMap::erase(uint32_t N, Instruction *I, const BasicBlock *BB) {
-  auto It = NumToLeaders.find(N);
-  if (It == NumToLeaders.end())
-    return;
-
-  LeaderListNode *Prev = nullptr;
-  LeaderListNode *Curr = &It->second;
 
-  while (Curr && (Curr->Entry.Val != I || Curr->Entry.BB != BB)) {
-    Prev = Curr;
-    Curr = Curr->Next;
+  static AvailableValueInBlock get(BasicBlock *BB, Value *V,
+                                   unsigned Offset = 0) {
+    return get(BB, AvailableValue::get(V, Offset));
   }
 
-  if (!Curr)
-    return;
+  static AvailableValueInBlock getUndef(BasicBlock *BB) {
+    return get(BB, AvailableValue::getUndef());
+  }
 
-  if (Prev) {
-    // Non-head node: unlink and destroy.
-    Prev->Next = Curr->Next;
-    Curr->~LeaderListNode();
-    TableAllocator.Deallocate<LeaderListNode>(Curr);
-  } else {
-    // Head node (stored by value in DenseMap).
-    if (!Curr->Next) {
-      // Only node; erase from map (DenseMap calls the destructor).
-      NumToLeaders.erase(It);
-    } else {
-      // Move second node's data into head, then destroy second node.
-      LeaderListNode *Next = Curr->Next;
-      Curr->Entry.Val = std::move(Next->Entry.Val);
-      Curr->Entry.BB = Next->Entry.BB;
-      Curr->Next = Next->Next;
-      Next->~LeaderListNode();
-      TableAllocator.Deallocate<LeaderListNode>(Next);
-    }
+  /// Emit code at the end of this block to adjust the value defined here to
+  /// the specified type. This handles various coercion cases.
+  Value *MaterializeAdjustedValue(LoadInst *Load) const {
+    return AV.MaterializeAdjustedValue(Load, BB->getTerminator());
   }
-}
+};
+
+} // namespace
 
 //===----------------------------------------------------------------------===//
-//                     Helper Dependency Information Classes
+//                                GVN Pass
 //===----------------------------------------------------------------------===//
 
-enum class DepKind {
-  Other = 0, // Unknown value.
-  Def,       // Exactly overlapping locations.
-  Clobber,   // Reaching value superset of needed bits.
-  Select,    // Reaching value is a select of two reaching addresses.
-};
+/// The core GVN pass object.
+///
+/// FIXME: We should have a good summary of the GVN algorithm implemented by
+/// this particular pass here.
+class llvm::GVNPassImpl {
+public:
+  friend class GVNValueTable;
+  friend class GVNLegacyPass;
 
-// Describe a memory location value, such that there exists a path to a point
-// in the program, along which that memory location is not modified.
-struct GVNPass::ReachingMemVal {
-  DepKind Kind;
-  BasicBlock *Block;
-  const Value *Addr;
-  Instruction *Inst;
-  int32_t Offset;
-  // For DepKind::Select only: the condition and the two addresses referenced
-  // by the "true" and "false" side of the select-dependent load.
-  const Value *SelCond = nullptr;
-  const Value *SelTrueAddr = nullptr;
-  const Value *SelFalseAddr = nullptr;
+private:
+  GVNOptions Options;
+  MemoryDependenceResults *MD = nullptr;
+  DominatorTree *DT = nullptr;
+  const TargetLibraryInfo *TLI = nullptr;
+  AssumptionCache *AC = nullptr;
+  SetVector<BasicBlock *> DeadBlocks;
+  OptimizationRemarkEmitter *ORE = nullptr;
+  ImplicitControlFlowTracking *ICF = nullptr;
+  LoopInfo *LI = nullptr;
+  AAResults *AA = nullptr;
+  MemorySSAUpdater *MSSAU = nullptr;
+  GVNValueTable VN;
+  GVNLeaderMap LeaderTable;
+
+  // Map the block to reversed postorder traversal number. It is used to
+  // find back edge easily.
+  DenseMap<AssertingVH<BasicBlock>, uint32_t> BlockRPONumber;
+
+  // This is set 'true' initially and also when new blocks have been added to
+  // the function being analyzed. This boolean is used to control the updating
+  // of BlockRPONumber prior to accessing the contents of BlockRPONumber.
+  bool InvalidBlockRPONumbers = true;
+
+  // List of critical edges to be split between iterations.
+  SmallVector<std::pair<Instruction *, unsigned>, 4> ToSplit;
 
-  static ReachingMemVal getUnknown(BasicBlock *BB, const Value *Addr,
-                                   Instruction *Inst = nullptr) {
-    return {DepKind::Other, BB, Addr, Inst, -1};
-  }
+public:
+  GVNPassImpl(GVNOptions Options = {}) : Options(Options) {}
 
-  static ReachingMemVal getDef(const Value *Addr, Instruction *Inst) {
-    return {DepKind::Def, Inst->getParent(), Addr, Inst, -1};
-  }
+  void printPipeline(raw_ostream &OS,
+                     function_ref<StringRef(StringRef)> MapClassName2PassName);
 
-  static ReachingMemVal getClobber(const Value *Addr, Instruction *Inst,
-                                   int32_t Offset = -1) {
-    return {DepKind::Clobber, Inst->getParent(), Addr, Inst, Offset};
-  }
+  bool isScalarPREEnabled() const;
+  bool isLoadPREEnabled() const;
+  bool isLoadInLoopPREEnabled() const;
+  bool isLoadPRESplitBackedgeEnabled() const;
+  bool isMemDepEnabled() const;
+  bool isMemorySSAEnabled() const;
 
-  static ReachingMemVal getSelect(BasicBlock *BB, const Value *Cond,
-                                  const Value *TrueAddr,
-                                  const Value *FalseAddr) {
-    return {DepKind::Select, BB,       nullptr, nullptr, -1, Cond,
-            TrueAddr,        FalseAddr};
+  /// Main entry point for the GVN pass. Also used by the GVNLegacyPass.
+  bool run(Function &F, AssumptionCache &RunAC, DominatorTree &RunDT,
+           const TargetLibraryInfo &RunTLI, AAResults &RunAA,
+           MemoryDependenceResults *RunMD, LoopInfo &LI,
+           OptimizationRemarkEmitter *ORE, MemorySSA *MSSA = nullptr);
+
+private:
+  DominatorTree &getDominatorTree() const { return *DT; }
+  AAResults *getAliasAnalysis() const { return VN.getAliasAnalysis(); }
+  MemoryDependenceResults &getMemDep() const { return *MD; }
+
+  using LoadDepVect = SmallVector<NonLocalDepResult, 64>;
+  using AvailValInBlkVect = SmallVector<AvailableValueInBlock, 64>;
+  using UnavailBlkVect = SmallVector<BasicBlock *, 64>;
+
+  using DependencyBlockSet = DenseMap<BasicBlock *, DependencyBlockInfo>;
+
+  /// Given a select-dependency for the load (the load address is a select of
+  /// \p TrueAddr and \p FalseAddr guarded by \p Cond), determine whether a
+  /// value is available by finding dominating values for both addresses.  If
+  /// so, the load can be rematerialized as a select of those two values.
+  std::optional<AvailableValue>
+  analyzeSelectAvailability(LoadInst *Load, Value *Cond, Value *TrueAddr,
+                            Value *FalseAddr, Instruction *From);
+
+  /// Given a local dependency (Def or Clobber) determine if a value is
+  /// available for the load.
+  std::optional<AvailableValue>
+  analyzeLoadAvailability(LoadInst *Load, const ReachingMemVal &Dep,
+                          Value *Address);
+
+  /// Given a list of non-local dependencies, determine if a value is
+  /// available for the load in each specified block.  If it is, add it to
+  /// ValuesPerBlock.  If not, add it to UnavailableBlocks.
+  void analyzeLoadAvailability(LoadInst *Load,
+                               SmallVectorImpl<ReachingMemVal> &Deps,
+                               AvailValInBlkVect &ValuesPerBlock,
+                               UnavailBlkVect &UnavailableBlocks);
+
+  /// Given a critical edge from Pred to LoadBB, find a load instruction
+  /// which is identical to Load from another successor of Pred.
+  LoadInst *findLoadToHoistIntoPred(BasicBlock *Pred, BasicBlock *LoadBB,
+                                    LoadInst *Load);
+
+  /// Eliminates partially redundant \p Load, replacing it with \p
+  /// AvailableLoads (connected by Phis if needed).
+  void eliminatePartiallyRedundantLoad(
+      LoadInst *Load, AvailValInBlkVect &ValuesPerBlock,
+      MapVector<BasicBlock *, Value *> &AvailableLoads,
+      MapVector<BasicBlock *, LoadInst *> *CriticalEdgePredAndLoad);
+
+  // Helper functions for d etermining load dependencies.
+  std::optional<ReachingMemVal> scanMemoryAccessesUsers(
+      const MemoryLocation &Loc, bool IsInvariantLoad, BasicBlock *BB,
+      const SmallVectorImpl<MemoryAccess *> &ClobbersList, MemorySSA &MSSA,
+      BatchAAResults &AA, LoadInst *L = nullptr);
+
+  std::optional<ReachingMemVal>
+  accessMayModifyLocation(MemoryAccess *ClobberMA, const MemoryLocation &Loc,
+                          bool IsInvariantLoad, BasicBlock *BB, MemorySSA &MSSA,
+                          BatchAAResults &AA);
+
+  bool collectPredecessors(BasicBlock *BB, const PHITransAddr &Addr,
+                           MemoryAccess *ClobberMA, DependencyBlockSet &Blocks,
+                           SmallVectorImpl<BasicBlock *> &Worklist);
+
+  void collectClobberList(SmallVectorImpl<MemoryAccess *> &Clobbers,
+                          BasicBlock *BB, const DependencyBlockInfo &StartInfo,
+                          const DependencyBlockSet &Blocks, MemorySSA &MSSA);
+
+  bool findReachingValuesForLoad(LoadInst *Inst,
+                                 SmallVectorImpl<ReachingMemVal> &Values,
+                                 MemorySSA &MSSA, AAResults &AA);
+
+  bool performLoadPRE(LoadInst *Load, AvailValInBlkVect &ValuesPerBlock,
+                      UnavailBlkVect &UnavailableBlocks);
+
+  /// Try to replace a load which executes on each loop iteraiton with Phi
+  /// translation of load in preheader and load(s) in conditionally executed
+  /// paths.
+  bool performLoopLoadPRE(LoadInst *Load, AvailValInBlkVect &ValuesPerBlock,
+                          UnavailBlkVect &UnavailableBlocks);
+
+  // Try to eliminate redundent loades with non-local dependencies.
+  bool processNonLocalLoad(LoadInst *L);
+  bool processNonLocalLoad(LoadInst *L, SmallVectorImpl<ReachingMemVal> &Deps);
+
+  /// Add any blocks determined to be unreachable by a conditional branch with a
+  /// constant condition to the dead blocks.
+  bool processFoldableCondBr(CondBrInst *BI);
+
+  /// Propagate equalities derived from llvm.assume intrinsics.
+  bool processAssumeIntrinsic(AssumeInst *II);
+
+  /// Try to eliminate redundant loads.
+  bool processLoad(LoadInst *L);
+
+  /// Try to eliminate masked loads which have loaded from
+  /// masked stores with the same mask.
+  bool processMaskedLoad(IntrinsicInst *I);
+
+  /// Propagate value of a condition to blocks dominated by "then" and "else"
+  /// edges, as well as certains derived equalities.
+  bool
+  propagateEquality(Value *LHS, Value *RHS,
+                    const std::variant<BasicBlockEdge, Instruction *> &Root);
+
+  // Pass iteration helper functions.
+  bool processInstruction(Instruction *I);
+  bool processBlock(BasicBlock *BB);
+  bool iterateOnFunction(Function &F);
+
+  // Scalar PRE helper functions
+  bool performScalarPREInsertion(Instruction *Instr, BasicBlock *Pred,
+                                 BasicBlock *Curr, unsigned int ValNo);
+  bool performScalarPRE(Instruction *I);
+  bool performPRE(Function &F);
+
+  // Other helper routines.
+
+  Value *findLeader(const BasicBlock *BB, uint32_t Num);
+
+  /// Return whether all the values related with the same \p num are
+  /// defined in \p BB.
+  bool areAllValsInBB(uint32_t Num, const BasicBlock *BB) {
+    return all_of(
+        LeaderTable.getLeaders(Num),
+        [=](const GVNLeaderMap::LeaderTableEntry &L) { return L.BB == BB; });
   }
-};
 
-struct GVNPass::DependencyBlockInfo {
-  DependencyBlockInfo() = delete;
-  DependencyBlockInfo(const PHITransAddr &Addr, MemoryAccess *ClobberMA)
-      : Addr(Addr), InitialClobberMA(ClobberMA), ClobberMA(ClobberMA),
-        ForceUnknown(false), Visited(false) {}
-  PHITransAddr Addr;
-  MemoryAccess *InitialClobberMA;
-  MemoryAccess *ClobberMA;
-  std::optional<ReachingMemVal> MemVal;
-  bool ForceUnknown : 1;
-  bool Visited : 1;
-};
+  void cleanupGlobalSets();
 
-//===----------------------------------------------------------------------===//
-//                                GVN Pass
-//===----------------------------------------------------------------------===//
+  void removeInstruction(Instruction *I);
 
-PreservedAnalyses GVNPass::run(Function &F, FunctionAnalysisManager &AM) {
-  // FIXME: The order of evaluation of these 'getResult' calls is very
-  // significant! Re-ordering these variables will cause GVN when run alone to
-  // be less effective! We should fix memdep and basic-aa to not exhibit this
-  // behavior, but until then don't change the order here.
-  auto &AC = AM.getResult<AssumptionAnalysis>(F);
-  auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
-  auto &TLI = AM.getResult<TargetLibraryAnalysis>(F);
-  auto &AA = AM.getResult<AAManager>(F);
-  auto *MemDep =
-      isMemDepEnabled() ? &AM.getResult<MemoryDependenceAnalysis>(F) : nullptr;
-  auto &LI = AM.getResult<LoopAnalysis>(F);
-  auto *MSSA = AM.getCachedResult<MemorySSAAnalysis>(F);
-  if (isMemorySSAEnabled() && !MSSA) {
-    assert(!MemDep &&
-           "On-demand computation of MemSSA implies that MemDep is disabled!");
-    MSSA = &AM.getResult<MemorySSAAnalysis>(F);
-  }
-  auto &ORE = AM.getResult<OptimizationRemarkEmitterAnalysis>(F);
-  bool Changed = runImpl(F, AC, DT, TLI, AA, MemDep, LI, &ORE,
-                         MSSA ? &MSSA->getMSSA() : nullptr);
-  if (!Changed)
-    return PreservedAnalyses::all();
-  PreservedAnalyses PA;
-  PA.preserve<DominatorTreeAnalysis>();
-  PA.preserve<TargetLibraryAnalysis>();
-  if (MSSA)
-    PA.preserve<MemorySSAAnalysis>();
-  PA.preserve<LoopAnalysis>();
-  return PA;
-}
+  /// This removes the specified instruction from
+  /// our various maps and marks it for deletion.
+  void salvageAndRemoveInstruction(Instruction *I);
 
-void GVNPass::printPipeline(
-    raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {
-  static_cast<PassInfoMixin<GVNPass> *>(this)->printPipeline(
-      OS, MapClassName2PassName);
+  void verifyRemoved(const Instruction *I) const;
 
-  OS << '<';
-  if (Options.AllowScalarPRE != std::nullopt)
-    OS << (*Options.AllowScalarPRE ? "" : "no-") << "scalar-pre;";
-  if (Options.AllowLoadPRE != std::nullopt)
-    OS << (*Options.AllowLoadPRE ? "" : "no-") << "load-pre;";
-  if (Options.AllowLoadPRESplitBackedge != std::nullopt)
-    OS << (*Options.AllowLoadPRESplitBackedge ? "" : "no-")
-       << "split-backedge-load-pre;";
-  if (Options.AllowMemDep != std::nullopt)
-    OS << (*Options.AllowMemDep ? "" : "no-") << "memdep;";
-  if (Options.AllowMemorySSA != std::nullopt)
-    OS << (*Options.AllowMemorySSA ? "" : "no-") << "memoryssa";
-  OS << '>';
-}
+  bool splitCriticalEdges();
+  BasicBlock *splitCriticalEdges(BasicBlock *Pred, BasicBlock *Succ);
+
+  void addDeadBlock(BasicBlock *BB);
+  void assignValNumForDeadCode();
+  void assignBlockRPONumber(Function &F);
+};
 
-bool GVNPass::isScalarPREEnabled() const {
+bool GVNPassImpl::isScalarPREEnabled() const {
   return Options.AllowScalarPRE.value_or(GVNEnableScalarPRE);
 }
 
-bool GVNPass::isLoadPREEnabled() const {
+bool GVNPassImpl::isLoadPREEnabled() const {
   return Options.AllowLoadPRE.value_or(GVNEnableLoadPRE);
 }
 
-bool GVNPass::isLoadInLoopPREEnabled() const {
+bool GVNPassImpl::isLoadInLoopPREEnabled() const {
   return Options.AllowLoadInLoopPRE.value_or(GVNEnableLoadInLoopPRE);
 }
 
-bool GVNPass::isLoadPRESplitBackedgeEnabled() const {
+bool GVNPassImpl::isLoadPRESplitBackedgeEnabled() const {
   return Options.AllowLoadPRESplitBackedge.value_or(
       GVNEnableSplitBackedgeInLoadPRE);
 }
 
-bool GVNPass::isMemDepEnabled() const {
+bool GVNPassImpl::isMemDepEnabled() const {
   return Options.AllowMemDep.value_or(GVNEnableMemDep);
 }
 
-bool GVNPass::isMemorySSAEnabled() const {
+bool GVNPassImpl::isMemorySSAEnabled() const {
   return Options.AllowMemorySSA.value_or(GVNEnableMemorySSA);
 }
 
-enum class AvailabilityState : char {
-  /// We know the block *is not* fully available. This is a fixpoint.
-  Unavailable = 0,
-  /// We know the block *is* fully available. This is a fixpoint.
-  Available = 1,
-  /// We do not know whether the block is fully available or not,
-  /// but we are currently speculating that it will be.
-  /// If it would have turned out that the block was, in fact, not fully
-  /// available, this would have been cleaned up into an Unavailable.
-  SpeculativelyAvailable = 2,
-};
-
 /// Return true if we can prove that the value
 /// we're analyzing is fully available in the specified block.  As we go, keep
 /// track of which blocks we know are fully alive in FullyAvailableBlocks.  This
@@ -1534,8 +1679,9 @@ static Value *findDominatingValue(const MemoryLocation &Loc, Type *LoadTy,
 }
 
 std::optional<AvailableValue>
-GVNPass::analyzeSelectAvailability(LoadInst *Load, Value *Cond, Value *TrueAddr,
-                                   Value *FalseAddr, Instruction *From) {
+GVNPassImpl::analyzeSelectAvailability(LoadInst *Load, Value *Cond,
+                                       Value *TrueAddr, Value *FalseAddr,
+                                       Instruction *From) {
   assert(TrueAddr->getType() == Load->getPointerOperandType() &&
          "Invalid address type of true side of select dependency");
   assert(FalseAddr->getType() == Load->getPointerOperandType() &&
@@ -1556,8 +1702,8 @@ GVNPass::analyzeSelectAvailability(LoadInst *Load, Value *Cond, Value *TrueAddr,
 }
 
 std::optional<AvailableValue>
-GVNPass::analyzeLoadAvailability(LoadInst *Load, const ReachingMemVal &Dep,
-                                 Value *Address) {
+GVNPassImpl::analyzeLoadAvailability(LoadInst *Load, const ReachingMemVal &Dep,
+                                     Value *Address) {
   assert(Load->isUnordered() && "rules below are incorrect for ordered access");
   assert((Dep.Kind == DepKind::Def || Dep.Kind == DepKind::Clobber) &&
          "expected a local dependence");
@@ -1698,10 +1844,10 @@ GVNPass::analyzeLoadAvailability(LoadInst *Load, const ReachingMemVal &Dep,
   return std::nullopt;
 }
 
-void GVNPass::analyzeLoadAvailability(LoadInst *Load,
-                                      SmallVectorImpl<ReachingMemVal> &Deps,
-                                      AvailValInBlkVect &ValuesPerBlock,
-                                      UnavailBlkVect &UnavailableBlocks) {
+void GVNPassImpl::analyzeLoadAvailability(LoadInst *Load,
+                                          SmallVectorImpl<ReachingMemVal> &Deps,
+                                          AvailValInBlkVect &ValuesPerBlock,
+                                          UnavailBlkVect &UnavailableBlocks) {
   // Filter out useless results (non-locals, etc).  Keep track of the blocks
   // where we have a value available in repl, also keep track of whether we see
   // dependencies that produce an unknown value for the load (such as a call
@@ -1775,8 +1921,9 @@ void GVNPass::analyzeLoadAvailability(LoadInst *Load,
 ///      v2 = load %addr
 ///      ...
 ///
-LoadInst *GVNPass::findLoadToHoistIntoPred(BasicBlock *Pred, BasicBlock *LoadBB,
-                                           LoadInst *Load) {
+LoadInst *GVNPassImpl::findLoadToHoistIntoPred(BasicBlock *Pred,
+                                               BasicBlock *LoadBB,
+                                               LoadInst *Load) {
   // For simplicity we handle a Pred has 2 successors only.
   auto *Term = Pred->getTerminator();
   if (Term->getNumSuccessors() != 2 || Term->isSpecialTerminator())
@@ -1825,7 +1972,7 @@ LoadInst *GVNPass::findLoadToHoistIntoPred(BasicBlock *Pred, BasicBlock *LoadBB,
   return nullptr;
 }
 
-void GVNPass::eliminatePartiallyRedundantLoad(
+void GVNPassImpl::eliminatePartiallyRedundantLoad(
     LoadInst *Load, AvailValInBlkVect &ValuesPerBlock,
     MapVector<BasicBlock *, Value *> &AvailableLoads,
     MapVector<BasicBlock *, LoadInst *> *CriticalEdgePredAndLoad) {
@@ -2010,7 +2157,7 @@ maybeLoadStoreLocation(Instruction *I, bool AllowStores,
 /// Scan the users of each MemoryAccess in `ClobbersList` that belong to `BB`,
 /// looking for memory reads whose location aliases `Loc` and dominates our
 /// load.
-std::optional<GVNPass::ReachingMemVal> GVNPass::scanMemoryAccessesUsers(
+std::optional<ReachingMemVal> GVNPassImpl::scanMemoryAccessesUsers(
     const MemoryLocation &Loc, bool IsInvariantLoad, BasicBlock *BB,
     const SmallVectorImpl<MemoryAccess *> &ClobbersList, MemorySSA &MSSA,
     BatchAAResults &AA, LoadInst *L) {
@@ -2084,7 +2231,7 @@ std::optional<GVNPass::ReachingMemVal> GVNPass::scanMemoryAccessesUsers(
 
 /// Check if a given MemoryAccess (usually a MemoryDef) actually modifies a
 /// given location. Returns a ReachingMemVal describing the dependency.
-std::optional<GVNPass::ReachingMemVal> GVNPass::accessMayModifyLocation(
+std::optional<ReachingMemVal> GVNPassImpl::accessMayModifyLocation(
     MemoryAccess *ClobberMA, const MemoryLocation &Loc, bool IsInvariantLoad,
     BasicBlock *BB, MemorySSA &MSSA, BatchAAResults &AA) {
   assert(ClobberMA->getBlock() == BB);
@@ -2185,10 +2332,10 @@ std::optional<GVNPass::ReachingMemVal> GVNPass::accessMayModifyLocation(
 /// Collect the predecessors of block, while doing phi-translation of the memory
 /// address and the memory clobber. Return false if the block should be marked
 /// as clobbering the memory location in an unknown way.
-bool GVNPass::collectPredecessors(BasicBlock *BB, const PHITransAddr &Addr,
-                                  MemoryAccess *ClobberMA,
-                                  DependencyBlockSet &Blocks,
-                                  SmallVectorImpl<BasicBlock *> &Worklist) {
+bool GVNPassImpl::collectPredecessors(BasicBlock *BB, const PHITransAddr &Addr,
+                                      MemoryAccess *ClobberMA,
+                                      DependencyBlockSet &Blocks,
+                                      SmallVectorImpl<BasicBlock *> &Worklist) {
   if (Addr.needsPHITranslationFromBlock(BB) &&
       !Addr.isPotentiallyPHITranslatable())
     return false;
@@ -2243,11 +2390,11 @@ bool GVNPass::collectPredecessors(BasicBlock *BB, const PHITransAddr &Addr,
 /// walk the use-def chain to the final clobber. If the chain extends beyond
 /// `BB`, continue into that block but only if it is in the previously collected
 /// set.
-void GVNPass::collectClobberList(SmallVectorImpl<MemoryAccess *> &Clobbers,
-                                 BasicBlock *BB,
-                                 const DependencyBlockInfo &StartInfo,
-                                 const DependencyBlockSet &Blocks,
-                                 MemorySSA &MSSA) {
+void GVNPassImpl::collectClobberList(SmallVectorImpl<MemoryAccess *> &Clobbers,
+                                     BasicBlock *BB,
+                                     const DependencyBlockInfo &StartInfo,
+                                     const DependencyBlockSet &Blocks,
+                                     MemorySSA &MSSA) {
   MemoryAccess *MA = StartInfo.InitialClobberMA;
   MemoryAccess *LastMA = StartInfo.ClobberMA;
 
@@ -2293,9 +2440,9 @@ void GVNPass::collectClobberList(SmallVectorImpl<MemoryAccess *> &Clobbers,
 /// * Other: we know which block defines the memory location in some way, but
 /// could not identify a precise instruction (e.g., memory already live at
 /// function entry).
-bool GVNPass::findReachingValuesForLoad(LoadInst *L,
-                                        SmallVectorImpl<ReachingMemVal> &Values,
-                                        MemorySSA &MSSA, AAResults &AAR) {
+bool GVNPassImpl::findReachingValuesForLoad(
+    LoadInst *L, SmallVectorImpl<ReachingMemVal> &Values, MemorySSA &MSSA,
+    AAResults &AAR) {
   EarliestEscapeAnalysis EA(*DT, LI);
   BatchAAResults AA(AAR, &EA);
   BasicBlock *StartBlock = L->getParent();
@@ -2470,8 +2617,9 @@ bool GVNPass::findReachingValuesForLoad(LoadInst *L,
   return true;
 }
 
-bool GVNPass::performLoadPRE(LoadInst *Load, AvailValInBlkVect &ValuesPerBlock,
-                             UnavailBlkVect &UnavailableBlocks) {
+bool GVNPassImpl::performLoadPRE(LoadInst *Load,
+                                 AvailValInBlkVect &ValuesPerBlock,
+                                 UnavailBlkVect &UnavailableBlocks) {
   // Okay, we have *some* definitions of the value.  This means that the value
   // is available in some of our (transitive) predecessors.  Lets think about
   // doing PRE of this load.  This will involve inserting a new load into the
@@ -2723,9 +2871,9 @@ bool GVNPass::performLoadPRE(LoadInst *Load, AvailValInBlkVect &ValuesPerBlock,
   return true;
 }
 
-bool GVNPass::performLoopLoadPRE(LoadInst *Load,
-                                 AvailValInBlkVect &ValuesPerBlock,
-                                 UnavailBlkVect &UnavailableBlocks) {
+bool GVNPassImpl::performLoopLoadPRE(LoadInst *Load,
+                                     AvailValInBlkVect &ValuesPerBlock,
+                                     UnavailBlkVect &UnavailableBlocks) {
   const Loop *L = LI->getLoopFor(Load->getParent());
   // TODO: Generalize to other loop blocks that dominate the latch.
   if (!L || L->getHeader() != Load->getParent())
@@ -2802,7 +2950,7 @@ bool GVNPass::performLoopLoadPRE(LoadInst *Load,
 
 /// Attempt to eliminate a load whose dependencies are
 /// non-local by performing PHI construction.
-bool GVNPass::processNonLocalLoad(LoadInst *Load) {
+bool GVNPassImpl::processNonLocalLoad(LoadInst *Load) {
   // Non-local speculations are not allowed under asan.
   if (Load->getFunction()->hasFnAttribute(Attribute::SanitizeAddress) ||
       Load->getFunction()->hasFnAttribute(Attribute::SanitizeHWAddress))
@@ -2845,8 +2993,8 @@ bool GVNPass::processNonLocalLoad(LoadInst *Load) {
   return processNonLocalLoad(Load, MemVals);
 }
 
-bool GVNPass::processNonLocalLoad(LoadInst *Load,
-                                  SmallVectorImpl<ReachingMemVal> &Deps) {
+bool GVNPassImpl::processNonLocalLoad(LoadInst *Load,
+                                      SmallVectorImpl<ReachingMemVal> &Deps) {
   // If we had a phi translation failure, we'll have a single entry which is a
   // clobber in the current block.  Reject this early.
   if (Deps.size() == 1 && Deps[0].Kind == DepKind::Other) {
@@ -2922,7 +3070,7 @@ bool GVNPass::processNonLocalLoad(LoadInst *Load,
 
 /// Attempt to eliminate a load, first by eliminating it
 /// locally, and then attempting non-local elimination if that fails.
-bool GVNPass::processLoad(LoadInst *L) {
+bool GVNPassImpl::processLoad(LoadInst *L) {
   if (!MD && !isMemorySSAEnabled())
     return false;
 
@@ -2996,7 +3144,7 @@ bool GVNPass::processLoad(LoadInst *L) {
 
 // Attempt to process masked loads which have loaded from
 // masked stores with the same mask
-bool GVNPass::processMaskedLoad(IntrinsicInst *I) {
+bool GVNPassImpl::processMaskedLoad(IntrinsicInst *I) {
   if (!MD)
     return false;
   MemDepResult Dep = MD->getDependency(I);
@@ -3036,7 +3184,7 @@ bool GVNPass::processMaskedLoad(IntrinsicInst *I) {
 //     dead blocks with "UndefVal" in an hope these PHIs will optimized away.
 //
 // Return true iff *NEW* dead code are found.
-bool GVNPass::processFoldableCondBr(CondBrInst *BI) {
+bool GVNPassImpl::processFoldableCondBr(CondBrInst *BI) {
   // If a branch has two identical successors, we cannot declare either dead.
   if (BI->getSuccessor(0) == BI->getSuccessor(1))
     return false;
@@ -3057,7 +3205,7 @@ bool GVNPass::processFoldableCondBr(CondBrInst *BI) {
   return true;
 }
 
-bool GVNPass::processAssumeIntrinsic(AssumeInst *IntrinsicI) {
+bool GVNPassImpl::processAssumeIntrinsic(AssumeInst *IntrinsicI) {
   Value *V = IntrinsicI->getArgOperand(0);
 
   if (ConstantInt *Cond = dyn_cast<ConstantInt>(V)) {
@@ -3139,7 +3287,7 @@ static bool isOnlyReachableViaThisEdge(const BasicBlockEdge &E,
 /// 'RHS' everywhere in the scope.  Returns whether a change was made.
 /// The Root may either be a basic block edge (for conditions) or an
 /// instruction (for assumes).
-bool GVNPass::propagateEquality(
+bool GVNPassImpl::propagateEquality(
     Value *LHS, Value *RHS,
     const std::variant<BasicBlockEdge, Instruction *> &Root) {
   SmallVector<std::pair<Value*, Value*>, 4> Worklist;
@@ -3348,7 +3496,7 @@ static void patchAndReplaceAllUsesWith(Instruction *I, Value *Repl) {
 
 /// When calculating availability, handle an instruction
 /// by inserting it into the appropriate sets.
-bool GVNPass::processInstruction(Instruction *I) {
+bool GVNPassImpl::processInstruction(Instruction *I) {
   // If the instruction can be easily simplified then do so now in preference
   // to value numbering it.  Value numbering often exposes redundancies, for
   // example if it determines that %y is equal to %x then the instruction
@@ -3503,7 +3651,7 @@ bool GVNPass::processInstruction(Instruction *I) {
   return true;
 }
 
-bool GVNPass::processBlock(BasicBlock *BB) {
+bool GVNPassImpl::processBlock(BasicBlock *BB) {
   if (DeadBlocks.count(BB))
     return false;
 
@@ -3524,7 +3672,7 @@ bool GVNPass::processBlock(BasicBlock *BB) {
 }
 
 /// Executes one iteration of GVN.
-bool GVNPass::iterateOnFunction(Function &F) {
+bool GVNPassImpl::iterateOnFunction(Function &F) {
   cleanupGlobalSets();
 
   // Top-down walk of the dominator tree.
@@ -3541,8 +3689,9 @@ bool GVNPass::iterateOnFunction(Function &F) {
 }
 
 // Instantiate an expression in a predecessor that lacked it.
-bool GVNPass::performScalarPREInsertion(Instruction *Instr, BasicBlock *Pred,
-                                        BasicBlock *Curr, unsigned int ValNo) {
+bool GVNPassImpl::performScalarPREInsertion(Instruction *Instr,
+                                            BasicBlock *Pred, BasicBlock *Curr,
+                                            unsigned int ValNo) {
   // Because we are going top-down through the block, all value numbers
   // will be available in the predecessor by the time we need them.  Any
   // that weren't originally present will have been instantiated earlier
@@ -3589,7 +3738,7 @@ bool GVNPass::performScalarPREInsertion(Instruction *Instr, BasicBlock *Pred,
   return true;
 }
 
-bool GVNPass::performScalarPRE(Instruction *CurInst) {
+bool GVNPassImpl::performScalarPRE(Instruction *CurInst) {
   if (isa<AllocaInst>(CurInst) || CurInst->isTerminator() ||
       isa<PHINode>(CurInst) || CurInst->getType()->isVoidTy() ||
       CurInst->mayReadFromMemory() || CurInst->mayHaveSideEffects() ||
@@ -3751,7 +3900,7 @@ bool GVNPass::performScalarPRE(Instruction *CurInst) {
 
 /// Perform a purely local form of PRE that looks for diamond
 /// control flow patterns and attempts to perform simple PRE at the join point.
-bool GVNPass::performPRE(Function &F) {
+bool GVNPassImpl::performPRE(Function &F) {
   bool Changed = false;
   for (BasicBlock *CurrentBlock : depth_first(&F.getEntryBlock())) {
     // Nothing to PRE in the entry block.
@@ -3776,8 +3925,26 @@ bool GVNPass::performPRE(Function &F) {
   return Changed;
 }
 
+void GVNPassImpl::printPipeline(
+    raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {
+
+  OS << '<';
+  if (Options.AllowScalarPRE != std::nullopt)
+    OS << (*Options.AllowScalarPRE ? "" : "no-") << "scalar-pre;";
+  if (Options.AllowLoadPRE != std::nullopt)
+    OS << (*Options.AllowLoadPRE ? "" : "no-") << "load-pre;";
+  if (Options.AllowLoadPRESplitBackedge != std::nullopt)
+    OS << (*Options.AllowLoadPRESplitBackedge ? "" : "no-")
+       << "split-backedge-load-pre;";
+  if (Options.AllowMemDep != std::nullopt)
+    OS << (*Options.AllowMemDep ? "" : "no-") << "memdep;";
+  if (Options.AllowMemorySSA != std::nullopt)
+    OS << (*Options.AllowMemorySSA ? "" : "no-") << "memoryssa";
+  OS << '>';
+}
+
 /// runOnFunction - This is the main transformation entry point for a function.
-bool GVNPass::runImpl(Function &F, AssumptionCache &RunAC, DominatorTree &RunDT,
+bool GVNPassImpl::run(Function &F, AssumptionCache &RunAC, DominatorTree &RunDT,
                       const TargetLibraryInfo &RunTLI, AAResults &RunAA,
                       MemoryDependenceResults *RunMD, LoopInfo &LI,
                       OptimizationRemarkEmitter *RunORE, MemorySSA *MSSA) {
@@ -3857,7 +4024,7 @@ bool GVNPass::runImpl(Function &F, AssumptionCache &RunAC, DominatorTree &RunDT,
 // and then scan the list to find one whose block dominates the block in
 // question.  This is fast because dominator tree queries consist of only
 // a few comparisons of DFS numbers.
-Value *GVNPass::findLeader(const BasicBlock *BB, uint32_t Num) {
+Value *GVNPassImpl::findLeader(const BasicBlock *BB, uint32_t Num) {
   auto Leaders = LeaderTable.getLeaders(Num);
   if (Leaders.empty())
     return nullptr;
@@ -3874,7 +4041,7 @@ Value *GVNPass::findLeader(const BasicBlock *BB, uint32_t Num) {
   return Val;
 }
 
-void GVNPass::cleanupGlobalSets() {
+void GVNPassImpl::cleanupGlobalSets() {
   VN.clear();
   LeaderTable.clear();
   BlockRPONumber.clear();
@@ -3882,7 +4049,7 @@ void GVNPass::cleanupGlobalSets() {
   InvalidBlockRPONumbers = true;
 }
 
-void GVNPass::removeInstruction(Instruction *I) {
+void GVNPassImpl::removeInstruction(Instruction *I) {
   VN.erase(I);
   if (MD) MD->removeInstruction(I);
   if (MSSAU)
@@ -3895,7 +4062,7 @@ void GVNPass::removeInstruction(Instruction *I) {
   ++NumGVNInstr;
 }
 
-void GVNPass::salvageAndRemoveInstruction(Instruction *I) {
+void GVNPassImpl::salvageAndRemoveInstruction(Instruction *I) {
   salvageKnowledge(I, AC);
   salvageDebugInfo(*I);
   removeInstruction(I);
@@ -3903,13 +4070,13 @@ void GVNPass::salvageAndRemoveInstruction(Instruction *I) {
 
 /// Verify that the specified instruction does not occur in our
 /// internal data structures.
-void GVNPass::verifyRemoved(const Instruction *Inst) const {
+void GVNPassImpl::verifyRemoved(const Instruction *Inst) const {
   VN.verifyRemoved(Inst);
 }
 
 /// Split critical edges found during the previous
 /// iteration that may enable further optimization.
-bool GVNPass::splitCriticalEdges() {
+bool GVNPassImpl::splitCriticalEdges() {
   if (ToSplit.empty())
     return false;
 
@@ -3930,7 +4097,8 @@ bool GVNPass::splitCriticalEdges() {
 
 /// Split the critical edge connecting the given two blocks, and return
 /// the block inserted to the critical edge.
-BasicBlock *GVNPass::splitCriticalEdges(BasicBlock *Pred, BasicBlock *Succ) {
+BasicBlock *GVNPassImpl::splitCriticalEdges(BasicBlock *Pred,
+                                            BasicBlock *Succ) {
   // GVN does not require loop-simplify, do not try to preserve it if it is not
   // possible.
   BasicBlock *BB = SplitCriticalEdge(
@@ -3948,7 +4116,7 @@ BasicBlock *GVNPass::splitCriticalEdges(BasicBlock *Pred, BasicBlock *Succ) {
 /// function is to add all these blocks to "DeadBlocks". For the dead blocks'
 /// live successors, update their phi nodes by replacing the operands
 /// corresponding to dead blocks with UndefVal.
-void GVNPass::addDeadBlock(BasicBlock *BB) {
+void GVNPassImpl::addDeadBlock(BasicBlock *BB) {
   SmallVector<BasicBlock *, 4> NewDead;
   SmallSetVector<BasicBlock *, 4> DF;
 
@@ -4027,7 +4195,7 @@ void GVNPass::addDeadBlock(BasicBlock *BB) {
 // associated val-num. As it normally has far more live instructions than dead
 // instructions, it makes more sense just to "fabricate" a val-number for the
 // dead code than checking if instruction involved is dead or not.
-void GVNPass::assignValNumForDeadCode() {
+void GVNPassImpl::assignValNumForDeadCode() {
   for (BasicBlock *BB : DeadBlocks) {
     for (Instruction &Inst : *BB) {
       unsigned ValNum = VN.lookupOrAdd(&Inst);
@@ -4036,7 +4204,7 @@ void GVNPass::assignValNumForDeadCode() {
   }
 }
 
-void GVNPass::assignBlockRPONumber(Function &F) {
+void GVNPassImpl::assignBlockRPONumber(Function &F) {
   BlockRPONumber.clear();
   uint32_t NextBlockNumber = 1;
   ReversePostOrderTraversal<Function *> RPOT(&F);
@@ -4045,6 +4213,56 @@ void GVNPass::assignBlockRPONumber(Function &F) {
   InvalidBlockRPONumbers = false;
 }
 
+GVNPass::GVNPass(GVNOptions Options)
+    : Impl(std::make_unique<GVNPassImpl>(Options)) {}
+
+GVNPass::~GVNPass() = default;
+
+GVNPass::GVNPass(GVNPass &&) = default;
+
+GVNPass &GVNPass::operator=(GVNPass &&) = default;
+
+PreservedAnalyses GVNPass::run(Function &F, FunctionAnalysisManager &AM) {
+  // FIXME: The order of evaluation of these 'getResult' calls is very
+  // significant! Re-ordering these variables will cause GVN when run alone to
+  // be less effective! We should fix memdep and basic-aa to not exhibit this
+  // behavior, but until then don't change the order here.
+  auto &AC = AM.getResult<AssumptionAnalysis>(F);
+  auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
+  auto &TLI = AM.getResult<TargetLibraryAnalysis>(F);
+  auto &AA = AM.getResult<AAManager>(F);
+  auto *MemDep = Impl->isMemDepEnabled()
+                     ? &AM.getResult<MemoryDependenceAnalysis>(F)
+                     : nullptr;
+  auto &LI = AM.getResult<LoopAnalysis>(F);
+  auto *MSSA = AM.getCachedResult<MemorySSAAnalysis>(F);
+  if (Impl->isMemorySSAEnabled() && !MSSA) {
+    assert(!MemDep &&
+           "On-demand computation of MemSSA implies that MemDep is disabled!");
+    MSSA = &AM.getResult<MemorySSAAnalysis>(F);
+  }
+  auto &ORE = AM.getResult<OptimizationRemarkEmitterAnalysis>(F);
+  bool Changed = Impl->run(F, AC, DT, TLI, AA, MemDep, LI, &ORE,
+                           MSSA ? &MSSA->getMSSA() : nullptr);
+  if (!Changed)
+    return PreservedAnalyses::all();
+  PreservedAnalyses PA;
+  PA.preserve<DominatorTreeAnalysis>();
+  PA.preserve<TargetLibraryAnalysis>();
+  if (MSSA)
+    PA.preserve<MemorySSAAnalysis>();
+  PA.preserve<LoopAnalysis>();
+  return PA;
+}
+
+void GVNPass::printPipeline(
+    raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {
+  static_cast<PassInfoMixin<GVNPass> *>(this)->printPipeline(
+      OS, MapClassName2PassName);
+
+  Impl->printPipeline(OS, MapClassName2PassName);
+}
+
 class llvm::GVNLegacyPass : public FunctionPass {
 public:
   static char ID; // Pass identification, replacement for typeid.
@@ -4067,7 +4285,7 @@ class llvm::GVNLegacyPass : public FunctionPass {
     if (Impl.isMemorySSAEnabled() && !MSSAWP)
       MSSAWP = &getAnalysis<MemorySSAWrapperPass>();
 
-    return Impl.runImpl(
+    return Impl.run(
         F, getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F),
         getAnalysis<DominatorTreeWrapperPass>().getDomTree(),
         getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F),
@@ -4099,7 +4317,7 @@ class llvm::GVNLegacyPass : public FunctionPass {
   }
 
 private:
-  GVNPass Impl;
+  GVNPassImpl Impl;
 };
 
 char GVNLegacyPass::ID = 0;



More information about the llvm-branch-commits mailing list