Skip to content

Commit

Permalink
[DSE,MemorySSA] Use BatchAA for AA queries.
Browse files Browse the repository at this point in the history
We can use BatchAA to avoid some repeated AA queries. We only remove
stores, so I think we will get away with using a single BatchAA instance
for the complete run.

The changes in AliasAnalysis.h mirror the changes in D85583.

The change improves compile-time by roughly 1%.
http://llvm-compile-time-tracker.com/compare.php?from=67ad786353dfcc7633c65de11601d7823746378e&to=10529e5b43809808e8c198f88fffd8f756554e45&stat=instructions

This is part of the patches to bring down compile-time to the level
referenced in
http://lists.llvm.org/pipermail/llvm-dev/2020-August/144417.html

Reviewed By: asbirlea

Differential Revision: https://reviews.llvm.org/D86275
  • Loading branch information
fhahn committed Aug 22, 2020
1 parent b65ba70 commit 5e7e216
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 31 deletions.
7 changes: 7 additions & 0 deletions llvm/include/llvm/Analysis/AliasAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,13 @@ class BatchAAResults {
FunctionModRefBehavior getModRefBehavior(const CallBase *Call) {
return AA.getModRefBehavior(Call);
}
bool isMustAlias(const MemoryLocation &LocA, const MemoryLocation &LocB) {
return alias(LocA, LocB) == MustAlias;
}
bool isMustAlias(const Value *V1, const Value *V2) {
return alias(MemoryLocation(V1, LocationSize::precise(1)),
MemoryLocation(V2, LocationSize::precise(1))) == MustAlias;
}
};

