Skip to content

Commit bb3e1cc

Browse files
momchil-velikovantoniofrighetto
authored andcommitted
[GVN] MemorySSA for GVN: eliminate redundant loads via MemorySSA
Introduce the main algorithm performing redundant load elimination via MemorySSA in GVN. The entry point is `findReachingValuesForLoad`, which, given as input a possibly redundant load `L`, it attempts to provide as output a set of reaching memory values (`ReachingMemVal`), i.e., which values (defs or equivalent reads) can reach `L` along at least one path where that memory location is not modified meanwhile (if non-local, PRE will establish whether the load may be eliminated). Specifically, a reaching value may be of the following descriptor kind: * Def: found a new instruction that produces exactly the bits the load would read. For example, a must-alias store (which defines the load memory location), or a must-alias read (exactly reads the same memory location, found, e.g., after a phi-translation fixup); * Clobber: found a write that clobbers a superset of the bits the load would read. For example, a memset call over a memory region, whose value read overlaps such a region (and may be forwarded to the load), or a generic call clobbering the load that cannot be reused; * Other: a precise instruction could not be found, but know the block where the dependency exists (e.g., a memory location already live at function entry). We start off by looking for the users of the defining memory access of the given load within the same block, searching for local dependencies. We may need to iteratively follow the use-def chain of the defining memory access to find the actual clobbering access, while staying within the scope of the load. If an actual local def/clobber (or liveOnEntry) is found, we are done and return that one. Otherwise, we move visiting the predecessors of the load's block, considering the incoming value from the MemoryPhi as a potential clobbering access. Hence, we search for dependencies within the predecessor block itself. If the new potential clobbering access is a MemoryPhi, we continue visiting the transitive closure of the predecessor, recording its new potential clobbering access. In this phase, as we are exploring non-local definitions, the load itself may be considered a must-alias def as well when being examined from a backedge path.
1 parent 7e1f79c commit bb3e1cc

25 files changed

+2293
-3668
lines changed

