[llvm] [GVN] Move `GVNPass` and `GVNLeaderMap` out of `GVN.h` and into 'GVN.cpp` (NFC) (PR #226234)
Momchil Velikov via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 30 02:58:39 PDT 2026
https://github.com/momchil-velikov updated https://github.com/llvm/llvm-project/pull/226234
>From b9a4527b652a3abb2ec7fe1aeac76901e784114e Mon Sep 17 00:00:00 2001
From: Momchil Velikov <momchil.velikov at arm.com>
Date: Thu, 24 Sep 2026 13:41:53 +0100
Subject: [PATCH 1/3] [GVN] Move `GVNPass` and `GVNLeaderMap` out of `GVN.h`
and into 'GVN.cpp` (NFC)
Rename `GVNPass` to `GVNPassImpl`. Leave in the header only
the class for interfacing with the pass manager.
---
llvm/include/llvm/Transforms/Scalar/GVN.h | 335 ---------------
llvm/lib/Transforms/Scalar/GVN.cpp | 487 ++++++++++++++++++----
2 files changed, 402 insertions(+), 420 deletions(-)
diff --git a/llvm/include/llvm/Transforms/Scalar/GVN.h b/llvm/include/llvm/Transforms/Scalar/GVN.h
index c59d830bb7016..5e8eb941e4670 100644
--- a/llvm/include/llvm/Transforms/Scalar/GVN.h
+++ b/llvm/include/llvm/Transforms/Scalar/GVN.h
@@ -15,54 +15,12 @@
#ifndef LLVM_TRANSFORMS_SCALAR_GVN_H
#define LLVM_TRANSFORMS_SCALAR_GVN_H
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/MapVector.h"
-#include "llvm/ADT/SetVector.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/Analysis/PHITransAddr.h"
-#include "llvm/IR/Dominators.h"
-#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/PassManager.h"
-#include "llvm/IR/ValueHandle.h"
-#include "llvm/Support/Allocator.h"
-#include "llvm/Support/Compiler.h"
-#include "llvm/Transforms/Scalar/GVNValueTable.h"
-#include <cstdint>
#include <optional>
-#include <utility>
-#include <variant>
-#include <vector>
namespace llvm {
-class AAResults;
-class AssumeInst;
-class AssumptionCache;
-class BasicBlock;
-class BatchAAResults;
-class CallInst;
-class CondBrInst;
-class EarliestEscapeAnalysis;
-class ExtractValueInst;
-class Function;
class FunctionPass;
-class GVNLegacyPass;
-class GetElementPtrInst;
-class ImplicitControlFlowTracking;
-class LoadInst;
-class LoopInfo;
-class MemDepResult;
-class MemoryAccess;
-class MemoryDependenceResults;
-class MemoryLocation;
-class MemorySSA;
-class MemorySSAUpdater;
-class NonLocalDepResult;
-class OptimizationRemarkEmitter;
-class PHINode;
-class TargetLibraryInfo;
-class Value;
-class IntrinsicInst;
/// A set of parameters to control various transforms performed by GVN pass.
// Each of the optional boolean parameters can be set to:
@@ -117,96 +75,10 @@ struct GVNOptions {
}
};
-/// A mapping from value numbers to lists of Value*'s that
-/// have that value number. Use getLeaders to query it.
-class GVNLeaderMap {
-public:
- struct LeaderTableEntry {
- // Use AssertingVH here to catch dangling Value*'s in the leader table.
- // Will crash if the value gets deleted before the AssertingVH is
- // destroyed.
- AssertingVH<Value> Val;
- const BasicBlock *BB;
- LeaderTableEntry(Value *V, const BasicBlock *BB) : Val(V), BB(BB) {}
- };
-
-private:
- struct LeaderListNode {
- LeaderTableEntry Entry;
- LeaderListNode *Next;
- LeaderListNode(Value *V, const BasicBlock *BB, LeaderListNode *Next)
- : Entry(V, BB), Next(Next) {}
- };
- DenseMap<uint32_t, LeaderListNode> NumToLeaders;
- BumpPtrAllocator TableAllocator;
-
-public:
- class leader_iterator {
- const LeaderListNode *Current;
-
- public:
- using iterator_category = std::forward_iterator_tag;
- using value_type = const LeaderTableEntry;
- using difference_type = std::ptrdiff_t;
- using pointer = value_type *;
- using reference = value_type &;
-
- leader_iterator(const LeaderListNode *C) : Current(C) {}
- leader_iterator &operator++() {
- assert(Current && "Dereferenced end of leader list!");
- Current = Current->Next;
- return *this;
- }
- bool operator==(const leader_iterator &Other) const {
- return Current == Other.Current;
- }
- bool operator!=(const leader_iterator &Other) const {
- return Current != Other.Current;
- }
- reference operator*() const { return Current->Entry; }
- };
-
- iterator_range<leader_iterator> getLeaders(uint32_t N) {
- auto I = NumToLeaders.find(N);
- if (I == NumToLeaders.end()) {
- return iterator_range(leader_iterator(nullptr), leader_iterator(nullptr));
- }
-
- return iterator_range(leader_iterator(&I->second),
- leader_iterator(nullptr));
- }
-
- LLVM_ABI void insert(uint32_t N, Value *V, const BasicBlock *BB);
- LLVM_ABI void erase(uint32_t N, Instruction *I, const BasicBlock *BB);
- void clear() {
- // Manually destroy non-head nodes (in BumpPtrAllocator) to properly
- // clean up AssertingVH handles before Reset(). Head nodes are destroyed
- // by NumToLeaders.clear() below.
- for (auto &[_, HeadNode] : NumToLeaders) {
- LeaderListNode *N = HeadNode.Next;
- while (N) {
- auto *Next = N->Next;
- N->~LeaderListNode();
- N = Next;
- }
- }
- NumToLeaders.clear();
- TableAllocator.Reset();
- }
-};
-
-/// The core GVN pass object.
-///
-/// FIXME: We should have a good summary of the GVN algorithm implemented by
-/// this particular pass here.
class GVNPass : public OptionalPassInfoMixin<GVNPass> {
GVNOptions Options;
public:
- struct Expression;
- struct AvailableValue;
- struct AvailableValueInBlock;
-
GVNPass(GVNOptions Options = {}) : Options(Options) {}
/// Run the pass over the function.
@@ -215,213 +87,6 @@ class GVNPass : public OptionalPassInfoMixin<GVNPass> {
LLVM_ABI void
printPipeline(raw_ostream &OS,
function_ref<StringRef(StringRef)> MapClassName2PassName);
-
- /// This removes the specified instruction from
- /// our various maps and marks it for deletion.
- LLVM_ABI void salvageAndRemoveInstruction(Instruction *I);
-
- DominatorTree &getDominatorTree() const { return *DT; }
- AAResults *getAliasAnalysis() const { return VN.getAliasAnalysis(); }
- MemoryDependenceResults &getMemDep() const { return *MD; }
-
- LLVM_ABI bool isScalarPREEnabled() const;
- LLVM_ABI bool isLoadPREEnabled() const;
- LLVM_ABI bool isLoadInLoopPREEnabled() const;
- LLVM_ABI bool isLoadPRESplitBackedgeEnabled() const;
- LLVM_ABI bool isMemDepEnabled() const;
- LLVM_ABI bool isMemorySSAEnabled() const;
-
-private:
- friend class GVNLegacyPass;
- friend struct DenseMapInfo<Expression>;
-
- 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;
-
- using LoadDepVect = SmallVector<NonLocalDepResult, 64>;
- using AvailValInBlkVect = SmallVector<AvailableValueInBlock, 64>;
- using UnavailBlkVect = SmallVector<BasicBlock *, 64>;
-
- bool runImpl(Function &F, AssumptionCache &RunAC, DominatorTree &RunDT,
- const TargetLibraryInfo &RunTLI, AAResults &RunAA,
- MemoryDependenceResults *RunMD, LoopInfo &LI,
- OptimizationRemarkEmitter *ORE, MemorySSA *MSSA = nullptr);
-
- // List of critical edges to be split between iterations.
- SmallVector<std::pair<Instruction *, unsigned>, 4> ToSplit;
-
- 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;
- };
-
- using DependencyBlockSet = DenseMap<BasicBlock *, DependencyBlockInfo>;
-
- 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,
- Align LoadAlign, 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);
-
- // Helper functions of redundant load elimination.
- bool processLoad(LoadInst *L);
- bool processMaskedLoad(IntrinsicInst *I);
- bool processNonLocalLoad(LoadInst *L);
- bool processNonLocalLoad(LoadInst *L, SmallVectorImpl<ReachingMemVal> &Deps);
- bool processAssumeIntrinsic(AssumeInst *II);
-
- /// 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 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 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);
-
- 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);
-
- /// 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);
-
- // Other helper routines.
- bool processInstruction(Instruction *I);
- bool processBlock(BasicBlock *BB);
- bool iterateOnFunction(Function &F);
- bool performPRE(Function &F);
- bool performScalarPRE(Instruction *I);
- bool performScalarPREInsertion(Instruction *Instr, BasicBlock *Pred,
- BasicBlock *Curr, unsigned int ValNo);
- Value *findLeader(const BasicBlock *BB, uint32_t Num);
- void cleanupGlobalSets();
- void removeInstruction(Instruction *I);
- void verifyRemoved(const Instruction *I) const;
- bool splitCriticalEdges();
- BasicBlock *splitCriticalEdges(BasicBlock *Pred, BasicBlock *Succ);
- bool
- propagateEquality(Value *LHS, Value *RHS,
- const std::variant<BasicBlockEdge, Instruction *> &Root);
- bool processFoldableCondBr(CondBrInst *BI);
- 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 463520f2b32d7..154e3106a39d3 100644
--- a/llvm/lib/Transforms/Scalar/GVN.cpp
+++ b/llvm/lib/Transforms/Scalar/GVN.cpp
@@ -70,6 +70,7 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/Scalar/GVNValueTable.h"
#include "llvm/Transforms/Utils/AssumeBundleBuilder.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Local.h"
@@ -80,14 +81,12 @@
#include <cstdint>
#include <optional>
#include <utility>
+#include <variant>
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");
@@ -197,11 +196,319 @@ template <> struct llvm::DenseMapInfo<GVNValueTable::Expression> {
}
};
+/// A mapping from value numbers to lists of Value*'s that
+/// have that value number. Use getLeaders to query it.
+class llvm::GVNLeaderMap {
+public:
+ struct LeaderTableEntry {
+ // Use AssertingVH here to catch dangling Value*'s in the leader table.
+ // Will crash if the value gets deleted before the AssertingVH is
+ // destroyed.
+ AssertingVH<Value> Val;
+ const BasicBlock *BB;
+ LeaderTableEntry(Value *V, const BasicBlock *BB) : Val(V), BB(BB) {}
+ };
+
+private:
+ struct LeaderListNode {
+ LeaderTableEntry Entry;
+ LeaderListNode *Next;
+ LeaderListNode(Value *V, const BasicBlock *BB, LeaderListNode *Next)
+ : Entry(V, BB), Next(Next) {}
+ };
+ DenseMap<uint32_t, LeaderListNode> NumToLeaders;
+ BumpPtrAllocator TableAllocator;
+
+public:
+ class leader_iterator {
+ const LeaderListNode *Current;
+
+ public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = const LeaderTableEntry;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type *;
+ using reference = value_type &;
+
+ leader_iterator(const LeaderListNode *C) : Current(C) {}
+ leader_iterator &operator++() {
+ assert(Current && "Dereferenced end of leader list!");
+ Current = Current->Next;
+ return *this;
+ }
+ bool operator==(const leader_iterator &Other) const {
+ return Current == Other.Current;
+ }
+ bool operator!=(const leader_iterator &Other) const {
+ return Current != Other.Current;
+ }
+ reference operator*() const { return Current->Entry; }
+ };
+
+ iterator_range<leader_iterator> getLeaders(uint32_t N) {
+ auto I = NumToLeaders.find(N);
+ if (I == NumToLeaders.end()) {
+ return iterator_range(leader_iterator(nullptr), leader_iterator(nullptr));
+ }
+
+ return iterator_range(leader_iterator(&I->second),
+ leader_iterator(nullptr));
+ }
+
+ LLVM_ABI void insert(uint32_t N, Value *V, const BasicBlock *BB);
+ LLVM_ABI void erase(uint32_t N, Instruction *I, const BasicBlock *BB);
+ void clear() {
+ // Manually destroy non-head nodes (in BumpPtrAllocator) to properly
+ // clean up AssertingVH handles before Reset(). Head nodes are destroyed
+ // by NumToLeaders.clear() below.
+ for (auto &[_, HeadNode] : NumToLeaders) {
+ LeaderListNode *N = HeadNode.Next;
+ while (N) {
+ auto *Next = N->Next;
+ N->~LeaderListNode();
+ N = Next;
+ }
+ }
+ NumToLeaders.clear();
+ TableAllocator.Reset();
+ }
+};
+
+/// The core GVN pass object.
+///
+/// FIXME: We should have a good summary of the GVN algorithm implemented by
+/// this particular pass here.
+class GVNPassImpl {
+ llvm::GVNOptions Options;
+
+public:
+ struct Expression;
+ struct AvailableValue;
+ struct AvailableValueInBlock;
+
+ GVNPassImpl(llvm::GVNOptions Options = {}) : Options(Options) {}
+
+ /// Run the pass over the function.
+ LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+
+ LLVM_ABI void
+ printPipeline(raw_ostream &OS,
+ function_ref<StringRef(StringRef)> MapClassName2PassName);
+
+ /// This removes the specified instruction from
+ /// our various maps and marks it for deletion.
+ LLVM_ABI void salvageAndRemoveInstruction(Instruction *I);
+
+ DominatorTree &getDominatorTree() const { return *DT; }
+ AAResults *getAliasAnalysis() const { return VN.getAliasAnalysis(); }
+ MemoryDependenceResults &getMemDep() const { return *MD; }
+
+ LLVM_ABI bool isScalarPREEnabled() const;
+ LLVM_ABI bool isLoadPREEnabled() const;
+ LLVM_ABI bool isLoadInLoopPREEnabled() const;
+ LLVM_ABI bool isLoadPRESplitBackedgeEnabled() const;
+ LLVM_ABI bool isMemDepEnabled() const;
+ LLVM_ABI bool isMemorySSAEnabled() const;
+
+private:
+ friend class llvm::GVNPass;
+ friend class GVNLegacyPass;
+ friend struct DenseMapInfo<Expression>;
+
+ 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;
+
+ using LoadDepVect = SmallVector<NonLocalDepResult, 64>;
+ using AvailValInBlkVect = SmallVector<AvailableValueInBlock, 64>;
+ using UnavailBlkVect = SmallVector<BasicBlock *, 64>;
+
+ bool run(Function &F, AssumptionCache &RunAC, DominatorTree &RunDT,
+ const TargetLibraryInfo &RunTLI, AAResults &RunAA,
+ MemoryDependenceResults *RunMD, LoopInfo &LI,
+ OptimizationRemarkEmitter *ORE, MemorySSA *MSSA = nullptr);
+
+ // List of critical edges to be split between iterations.
+ SmallVector<std::pair<Instruction *, unsigned>, 4> ToSplit;
+
+ 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;
+ };
+
+ using DependencyBlockSet = DenseMap<BasicBlock *, DependencyBlockInfo>;
+
+ std::optional<GVNPassImpl::ReachingMemVal> scanMemoryAccessesUsers(
+ const MemoryLocation &Loc, bool IsInvariantLoad, BasicBlock *BB,
+ const SmallVectorImpl<MemoryAccess *> &ClobbersList, MemorySSA &MSSA,
+ BatchAAResults &AA, LoadInst *L = nullptr);
+
+ std::optional<GVNPassImpl::ReachingMemVal>
+ accessMayModifyLocation(MemoryAccess *ClobberMA, const MemoryLocation &Loc,
+ Align LoadAlign, 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);
+
+ // Helper functions of redundant load elimination.
+ bool processLoad(LoadInst *L);
+ bool processMaskedLoad(IntrinsicInst *I);
+ bool processNonLocalLoad(LoadInst *L);
+ bool processNonLocalLoad(LoadInst *L, SmallVectorImpl<ReachingMemVal> &Deps);
+ bool processAssumeIntrinsic(AssumeInst *II);
+
+ /// 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 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 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);
+
+ 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);
+
+ /// 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);
+
+ // Other helper routines.
+ bool processInstruction(Instruction *I);
+ bool processBlock(BasicBlock *BB);
+ bool iterateOnFunction(Function &F);
+ bool performPRE(Function &F);
+ bool performScalarPRE(Instruction *I);
+ bool performScalarPREInsertion(Instruction *Instr, BasicBlock *Pred,
+ BasicBlock *Curr, unsigned int ValNo);
+ Value *findLeader(const BasicBlock *BB, uint32_t Num);
+ void cleanupGlobalSets();
+ void removeInstruction(Instruction *I);
+ void verifyRemoved(const Instruction *I) const;
+ bool splitCriticalEdges();
+ BasicBlock *splitCriticalEdges(BasicBlock *Pred, BasicBlock *Succ);
+ bool
+ propagateEquality(Value *LHS, Value *RHS,
+ const std::variant<BasicBlockEdge, Instruction *> &Root);
+ bool processFoldableCondBr(CondBrInst *BI);
+ void addDeadBlock(BasicBlock *BB);
+ void assignValNumForDeadCode();
+ void assignBlockRPONumber(Function &F);
+};
+
/// 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 {
+struct GVNPassImpl::AvailableValue {
enum class ValType {
SimpleVal, // A simple offsetted value that is accessed.
LoadVal, // A value produced by a load.
@@ -297,7 +604,7 @@ struct llvm::GVNPass::AvailableValue {
/// Represents an AvailableValue which can be rematerialized at the end of
/// the associated BasicBlock.
-struct llvm::GVNPass::AvailableValueInBlock {
+struct GVNPassImpl::AvailableValueInBlock {
/// BB - The basic block in question.
BasicBlock *BB = nullptr;
@@ -858,24 +1165,24 @@ void GVNLeaderMap::erase(uint32_t N, Instruction *I, const BasicBlock *BB) {
// GVN Pass
//===----------------------------------------------------------------------===//
-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 {
// MemDep and MemorySSA are mutually exclusive. parseGVNOptions() enforces
// this for pass parameters, but the -enable-gvn-{memdep,memoryssa} cl::opt
// overrides default independently, so honor MemorySSA winning here too.
@@ -884,11 +1191,13 @@ bool GVNPass::isMemDepEnabled() const {
return Options.AllowMemDep.value_or(GVNEnableMemDep);
}
-bool GVNPass::isMemorySSAEnabled() const {
+bool GVNPassImpl::isMemorySSAEnabled() const {
return Options.AllowMemorySSA.value_or(GVNEnableMemorySSA);
}
PreservedAnalyses GVNPass::run(Function &F, FunctionAnalysisManager &AM) {
+ GVNPassImpl Impl(Options);
+
// 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
@@ -897,18 +1206,19 @@ PreservedAnalyses GVNPass::run(Function &F, FunctionAnalysisManager &AM) {
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 *MemDep = Impl.isMemDepEnabled()
+ ? &AM.getResult<MemoryDependenceAnalysis>(F)
+ : nullptr;
auto &LI = AM.getResult<LoopAnalysis>(F);
auto *MSSA = AM.getCachedResult<MemorySSAAnalysis>(F);
- if (isMemorySSAEnabled() && !MSSA) {
+ 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 = runImpl(F, AC, DT, TLI, AA, MemDep, LI, &ORE,
- MSSA ? &MSSA->getMSSA() : nullptr);
+ bool Changed = Impl.run(F, AC, DT, TLI, AA, MemDep, LI, &ORE,
+ MSSA ? &MSSA->getMSSA() : nullptr);
if (!Changed)
return PreservedAnalyses::all();
PreservedAnalyses PA;
@@ -920,6 +1230,12 @@ PreservedAnalyses GVNPass::run(Function &F, FunctionAnalysisManager &AM) {
return PA;
}
+void GVNPassImpl::salvageAndRemoveInstruction(Instruction *I) {
+ salvageKnowledge(I, AC);
+ salvageDebugInfo(*I);
+ removeInstruction(I);
+}
+
void GVNPass::printPipeline(
raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {
static_cast<PassInfoMixin<GVNPass> *>(this)->printPipeline(
@@ -940,12 +1256,6 @@ void GVNPass::printPipeline(
OS << '>';
}
-void GVNPass::salvageAndRemoveInstruction(Instruction *I) {
- salvageKnowledge(I, AC);
- salvageDebugInfo(*I);
- removeInstruction(I);
-}
-
enum class AvailabilityState : char {
/// We know the block *is not* fully available. This is a fixpoint.
Unavailable = 0,
@@ -1080,6 +1390,9 @@ static bool isValueFullyAvailableInBlock(
return !UnavailableBB;
}
+using AvailableValue = GVNPassImpl::AvailableValue;
+using AvailableValueInBlock = GVNPassImpl::AvailableValueInBlock;
+
/// If the specified OldValue exists in ValuesPerBlock, replace its value with
/// NewValue.
static void replaceValuesPerBlockEntry(
@@ -1103,12 +1416,11 @@ static void replaceValuesPerBlockEntry(
static Value *
constructSSAForLoadSet(LoadInst *Load,
SmallVectorImpl<AvailableValueInBlock> &ValuesPerBlock,
- GVNPass &GVN) {
+ DominatorTree &DT) {
// Check for the fully redundant, dominating load case. In this case, we can
// just use the dominating value directly.
if (ValuesPerBlock.size() == 1 &&
- GVN.getDominatorTree().properlyDominates(ValuesPerBlock[0].BB,
- Load->getParent())) {
+ DT.properlyDominates(ValuesPerBlock[0].BB, Load->getParent())) {
assert(!ValuesPerBlock[0].AV.isUndefValue() &&
"Dead BB dominate this block");
return ValuesPerBlock[0].MaterializeAdjustedValue(Load);
@@ -1331,8 +1643,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() &&
@@ -1353,8 +1666,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");
@@ -1495,10 +1808,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
@@ -1572,8 +1885,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())
@@ -1622,7 +1936,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) {
@@ -1694,7 +2008,7 @@ void GVNPass::eliminatePartiallyRedundantLoad(
}
// Perform PHI construction.
- Value *V = constructSSAForLoadSet(Load, ValuesPerBlock, *this);
+ Value *V = constructSSAForLoadSet(Load, ValuesPerBlock, getDominatorTree());
// constructSSAForLoadSet is responsible for combining metadata.
ICF->removeUsersOf(Load);
Load->replaceAllUsesWith(V);
@@ -1711,8 +2025,9 @@ void GVNPass::eliminatePartiallyRedundantLoad(
salvageAndRemoveInstruction(Load);
}
-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
@@ -1964,9 +2279,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())
@@ -2055,7 +2370,7 @@ static void reportLoadElim(LoadInst *Load, Value *AvailableValue,
/// 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))
@@ -2098,8 +2413,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) {
@@ -2142,7 +2457,7 @@ bool GVNPass::processNonLocalLoad(LoadInst *Load,
LLVM_DEBUG(dbgs() << "GVN REMOVING NONLOCAL LOAD: " << *Load << '\n');
// Perform PHI construction.
- Value *V = constructSSAForLoadSet(Load, ValuesPerBlock, *this);
+ Value *V = constructSSAForLoadSet(Load, ValuesPerBlock, getDominatorTree());
// constructSSAForLoadSet is responsible for combining metadata.
ICF->removeUsersOf(Load);
Load->replaceAllUsesWith(V);
@@ -2176,7 +2491,7 @@ bool GVNPass::processNonLocalLoad(LoadInst *Load,
return Changed;
}
-bool GVNPass::processAssumeIntrinsic(AssumeInst *IntrinsicI) {
+bool GVNPassImpl::processAssumeIntrinsic(AssumeInst *IntrinsicI) {
Value *V = IntrinsicI->getArgOperand(0);
if (ConstantInt *Cond = dyn_cast<ConstantInt>(V)) {
@@ -2326,7 +2641,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<GVNPassImpl::ReachingMemVal> GVNPassImpl::scanMemoryAccessesUsers(
const MemoryLocation &Loc, bool IsInvariantLoad, BasicBlock *BB,
const SmallVectorImpl<MemoryAccess *> &ClobbersList, MemorySSA &MSSA,
BatchAAResults &AA, LoadInst *L) {
@@ -2400,7 +2715,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<GVNPassImpl::ReachingMemVal> GVNPassImpl::accessMayModifyLocation(
MemoryAccess *ClobberMA, const MemoryLocation &Loc, Align LoadAlign,
bool IsInvariantLoad, BasicBlock *BB, MemorySSA &MSSA, BatchAAResults &AA) {
assert(ClobberMA->getBlock() == BB);
@@ -2508,10 +2823,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;
@@ -2566,11 +2881,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;
@@ -2616,9 +2931,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();
@@ -2800,7 +3115,7 @@ bool GVNPass::findReachingValuesForLoad(LoadInst *L,
/// 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;
@@ -2874,7 +3189,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);
@@ -3069,7 +3384,7 @@ void GVNValueTable::eraseTranslateCacheEntry(uint32_t Num,
// 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;
@@ -3102,7 +3417,7 @@ static bool isOnlyReachableViaThisEdge(const BasicBlockEdge &E,
return Pred != nullptr;
}
-void GVNPass::assignBlockRPONumber(Function &F) {
+void GVNPassImpl::assignBlockRPONumber(Function &F) {
BlockRPONumber.clear();
uint32_t NextBlockNumber = 1;
ReversePostOrderTraversal<Function *> RPOT(&F);
@@ -3116,7 +3431,7 @@ void GVNPass::assignBlockRPONumber(Function &F) {
/// '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;
@@ -3324,7 +3639,7 @@ bool GVNPass::propagateEquality(
/// 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
@@ -3480,7 +3795,7 @@ bool GVNPass::processInstruction(Instruction *I) {
}
/// 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) {
@@ -3564,7 +3879,7 @@ bool GVNPass::runImpl(Function &F, AssumptionCache &RunAC, DominatorTree &RunDT,
return Changed;
}
-bool GVNPass::processBlock(BasicBlock *BB) {
+bool GVNPassImpl::processBlock(BasicBlock *BB) {
if (DeadBlocks.count(BB))
return false;
@@ -3585,8 +3900,9 @@ bool GVNPass::processBlock(BasicBlock *BB) {
}
// 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
@@ -3633,7 +3949,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() ||
@@ -3795,7 +4111,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.
@@ -3822,7 +4138,8 @@ bool GVNPass::performPRE(Function &F) {
/// 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(
@@ -3838,7 +4155,7 @@ BasicBlock *GVNPass::splitCriticalEdges(BasicBlock *Pred, BasicBlock *Succ) {
/// Split critical edges found during the previous
/// iteration that may enable further optimization.
-bool GVNPass::splitCriticalEdges() {
+bool GVNPassImpl::splitCriticalEdges() {
if (ToSplit.empty())
return false;
@@ -3858,7 +4175,7 @@ bool GVNPass::splitCriticalEdges() {
}
/// Executes one iteration of GVN.
-bool GVNPass::iterateOnFunction(Function &F) {
+bool GVNPassImpl::iterateOnFunction(Function &F) {
cleanupGlobalSets();
// Top-down walk of the dominator tree.
@@ -3874,7 +4191,7 @@ bool GVNPass::iterateOnFunction(Function &F) {
return Changed;
}
-void GVNPass::cleanupGlobalSets() {
+void GVNPassImpl::cleanupGlobalSets() {
VN.clear();
LeaderTable.clear();
BlockRPONumber.clear();
@@ -3882,7 +4199,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)
@@ -3897,7 +4214,7 @@ void GVNPass::removeInstruction(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);
}
@@ -3905,7 +4222,7 @@ void GVNPass::verifyRemoved(const Instruction *Inst) const {
/// 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;
@@ -3993,7 +4310,7 @@ void GVNPass::addDeadBlock(BasicBlock *BB) {
// 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;
@@ -4018,7 +4335,7 @@ bool GVNPass::processFoldableCondBr(CondBrInst *BI) {
// 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);
@@ -4027,7 +4344,7 @@ void GVNPass::assignValNumForDeadCode() {
}
}
-class llvm::GVNLegacyPass : public FunctionPass {
+class GVNLegacyPass : public FunctionPass {
public:
static char ID; // Pass identification, replacement for typeid.
@@ -4049,7 +4366,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),
@@ -4081,7 +4398,7 @@ class llvm::GVNLegacyPass : public FunctionPass {
}
private:
- GVNPass Impl;
+ GVNPassImpl Impl;
};
char GVNLegacyPass::ID = 0;
>From 394ca97b8169667da874f19e83cf7c21c94000a3 Mon Sep 17 00:00:00 2001
From: Momchil Velikov <momchil.velikov at arm.com>
Date: Tue, 29 Sep 2026 17:28:29 +0100
Subject: [PATCH 2/3] [fixup] Remove unused member functions from GVNPassImpl
---
llvm/lib/Transforms/Scalar/GVN.cpp | 7 -------
1 file changed, 7 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp
index 154e3106a39d3..6c35ed71483d7 100644
--- a/llvm/lib/Transforms/Scalar/GVN.cpp
+++ b/llvm/lib/Transforms/Scalar/GVN.cpp
@@ -288,13 +288,6 @@ class GVNPassImpl {
GVNPassImpl(llvm::GVNOptions Options = {}) : Options(Options) {}
- /// Run the pass over the function.
- LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
-
- LLVM_ABI void
- printPipeline(raw_ostream &OS,
- function_ref<StringRef(StringRef)> MapClassName2PassName);
-
/// This removes the specified instruction from
/// our various maps and marks it for deletion.
LLVM_ABI void salvageAndRemoveInstruction(Instruction *I);
>From a82586daf6152161c66b73c3b4fff539f8d42223 Mon Sep 17 00:00:00 2001
From: Momchil Velikov <momchil.velikov at arm.com>
Date: Wed, 30 Sep 2026 10:56:26 +0100
Subject: [PATCH 3/3] [fixup] Clean GVNPassImpl from histerical cruft
---
llvm/lib/Transforms/Scalar/GVN.cpp | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp
index 6c35ed71483d7..d7a3ff8bca76d 100644
--- a/llvm/lib/Transforms/Scalar/GVN.cpp
+++ b/llvm/lib/Transforms/Scalar/GVN.cpp
@@ -282,7 +282,6 @@ class GVNPassImpl {
llvm::GVNOptions Options;
public:
- struct Expression;
struct AvailableValue;
struct AvailableValueInBlock;
@@ -290,23 +289,22 @@ class GVNPassImpl {
/// This removes the specified instruction from
/// our various maps and marks it for deletion.
- LLVM_ABI void salvageAndRemoveInstruction(Instruction *I);
+ void salvageAndRemoveInstruction(Instruction *I);
DominatorTree &getDominatorTree() const { return *DT; }
AAResults *getAliasAnalysis() const { return VN.getAliasAnalysis(); }
MemoryDependenceResults &getMemDep() const { return *MD; }
- LLVM_ABI bool isScalarPREEnabled() const;
- LLVM_ABI bool isLoadPREEnabled() const;
- LLVM_ABI bool isLoadInLoopPREEnabled() const;
- LLVM_ABI bool isLoadPRESplitBackedgeEnabled() const;
- LLVM_ABI bool isMemDepEnabled() const;
- LLVM_ABI bool isMemorySSAEnabled() const;
+ bool isScalarPREEnabled() const;
+ bool isLoadPREEnabled() const;
+ bool isLoadInLoopPREEnabled() const;
+ bool isLoadPRESplitBackedgeEnabled() const;
+ bool isMemDepEnabled() const;
+ bool isMemorySSAEnabled() const;
private:
friend class llvm::GVNPass;
friend class GVNLegacyPass;
- friend struct DenseMapInfo<Expression>;
MemoryDependenceResults *MD = nullptr;
DominatorTree *DT = nullptr;
More information about the llvm-commits
mailing list