/// Temporary typedef for legacy code that uses a generic \c AliasAnalysis
Expand Down
70 changes: 39 additions & 31 deletions llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,12 +383,12 @@ enum OverwriteResult {
/// write to the same underlying object. In that case, use isPartialOverwrite to
/// check if \p Later partially overwrites \p Earlier. Returns 'OW_Unknown' if
/// nothing can be determined.
static OverwriteResult isOverwrite(const MemoryLocation &Later,
const MemoryLocation &Earlier,
const DataLayout &DL,
const TargetLibraryInfo &TLI,
int64_t &EarlierOff, int64_t &LaterOff,
AliasAnalysis &AA, const Function *F) {
template <typename AATy>
static OverwriteResult
isOverwrite(const MemoryLocation &Later, const MemoryLocation &Earlier,
const DataLayout &DL, const TargetLibraryInfo &TLI,
int64_t &EarlierOff, int64_t &LaterOff, AATy &AA,
const Function *F) {
// FIXME: Vet that this works for size upper-bounds. Seems unlikely that we'll
// get imprecise values here, though (except for unknown sizes).
if (!Later.Size.isPrecise() || !Earlier.Size.isPrecise())
Expand Down Expand Up @@ -643,11 +643,10 @@ static bool isPossibleSelfRead(Instruction *Inst,
/// modified between the first and the second instruction.
/// Precondition: Second instruction must be dominated by the first
/// instruction.
static bool memoryIsNotModifiedBetween(Instruction *FirstI,
Instruction *SecondI,
AliasAnalysis *AA,
const DataLayout &DL,
DominatorTree *DT) {
template <typename AATy>
static bool
memoryIsNotModifiedBetween(Instruction *FirstI, Instruction *SecondI, AATy &AA,
const DataLayout &DL, DominatorTree *DT) {
// Do a backwards scan through the CFG from SecondI to FirstI. Look for
// instructions which can modify the memory location accessed by SecondI.
//
Expand Down Expand Up @@ -696,7 +695,7 @@ static bool memoryIsNotModifiedBetween(Instruction *FirstI,
for (; BI != EI; ++BI) {
Instruction *I = &*BI;
if (I->mayWriteToMemory() && I != SecondI)
if (isModSet(AA->getModRefInfo(I, MemLoc.getWithNewPtr(Ptr))))
if (isModSet(AA.getModRefInfo(I, MemLoc.getWithNewPtr(Ptr))))
return false;
}
if (B != FirstBB) {
Expand Down Expand Up @@ -1132,7 +1131,7 @@ static bool eliminateNoopStore(Instruction *Inst, BasicBlock::iterator &BBI,
if (LoadInst *DepLoad = dyn_cast<LoadInst>(SI->getValueOperand())) {
if (SI->getPointerOperand() == DepLoad->getPointerOperand() &&
isRemovable(SI) &&
memoryIsNotModifiedBetween(DepLoad, SI, AA, DL, DT)) {
memoryIsNotModifiedBetween(DepLoad, SI, *AA, DL, DT)) {

LLVM_DEBUG(
dbgs() << "DSE: Remove Store Of Load from same pointer:\n LOAD: "
Expand All @@ -1151,7 +1150,7 @@ static bool eliminateNoopStore(Instruction *Inst, BasicBlock::iterator &BBI,
dyn_cast<Instruction>(getUnderlyingObject(SI->getPointerOperand()));

if (UnderlyingPointer && isCallocLikeFn(UnderlyingPointer, TLI) &&
memoryIsNotModifiedBetween(UnderlyingPointer, SI, AA, DL, DT)) {
memoryIsNotModifiedBetween(UnderlyingPointer, SI, *AA, DL, DT)) {
LLVM_DEBUG(
dbgs() << "DSE: Remove null store to the calloc'ed object:\n DEAD: "
<< *Inst << "\n OBJECT: " << *UnderlyingPointer << '\n');
Expand All @@ -1164,11 +1163,10 @@ static bool eliminateNoopStore(Instruction *Inst, BasicBlock::iterator &BBI,
return false;
}

static Constant *
tryToMergePartialOverlappingStores(StoreInst *Earlier, StoreInst *Later,
int64_t InstWriteOffset,
int64_t DepWriteOffset, const DataLayout &DL,
AliasAnalysis *AA, DominatorTree *DT) {
template <typename AATy>
static Constant *tryToMergePartialOverlappingStores(
StoreInst *Earlier, StoreInst *Later, int64_t InstWriteOffset,
int64_t DepWriteOffset, const DataLayout &DL, AATy &AA, DominatorTree *DT) {

if (Earlier && isa<ConstantInt>(Earlier->getValueOperand()) &&
DL.typeSizeEqualsStoreSize(Earlier->getValueOperand()->getType()) &&
Expand Down Expand Up @@ -1361,7 +1359,7 @@ static bool eliminateDeadStores(BasicBlock &BB, AliasAnalysis *AA,
auto *Earlier = dyn_cast<StoreInst>(DepWrite);
auto *Later = dyn_cast<StoreInst>(Inst);
if (Constant *C = tryToMergePartialOverlappingStores(
Earlier, Later, InstWriteOffset, DepWriteOffset, DL, AA,
Earlier, Later, InstWriteOffset, DepWriteOffset, DL, *AA,
DT)) {
auto *SI = new StoreInst(
C, Earlier->getPointerOperand(), false, Earlier->getAlign(),
Expand Down Expand Up @@ -1507,6 +1505,16 @@ bool canSkipDef(MemoryDef *D, bool DefVisibleToCaller) {
struct DSEState {
Function &F;
AliasAnalysis &AA;

/// The single BatchAA instance that is used to cache AA queries. It will
/// not be invalidated over the whole run. This is safe, because:
/// 1. Only memory writes are removed, so the alias cache for memory
/// locations remains valid.
/// 2. No new instructions are added (only instructions removed), so cached
/// information for a deleted value cannot be accessed by a re-used new
/// value pointer.
BatchAAResults BatchAA;

MemorySSA &MSSA;
DominatorTree &DT;
PostDominatorTree &PDT;
Expand Down Expand Up @@ -1534,7 +1542,7 @@ struct DSEState {

DSEState(Function &F, AliasAnalysis &AA, MemorySSA &MSSA, DominatorTree &DT,
PostDominatorTree &PDT, const TargetLibraryInfo &TLI)
: F(F), AA(AA), MSSA(MSSA), DT(DT), PDT(PDT), TLI(TLI) {}
: F(F), AA(AA), BatchAA(AA), MSSA(MSSA), DT(DT), PDT(PDT), TLI(TLI) {}

static DSEState get(Function &F, AliasAnalysis &AA, MemorySSA &MSSA,
DominatorTree &DT, PostDominatorTree &PDT,
Expand Down Expand Up @@ -1623,7 +1631,7 @@ struct DSEState {
}

/// Returns true if \p Use completely overwrites \p DefLoc.
bool isCompleteOverwrite(MemoryLocation DefLoc, Instruction *UseInst) const {
bool isCompleteOverwrite(MemoryLocation DefLoc, Instruction *UseInst) {
// UseInst has a MemoryDef associated in MemorySSA. It's possible for a
// MemoryDef to not write to memory, e.g. a volatile load is modeled as a
// MemoryDef.
Expand All @@ -1638,7 +1646,7 @@ struct DSEState {
auto CC = getLocForWriteEx(UseInst);
const DataLayout &DL = F.getParent()->getDataLayout();
return CC && isOverwrite(*CC, DefLoc, DL, TLI, DepWriteOffset,
InstWriteOffset, AA, &F) == OW_Complete;
InstWriteOffset, BatchAA, &F) == OW_Complete;
}

/// Returns true if \p Def is not read before returning from the function.
Expand Down Expand Up @@ -1717,7 +1725,7 @@ struct DSEState {

/// Returns true if \p MaybeTerm is a memory terminator for the same
/// underlying object as \p DefLoc.
bool isMemTerminator(MemoryLocation DefLoc, Instruction *MaybeTerm) const {
bool isMemTerminator(MemoryLocation DefLoc, Instruction *MaybeTerm) {
Optional<std::pair<MemoryLocation, bool>> MaybeTermLoc =
getLocForTerminator(MaybeTerm);

Expand All @@ -1730,19 +1738,19 @@ struct DSEState {
DataLayout DL = MaybeTerm->getParent()->getModule()->getDataLayout();
DefLoc = MemoryLocation(getUnderlyingObject(DefLoc.Ptr));
}
return AA.isMustAlias(MaybeTermLoc->first, DefLoc);
return BatchAA.isMustAlias(MaybeTermLoc->first, DefLoc);
}

// Returns true if \p Use may read from \p DefLoc.
bool isReadClobber(MemoryLocation DefLoc, Instruction *UseInst) const {
bool isReadClobber(MemoryLocation DefLoc, Instruction *UseInst) {
if (!UseInst->mayReadFromMemory())
return false;

if (auto *CB = dyn_cast<CallBase>(UseInst))
if (CB->onlyAccessesInaccessibleMemory())
return false;

ModRefInfo MR = AA.getModRefInfo(UseInst, DefLoc);
ModRefInfo MR = BatchAA.getModRefInfo(UseInst, DefLoc);
// If necessary, perform additional analysis.
if (isRefSet(MR))
MR = AA.callCapturesBefore(UseInst, DefLoc, &DT);
Expand All @@ -1758,7 +1766,7 @@ struct DSEState {
Optional<MemoryAccess *>
getDomMemoryDef(MemoryDef *KillingDef, MemoryAccess *Current,
MemoryLocation DefLoc, bool DefVisibleToCallerBeforeRet,
bool DefVisibleToCallerAfterRet, unsigned &ScanLimit) const {
bool DefVisibleToCallerAfterRet, unsigned &ScanLimit) {
if (ScanLimit == 0) {
LLVM_DEBUG(dbgs() << "\n ... hit scan limit\n");
return None;
Expand Down Expand Up @@ -2285,7 +2293,7 @@ bool eliminateDeadStoresMemorySSA(Function &F, AliasAnalysis &AA,
// Check if NI overwrites SI.
int64_t InstWriteOffset, DepWriteOffset;
OverwriteResult OR = isOverwrite(SILoc, NILoc, DL, TLI, DepWriteOffset,
InstWriteOffset, State.AA, &F);
InstWriteOffset, State.BatchAA, &F);
if (OR == OW_MaybePartial) {
auto Iter = State.IOLs.insert(
std::make_pair<BasicBlock *, InstOverlapIntervalsTy>(
Expand All @@ -2303,8 +2311,8 @@ bool eliminateDeadStoresMemorySSA(Function &F, AliasAnalysis &AA,
// TODO: implement tryToMergeParialOverlappingStores using MemorySSA.
if (Earlier && Later && DT.dominates(Earlier, Later)) {
if (Constant *Merged = tryToMergePartialOverlappingStores(
Earlier, Later, InstWriteOffset, DepWriteOffset, DL, &AA,
&DT)) {
Earlier, Later, InstWriteOffset, DepWriteOffset, DL,
State.BatchAA, &DT)) {

// Update stored value of earlier store to merged constant.
Earlier->setOperand(0, Merged);
Expand Down

0 comments on commit 5e7e216

Please sign in to comment.