/// basic block.
DenseMap<BasicBlock *, InstOverlapIntervalsTy> IOLs;
- struct CheckCache {
- SmallPtrSet<MemoryAccess *, 16> KnownNoReads;
- SmallPtrSet<MemoryAccess *, 16> KnownReads;
-
- bool isKnownNoRead(MemoryAccess *A) const {
- return KnownNoReads.find(A) != KnownNoReads.end();
- }
- bool isKnownRead(MemoryAccess *A) const {
- return KnownReads.find(A) != KnownReads.end();
- }
- };
-
DSEState(Function &F, AliasAnalysis &AA, MemorySSA &MSSA, DominatorTree &DT,
PostDominatorTree &PDT, const TargetLibraryInfo &TLI)
: F(F), AA(AA), BatchAA(AA), MSSA(MSSA), DT(DT), PDT(PDT), TLI(TLI),
Optional<MemoryAccess *>
getDomMemoryDef(MemoryDef *KillingDef, MemoryAccess *StartAccess,
const MemoryLocation &DefLoc, const Value *DefUO,
- CheckCache &Cache, unsigned &ScanLimit,
- unsigned &WalkerStepLimit, bool IsMemTerm,
- unsigned &PartialLimit) {
+ unsigned &ScanLimit, unsigned &WalkerStepLimit,
+ bool IsMemTerm, unsigned &PartialLimit) {
if (ScanLimit == 0 || WalkerStepLimit == 0) {
LLVM_DEBUG(dbgs() << "\n ... hit scan limit\n");
return None;
LLVM_DEBUG(dbgs() << " trying to get dominating access\n");
// Find the next clobbering Mod access for DefLoc, starting at StartAccess.
+ Optional<MemoryLocation> CurrentLoc;
do {
StepAgain = false;
LLVM_DEBUG({
// clobber, bail out, as the path is not profitable. We skip this check
// for intrinsic calls, because the code knows how to handle memcpy
// intrinsics.
- if (!isa<IntrinsicInst>(CurrentI) &&
- (Cache.KnownReads.contains(Current) ||
- isReadClobber(DefLoc, CurrentI))) {
- Cache.KnownReads.insert(Current);
+ if (!isa<IntrinsicInst>(CurrentI) && isReadClobber(DefLoc, CurrentI))
return None;
- }
// Quick check if there are direct uses that are read-clobbers.
if (any_of(Current->uses(), [this, &DefLoc, StartAccess](Use &U) {
isReadClobber(DefLoc, UseOrDef->getMemoryInst());
return false;
})) {
- Cache.KnownReads.insert(Current);
LLVM_DEBUG(dbgs() << " ... found a read clobber\n");
return None;
}
}
// If Current does not have an analyzable write location, skip it
- auto CurrentLoc = getLocForWriteEx(CurrentI);
+ CurrentLoc = getLocForWriteEx(CurrentI);
if (!CurrentLoc) {
StepAgain = true;
Current = CurrentDef->getDefiningAccess();
continue;
}
+ // AliasAnalysis does not account for loops. Limit elimination to
+ // candidates for which we can guarantee they always store to the same
+ // memory location and not multiple locations in a loop.
+ if (Current->getBlock() != KillingDef->getBlock() &&
+ !IsGuaranteedLoopInvariant(const_cast<Value *>(CurrentLoc->Ptr))) {
+ StepAgain = true;
+ Current = CurrentDef->getDefiningAccess();
+ WalkerStepLimit -= 1;
+ continue;
+ }
+
if (IsMemTerm) {
// If the killing def is a memory terminator (e.g. lifetime.end), check
// the next candidate if the current Current does not write the same
}
continue;
} else {
- // AliasAnalysis does not account for loops. Limit elimination to
- // candidates for which we can guarantee they always store to the same
- // memory location and not multiple locations in a loop.
- if (Current->getBlock() != KillingDef->getBlock() &&
- !IsGuaranteedLoopInvariant(const_cast<Value *>(CurrentLoc->Ptr))) {
- StepAgain = true;
- Current = CurrentDef->getDefiningAccess();
- WalkerStepLimit -= 1;
- continue;
- }
-
int64_t InstWriteOffset, DepWriteOffset;
auto OR = isOverwrite(KillingI, CurrentI, DefLoc, *CurrentLoc, DL, TLI,
DepWriteOffset, InstWriteOffset, BatchAA, &F);
}
--ScanLimit;
NumDomMemDefChecks++;
-
- // Check if we already visited this access.
- if (Cache.isKnownNoRead(UseAccess)) {
- LLVM_DEBUG(dbgs() << " ... skip, discovered that " << *UseAccess
- << " is safe earlier.\n");
- continue;
- }
- if (Cache.isKnownRead(UseAccess)) {
- LLVM_DEBUG(dbgs() << " ... bail out, discovered that " << *UseAccess
- << " has a read-clobber earlier.\n");
- return None;
- }
KnownNoReads.insert(UseAccess);
if (isa<MemoryPhi>(UseAccess)) {
// A memory terminator kills all preceeding MemoryDefs and all succeeding
// MemoryAccesses. We do not have to check it's users.
- if (isMemTerminator(DefLoc, KillingI, UseInst)) {
+ if (isMemTerminator(*CurrentLoc, EarlierMemInst, UseInst)) {
LLVM_DEBUG(
dbgs()
<< " ... skipping, memterminator invalidates following accesses\n");
if (UseInst->mayThrow() && !isInvisibleToCallerBeforeRet(DefUO)) {
LLVM_DEBUG(dbgs() << " ... found throwing instruction\n");
- Cache.KnownReads.insert(UseAccess);
- Cache.KnownReads.insert(StartAccess);
- Cache.KnownReads.insert(EarlierAccess);
return None;
}
// Uses which may read the original MemoryDef mean we cannot eliminate the
// original MD. Stop walk.
- if (isReadClobber(DefLoc, UseInst)) {
+ if (isReadClobber(*CurrentLoc, UseInst)) {
LLVM_DEBUG(dbgs() << " ... found read clobber\n");
- Cache.KnownReads.insert(UseAccess);
- Cache.KnownReads.insert(StartAccess);
- Cache.KnownReads.insert(EarlierAccess);
return None;
}
// 3 = Def(1) ; <---- Current (3, 2) = NoAlias, (3,1) = MayAlias,
// stores [0,1]
if (MemoryDef *UseDef = dyn_cast<MemoryDef>(UseAccess)) {
- if (isCompleteOverwrite(DefLoc, KillingI, UseInst)) {
+ if (isCompleteOverwrite(*CurrentLoc, EarlierMemInst, UseInst)) {
if (!isInvisibleToCallerAfterRet(DefUO) &&
UseAccess != EarlierAccess) {
BasicBlock *MaybeKillingBlock = UseInst->getParent();
// No aliasing MemoryUses of EarlierAccess found, EarlierAccess is
// potentially dead.
- Cache.KnownNoReads.insert(KnownNoReads.begin(), KnownNoReads.end());
return {EarlierAccess};
}
bool Shortend = false;
bool IsMemTerm = State.isMemTerminatorInst(SI);
- DSEState::CheckCache Cache;
// Check if MemoryAccesses in the worklist are killed by KillingDef.
for (unsigned I = 0; I < ToCheck.size(); I++) {
Current = ToCheck[I];
continue;
Optional<MemoryAccess *> Next = State.getDomMemoryDef(
- KillingDef, Current, SILoc, SILocUnd, Cache, ScanLimit,
- WalkerStepLimit, IsMemTerm, PartialLimit);
+ KillingDef, Current, SILoc, SILocUnd, ScanLimit, WalkerStepLimit,
+ IsMemTerm, PartialLimit);
if (!Next) {
LLVM_DEBUG(dbgs() << " finished walk\n");
ret void
}
-; TODO: The store to %a0 is dead, because only %a1 is read later.
+; The store to %a0 is dead, because only %a1 is read later.
define void @test3(i1 %c) {
; CHECK-LABEL: @test3(
; CHECK-NEXT: [[A:%.*]] = alloca [2 x i8], align 1
-; CHECK-NEXT: [[A0:%.*]] = getelementptr [2 x i8], [2 x i8]* [[A]], i32 0, i32 0
; CHECK-NEXT: [[A1:%.*]] = getelementptr [2 x i8], [2 x i8]* [[A]], i32 0, i32 1
-; CHECK-NEXT: store i8 1, i8* [[A0]], align 1
; CHECK-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
; CHECK: if:
; CHECK-NEXT: store [2 x i8] zeroinitializer, [2 x i8]* [[A]], align 1
define void @test4(i1 %c) {
; CHECK-LABEL: @test4(
; CHECK-NEXT: [[A:%.*]] = alloca [2 x i8], align 1
-; CHECK-NEXT: [[A0:%.*]] = getelementptr [2 x i8], [2 x i8]* [[A]], i32 0, i32 0
; CHECK-NEXT: [[A1:%.*]] = getelementptr [2 x i8], [2 x i8]* [[A]], i32 0, i32 1
; CHECK-NEXT: store i8 1, i8* [[A1]], align 1
-; CHECK-NEXT: store i8 1, i8* [[A0]], align 1
; CHECK-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
; CHECK: if:
; CHECK-NEXT: store [2 x i8] zeroinitializer, [2 x i8]* [[A]], align 1