llvm/include/llvm/Transforms/Scalar/GVN.h

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "llvm/ADT/MapVector.h"
2020
#include "llvm/ADT/SetVector.h"
2121
#include "llvm/ADT/SmallVector.h"
22+
#include "llvm/Analysis/PHITransAddr.h"
2223
#include "llvm/IR/Dominators.h"
2324
#include "llvm/IR/InstrTypes.h"
2425
#include "llvm/IR/PassManager.h"
@@ -37,8 +38,10 @@ class AAResults;
3738
class AssumeInst;
3839
class AssumptionCache;
3940
class BasicBlock;
41+
class BatchAAResults;
4042
class BranchInst;
4143
class CallInst;
44+
class EarliestEscapeAnalysis;
4245
class ExtractValueInst;
4346
class Function;
4447
class FunctionPass;
@@ -255,6 +258,7 @@ class GVNPass : public PassInfoMixin<GVNPass> {
255258
OptimizationRemarkEmitter *ORE = nullptr;
256259
ImplicitControlFlowTracking *ICF = nullptr;
257260
LoopInfo *LI = nullptr;
261+
AAResults *AA = nullptr;
258262
MemorySSAUpdater *MSSAU = nullptr;
259263

260264
ValueTable VN;
@@ -344,21 +348,91 @@ class GVNPass : public PassInfoMixin<GVNPass> {
344348
// List of critical edges to be split between iterations.
345349
SmallVector<std::pair<Instruction *, unsigned>, 4> ToSplit;
346350

351+
enum class DepKind {
352+
Other = 0, // Unknown value.
353+
Def, // Exactly overlapping locations.
354+
Clobber, // Reaching value superset of needed bits.
355+
};
356+
357+
// Describe a memory location value, such that there exists a path to a point
358+
// in the program, along which that memory location is not modified.
359+
struct ReachingMemVal {
360+
DepKind Kind;
361+
BasicBlock *Block;
362+
const Value *Addr;
363+
Instruction *Inst;
364+
int32_t Offset;
365+
366+
static ReachingMemVal getUnknown(BasicBlock *BB, const Value *Addr,
367+
Instruction *Inst = nullptr) {
368+
return {DepKind::Other, BB, Addr, Inst, -1};
369+
}
370+
371+
static ReachingMemVal getDef(const Value *Addr, Instruction *Inst) {
372+
return {DepKind::Def, Inst->getParent(), Addr, Inst, -1};
373+
}
374+
375+
static ReachingMemVal getClobber(const Value *Addr, Instruction *Inst,
376+
int32_t Offset = -1) {
377+
return {DepKind::Clobber, Inst->getParent(), Addr, Inst, Offset};
378+
}
379+
};
380+
381+
struct DependencyBlockInfo {
382+
DependencyBlockInfo() = delete;
383+
DependencyBlockInfo(const PHITransAddr &Addr, MemoryAccess *ClobberMA)
384+
: Addr(Addr), InitialClobberMA(ClobberMA), ClobberMA(ClobberMA),
385+
ForceUnknown(false), Visited(false) {}
386+
PHITransAddr Addr;
387+
MemoryAccess *InitialClobberMA;
388+
MemoryAccess *ClobberMA;
389+
std::optional<ReachingMemVal> MemVal;
390+
bool ForceUnknown : 1;
391+
bool Visited : 1;
392+
};
393+
394+
using DependencyBlockSet = DenseMap<BasicBlock *, DependencyBlockInfo>;
395+
396+
std::optional<GVNPass::ReachingMemVal> scanMemoryAccessesUsers(
397+
const MemoryLocation &Loc, bool IsInvariantLoad, BasicBlock *BB,
398+
const SmallVectorImpl<MemoryAccess *> &ClobbersList, MemorySSA &MSSA,
399+
BatchAAResults &AA, LoadInst *L = nullptr);
400+
401+
std::optional<GVNPass::ReachingMemVal>
402+
accessMayModifyLocation(MemoryAccess *ClobberMA, const MemoryLocation &Loc,
403+
bool IsInvariantLoad, BasicBlock *BB, MemorySSA &MSSA,
404+
BatchAAResults &AA);
405+
406+
bool collectPredecessors(BasicBlock *BB, const PHITransAddr &Addr,
407+
MemoryAccess *ClobberMA, DependencyBlockSet &Blocks,
408+
SmallVectorImpl<BasicBlock *> &Worklist);
409+
410+
void collectClobberList(SmallVectorImpl<MemoryAccess *> &Clobbers,
411+
BasicBlock *BB, const DependencyBlockInfo &StartInfo,
412+
const DependencyBlockSet &Blocks, MemorySSA &MSSA);
413+
414+
bool findReachingValuesForLoad(LoadInst *Inst,
415+
SmallVectorImpl<ReachingMemVal> &Values,
416+
MemorySSA &MSSA, AAResults &AA);
417+
347418
// Helper functions of redundant load elimination.
348419
bool processLoad(LoadInst *L);
349420
bool processMaskedLoad(IntrinsicInst *I);
350421
bool processNonLocalLoad(LoadInst *L);
422+
bool processNonLocalLoad(LoadInst *L, SmallVectorImpl<ReachingMemVal> &Deps);
351423
bool processAssumeIntrinsic(AssumeInst *II);
352424

353425
/// Given a local dependency (Def or Clobber) determine if a value is
354426
/// available for the load.
355427
std::optional<gvn::AvailableValue>
356-
AnalyzeLoadAvailability(LoadInst *Load, MemDepResult DepInfo, Value *Address);
428+
AnalyzeLoadAvailability(LoadInst *Load, const ReachingMemVal &Dep,
429+
Value *Address);
357430

358431
/// Given a list of non-local dependencies, determine if a value is
359432
/// available for the load in each specified block. If it is, add it to
360433
/// ValuesPerBlock. If not, add it to UnavailableBlocks.
361-
void AnalyzeLoadAvailability(LoadInst *Load, LoadDepVect &Deps,
434+
void AnalyzeLoadAvailability(LoadInst *Load,
435+
SmallVectorImpl<ReachingMemVal> &Deps,
362436
AvailValInBlkVect &ValuesPerBlock,
363437
UnavailBlkVect &UnavailableBlocks);
364438

0 commit comments

Comments
 (0)