[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