1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 11:02:59 +02:00

[DeadStoreElimination] Remove dead zero store to calloc initialized memory

This change allows dead store elimination to remove zero and null stores into memory freshly allocated with calloc-like function.

Differential Revision: http://reviews.llvm.org/D13021

llvm-svn: 248374
This commit is contained in:
Igor Laevsky 2015-09-23 11:38:44 +00:00
parent 19c3231029
commit fd0eac2496
2 changed files with 123 additions and 33 deletions

View File

@ -78,7 +78,7 @@ namespace {
} }
bool runOnBasicBlock(BasicBlock &BB); bool runOnBasicBlock(BasicBlock &BB);
bool MemoryIsNotModifiedBetween(LoadInst *LI, StoreInst *SI); bool MemoryIsNotModifiedBetween(Instruction *FirstI, Instruction *SecondI);
bool HandleFree(CallInst *F); bool HandleFree(CallInst *F);
bool handleEndBlock(BasicBlock &BB); bool handleEndBlock(BasicBlock &BB);
void RemoveAccessedObjects(const MemoryLocation &LoadedLoc, void RemoveAccessedObjects(const MemoryLocation &LoadedLoc,
@ -483,6 +483,7 @@ static bool isPossibleSelfRead(Instruction *Inst,
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
bool DSE::runOnBasicBlock(BasicBlock &BB) { bool DSE::runOnBasicBlock(BasicBlock &BB) {
const DataLayout &DL = BB.getModule()->getDataLayout();
bool MadeChange = false; bool MadeChange = false;
// Do a top-down walk on the BB. // Do a top-down walk on the BB.
@ -502,6 +503,22 @@ bool DSE::runOnBasicBlock(BasicBlock &BB) {
// If we're storing the same value back to a pointer that we just // If we're storing the same value back to a pointer that we just
// loaded from, then the store can be removed. // loaded from, then the store can be removed.
if (StoreInst *SI = dyn_cast<StoreInst>(Inst)) { if (StoreInst *SI = dyn_cast<StoreInst>(Inst)) {
auto RemoveDeadInstAndUpdateBBI = [&](Instruction *DeadInst) {
// DeleteDeadInstruction can delete the current instruction. Save BBI
// in case we need it.
WeakVH NextInst(BBI);
DeleteDeadInstruction(DeadInst, *MD, *TLI);
if (!NextInst) // Next instruction deleted.
BBI = BB.begin();
else if (BBI != BB.begin()) // Revisit this instruction if possible.
--BBI;
++NumRedundantStores;
MadeChange = true;
};
if (LoadInst *DepLoad = dyn_cast<LoadInst>(SI->getValueOperand())) { if (LoadInst *DepLoad = dyn_cast<LoadInst>(SI->getValueOperand())) {
if (SI->getPointerOperand() == DepLoad->getPointerOperand() && if (SI->getPointerOperand() == DepLoad->getPointerOperand() &&
isRemovable(SI) && isRemovable(SI) &&
@ -510,18 +527,26 @@ bool DSE::runOnBasicBlock(BasicBlock &BB) {
DEBUG(dbgs() << "DSE: Remove Store Of Load from same pointer:\n " DEBUG(dbgs() << "DSE: Remove Store Of Load from same pointer:\n "
<< "LOAD: " << *DepLoad << "\n STORE: " << *SI << '\n'); << "LOAD: " << *DepLoad << "\n STORE: " << *SI << '\n');
// DeleteDeadInstruction can delete the current instruction. Save BBI RemoveDeadInstAndUpdateBBI(SI);
// in case we need it. continue;
WeakVH NextInst(BBI); }
}
DeleteDeadInstruction(SI, *MD, *TLI); // Remove null stores into the calloc'ed objects
Constant *StoredConstant = dyn_cast<Constant>(SI->getValueOperand());
if (!NextInst) // Next instruction deleted. if (StoredConstant && StoredConstant->isNullValue() &&
BBI = BB.begin(); isRemovable(SI)) {
else if (BBI != BB.begin()) // Revisit this instruction if possible. Instruction *UnderlyingPointer = dyn_cast<Instruction>(
--BBI; GetUnderlyingObject(SI->getPointerOperand(), DL));
++NumRedundantStores;
MadeChange = true; if (UnderlyingPointer && isCallocLikeFn(UnderlyingPointer, TLI) &&
MemoryIsNotModifiedBetween(UnderlyingPointer, SI)) {
DEBUG(dbgs()
<< "DSE: Remove null store to the calloc'ed object:\n DEAD: "
<< *Inst << "\n OBJECT: " << *UnderlyingPointer << '\n');
RemoveDeadInstAndUpdateBBI(SI);
continue; continue;
} }
} }
@ -561,7 +586,6 @@ bool DSE::runOnBasicBlock(BasicBlock &BB) {
if (isRemovable(DepWrite) && if (isRemovable(DepWrite) &&
!isPossibleSelfRead(Inst, Loc, DepWrite, *TLI, *AA)) { !isPossibleSelfRead(Inst, Loc, DepWrite, *TLI, *AA)) {
int64_t InstWriteOffset, DepWriteOffset; int64_t InstWriteOffset, DepWriteOffset;
const DataLayout &DL = BB.getModule()->getDataLayout();
OverwriteResult OR = OverwriteResult OR =
isOverwrite(Loc, DepLoc, DL, *TLI, DepWriteOffset, InstWriteOffset); isOverwrite(Loc, DepLoc, DL, *TLI, DepWriteOffset, InstWriteOffset);
if (OR == OverwriteComplete) { if (OR == OverwriteComplete) {
@ -633,52 +657,53 @@ bool DSE::runOnBasicBlock(BasicBlock &BB) {
return MadeChange; return MadeChange;
} }
/// Returns true if the memory which is accessed by the store instruction is not /// Returns true if the memory which is accessed by the second instruction is not
/// modified between the load and the store instruction. /// modified between the first and the second instruction.
/// Precondition: The store instruction must be dominated by the load /// Precondition: Second instruction must be dominated by the first
/// instruction. /// instruction.
bool DSE::MemoryIsNotModifiedBetween(LoadInst *LI, StoreInst *SI) { bool DSE::MemoryIsNotModifiedBetween(Instruction *FirstI,
Instruction *SecondI) {
SmallVector<BasicBlock *, 16> WorkList; SmallVector<BasicBlock *, 16> WorkList;
SmallPtrSet<BasicBlock *, 8> Visited; SmallPtrSet<BasicBlock *, 8> Visited;
BasicBlock::iterator LoadBBI(LI); BasicBlock::iterator FirstBBI(FirstI);
++LoadBBI; ++FirstBBI;
BasicBlock::iterator StoreBBI(SI); BasicBlock::iterator SecondBBI(SecondI);
BasicBlock *LoadBB = LI->getParent(); BasicBlock *FirstBB = FirstI->getParent();
BasicBlock *StoreBB = SI->getParent(); BasicBlock *SecondBB = SecondI->getParent();
MemoryLocation StoreLoc = MemoryLocation::get(SI); MemoryLocation MemLoc = MemoryLocation::get(SecondI);
// Start checking the store-block. // Start checking the store-block.
WorkList.push_back(StoreBB); WorkList.push_back(SecondBB);
bool isFirstBlock = true; bool isFirstBlock = true;
// Check all blocks going backward until we reach the load-block. // Check all blocks going backward until we reach the load-block.
while (!WorkList.empty()) { while (!WorkList.empty()) {
BasicBlock *B = WorkList.pop_back_val(); BasicBlock *B = WorkList.pop_back_val();
// Ignore instructions before LI if this is the LoadBB. // Ignore instructions before LI if this is the FirstBB.
BasicBlock::iterator BI = (B == LoadBB ? LoadBBI : B->begin()); BasicBlock::iterator BI = (B == FirstBB ? FirstBBI : B->begin());
BasicBlock::iterator EI; BasicBlock::iterator EI;
if (isFirstBlock) { if (isFirstBlock) {
// Ignore instructions after SI if this is the first visit of StoreBB. // Ignore instructions after SI if this is the first visit of SecondBB.
assert(B == StoreBB && "first block is not the store block"); assert(B == SecondBB && "first block is not the store block");
EI = StoreBBI; EI = SecondBBI;
isFirstBlock = false; isFirstBlock = false;
} else { } else {
// It's not StoreBB or (in case of a loop) the second visit of StoreBB. // It's not SecondBB or (in case of a loop) the second visit of SecondBB.
// In this case we also have to look at instructions after SI. // In this case we also have to look at instructions after SI.
EI = B->end(); EI = B->end();
} }
for (; BI != EI; ++BI) { for (; BI != EI; ++BI) {
Instruction *I = BI; Instruction *I = BI;
if (I->mayWriteToMemory() && I != SI) { if (I->mayWriteToMemory() && I != SecondI) {
auto Res = AA->getModRefInfo(I, StoreLoc); auto Res = AA->getModRefInfo(I, MemLoc);
if (Res != MRI_NoModRef) if (Res != MRI_NoModRef)
return false; return false;
} }
} }
if (B != LoadBB) { if (B != FirstBB) {
assert(B != &LoadBB->getParent()->getEntryBlock() && assert(B != &FirstBB->getParent()->getEntryBlock() &&
"Should not hit the entry block because SI must be dominated by LI"); "Should not hit the entry block because SI must be dominated by LI");
for (auto PredI = pred_begin(B), PE = pred_end(B); PredI != PE; ++PredI) { for (auto PredI = pred_begin(B), PE = pred_end(B); PredI != PE; ++PredI) {
if (!Visited.insert(*PredI).second) if (!Visited.insert(*PredI).second)

View File

@ -0,0 +1,65 @@
; RUN: opt < %s -basicaa -dse -S | FileCheck %s
declare noalias i8* @calloc(i64, i64)
define i32* @test1() {
; CHECK-LABEL: test1
%1 = tail call noalias i8* @calloc(i64 1, i64 4)
%2 = bitcast i8* %1 to i32*
; This store is dead and should be removed
store i32 0, i32* %2, align 4
; CHECK-NOT: store i32 0, i32* %2, align 4
ret i32* %2
}
define i32* @test2() {
; CHECK-LABEL: test2
%1 = tail call noalias i8* @calloc(i64 1, i64 4)
%2 = bitcast i8* %1 to i32*
%3 = getelementptr i32, i32* %2, i32 5
store i32 0, i32* %3, align 4
; CHECK-NOT: store i32 0, i32* %2, align 4
ret i32* %2
}
define i32* @test3(i32 *%arg) {
; CHECK-LABEL: test3
store i32 0, i32* %arg, align 4
; CHECK: store i32 0, i32* %arg, align 4
ret i32* %arg
}
declare void @clobber_memory(i8*)
define i8* @test4() {
; CHECK-LABEL: test4
%1 = tail call noalias i8* @calloc(i64 1, i64 4)
call void @clobber_memory(i8* %1)
store i8 0, i8* %1, align 4
; CHECK: store i8 0, i8* %1, align 4
ret i8* %1
}
define i32* @test5() {
; CHECK-LABEL: test5
%1 = tail call noalias i8* @calloc(i64 1, i64 4)
%2 = bitcast i8* %1 to i32*
store volatile i32 0, i32* %2, align 4
; CHECK: store volatile i32 0, i32* %2, align 4
ret i32* %2
}
define i8* @test6() {
; CHECK-LABEL: test6
%1 = tail call noalias i8* @calloc(i64 1, i64 4)
store i8 5, i8* %1, align 4
; CHECK: store i8 5, i8* %1, align 4
ret i8* %1
}
define i8* @test7(i8 %arg) {
; CHECK-LABEL: test7
%1 = tail call noalias i8* @calloc(i64 1, i64 4)
store i8 %arg, i8* %1, align 4
; CHECK: store i8 %arg, i8* %1, align 4
ret i8* %1
}