1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-01-31 20:51:52 +01:00

Value number stores and memory states so we can detect when memory states are equivalent (IE store of same value to memory).

Reviewers: davide

Subscribers: llvm-commits

Differential Revision: https://reviews.llvm.org/D28084

llvm-svn: 290525
This commit is contained in:
Daniel Berlin 2016-12-25 22:23:49 +00:00
parent cde24fcdff
commit cf6a330da2
2 changed files with 219 additions and 20 deletions

View File

@ -27,6 +27,7 @@
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SparseBitVector.h"
@ -184,6 +185,13 @@ class NewGVN : public FunctionPass {
DenseMap<Value *, CongruenceClass *> ValueToClass;
DenseMap<Value *, const Expression *> ValueToExpression;
// A table storing which memorydefs/phis represent a memory state provably
// equivalent to another memory state.
// We could use the congruence class machinery, but the MemoryAccess's are
// abstract memory states, so they can only ever be equivalent to each other,
// and not to constants, etc.
DenseMap<MemoryAccess *, MemoryAccess *> MemoryAccessEquiv;
// Expression to class mapping.
typedef DenseMap<const Expression *, CongruenceClass *> ExpressionClassMap;
ExpressionClassMap ExpressionToClass;
@ -219,7 +227,7 @@ class NewGVN : public FunctionPass {
// DFS info.
DenseMap<const BasicBlock *, std::pair<int, int>> DFSDomMap;
DenseMap<const Value *, unsigned> InstrDFS;
std::vector<Instruction *> DFSToInstr;
std::vector<Value *> DFSToInstr;
// Deletion info.
SmallPtrSet<Instruction *, 8> InstructionsToErase;
@ -284,6 +292,10 @@ private:
}
void initializeCongruenceClasses(Function &F);
// Value number an Instruction or MemoryPhi.
void valueNumberMemoryPhi(MemoryPhi *);
void valueNumberInstruction(Instruction *);
// Symbolic evaluation.
const Expression *checkSimplificationResults(Expression *, Instruction *,
Value *);
@ -296,6 +308,7 @@ private:
const BasicBlock *);
const Expression *performSymbolicPHIEvaluation(Instruction *,
const BasicBlock *);
bool setMemoryAccessEquivTo(MemoryAccess *From, MemoryAccess *To);
const Expression *performSymbolicAggrValueEvaluation(Instruction *,
const BasicBlock *);
@ -310,6 +323,7 @@ private:
void processOutgoingEdges(TerminatorInst *, BasicBlock *);
bool isOnlyReachableViaThisEdge(const BasicBlockEdge &) const;
Value *findConditionEquivalence(Value *, BasicBlock *) const;
MemoryAccess *lookupMemoryAccessEquiv(MemoryAccess *) const;
// Elimination.
struct ValueDFS;
@ -663,6 +677,11 @@ Value *NewGVN::lookupOperandLeader(Value *V, const User *U,
return V;
}
MemoryAccess *NewGVN::lookupMemoryAccessEquiv(MemoryAccess *MA) const {
MemoryAccess *Result = MemoryAccessEquiv.lookup(MA);
return Result ? Result : MA;
}
LoadExpression *NewGVN::createLoadExpression(Type *LoadType, Value *PointerOp,
LoadInst *LI, MemoryAccess *DA,
const BasicBlock *B) {
@ -704,8 +723,22 @@ const StoreExpression *NewGVN::createStoreExpression(StoreInst *SI,
const Expression *NewGVN::performSymbolicStoreEvaluation(Instruction *I,
const BasicBlock *B) {
StoreInst *SI = cast<StoreInst>(I);
const Expression *E = createStoreExpression(SI, MSSA->getMemoryAccess(SI), B);
return E;
// If this store's memorydef stores the same value as the last store, the
// memory accesses are equivalent.
// Get the expression, if any, for the RHS of the MemoryDef.
MemoryAccess *StoreAccess = MSSA->getMemoryAccess(SI);
MemoryAccess *StoreRHS = lookupMemoryAccessEquiv(
cast<MemoryDef>(StoreAccess)->getDefiningAccess());
const Expression *OldStore = createStoreExpression(SI, StoreRHS, B);
// See if this store expression already has a value, and it's the same as our
// current store.
CongruenceClass *CC = ExpressionToClass.lookup(OldStore);
if (CC &&
CC->RepLeader == lookupOperandLeader(SI->getValueOperand(), SI, B)) {
setMemoryAccessEquivTo(StoreAccess, StoreRHS);
return OldStore;
}
return createStoreExpression(SI, StoreAccess, B);
}
const Expression *NewGVN::performSymbolicLoadEvaluation(Instruction *I,
@ -734,8 +767,9 @@ const Expression *NewGVN::performSymbolicLoadEvaluation(Instruction *I,
}
}
const Expression *E = createLoadExpression(
LI->getType(), LI->getPointerOperand(), LI, DefiningAccess, B);
const Expression *E =
createLoadExpression(LI->getType(), LI->getPointerOperand(), LI,
lookupMemoryAccessEquiv(DefiningAccess), B);
return E;
}
@ -752,6 +786,29 @@ const Expression *NewGVN::performSymbolicCallEvaluation(Instruction *I,
return nullptr;
}
// Update the memory access equivalence table to say that From is equal to To,
// and return true if this is different from what already existed in the table.
bool NewGVN::setMemoryAccessEquivTo(MemoryAccess *From, MemoryAccess *To) {
auto LookupResult = MemoryAccessEquiv.insert({From, nullptr});
bool Changed = false;
// If it's already in the table, see if the value changed.
if (LookupResult.second) {
if (To && LookupResult.first->second != To) {
// It wasn't equivalent before, and now it is.
LookupResult.first->second = To;
Changed = true;
} else if (!To) {
// It used to be equivalent to something, and now it's not.
MemoryAccessEquiv.erase(LookupResult.first);
Changed = true;
}
} else if (To) {
// It wasn't in the table, but is equivalent to something.
LookupResult.first->second = To;
Changed = true;
}
return Changed;
}
// Evaluate PHI nodes symbolically, and create an expression result.
const Expression *NewGVN::performSymbolicPHIEvaluation(Instruction *I,
const BasicBlock *B) {
@ -1214,11 +1271,17 @@ void NewGVN::cleanupTables() {
BlockInstRange.clear();
TouchedInstructions.clear();
DominatedInstRange.clear();
MemoryAccessEquiv.clear();
}
std::pair<unsigned, unsigned> NewGVN::assignDFSNumbers(BasicBlock *B,
unsigned Start) {
unsigned End = Start;
if (MemoryAccess *MemPhi = MSSA->getMemoryAccess(B)) {
InstrDFS[MemPhi] = End++;
DFSToInstr.emplace_back(MemPhi);
}
for (auto &I : *B) {
InstrDFS[&I] = End++;
DFSToInstr.emplace_back(&I);
@ -1241,6 +1304,61 @@ void NewGVN::updateProcessedCount(Value *V) {
}
#endif
}
// Evaluate MemoryPhi nodes symbolically, just like PHI nodes
void NewGVN::valueNumberMemoryPhi(MemoryPhi *MP) {
// If all the arguments are the same, the MemoryPhi has the same value as the
// argument.
// Filter out unreachable blocks from our operands.
auto Filtered = make_filter_range(MP->operands(), [&](const Use &U) {
return ReachableBlocks.count(MP->getIncomingBlock(U));
});
assert(Filtered.begin() != Filtered.end() &&
"We should not be processing a MemoryPhi in a completely "
"unreachable block");
// Transform the remaining operands into operand leaders.
// FIXME: mapped_iterator should have a range version.
auto LookupFunc = [&](const Use &U) {
return lookupMemoryAccessEquiv(cast<MemoryAccess>(U));
};
auto MappedBegin = map_iterator(Filtered.begin(), LookupFunc);
auto MappedEnd = map_iterator(Filtered.end(), LookupFunc);
// and now check if all the elements are equal.
// Sadly, we can't use std::equals since these are random access iterators.
MemoryAccess *AllSameValue = *MappedBegin;
++MappedBegin;
bool AllEqual = std::all_of(
MappedBegin, MappedEnd,
[&AllSameValue](const MemoryAccess *V) { return V == AllSameValue; });
if (AllEqual)
DEBUG(dbgs() << "Memory Phi value numbered to " << *AllSameValue << "\n");
else
DEBUG(dbgs() << "Memory Phi value numbered to itself\n");
if (setMemoryAccessEquivTo(MP, AllEqual ? AllSameValue : nullptr))
markMemoryUsersTouched(MP);
}
// Value number a single instruction, symbolically evaluating, performing
// congruence finding, and updating mappings.
void NewGVN::valueNumberInstruction(Instruction *I) {
DEBUG(dbgs() << "Processing instruction " << *I << "\n");
if (I->use_empty() && !I->getType()->isVoidTy()) {
DEBUG(dbgs() << "Skipping unused instruction\n");
if (isInstructionTriviallyDead(I, TLI))
markInstructionForDeletion(I);
return;
}
if (!I->isTerminator()) {
const Expression *Symbolized = performSymbolicEvaluation(I, I->getParent());
performCongruenceFinding(I, Symbolized);
} else {
processOutgoingEdges(dyn_cast<TerminatorInst>(I), I->getParent());
}
}
// This is the main transformation entry point.
bool NewGVN::runGVN(Function &F, DominatorTree *_DT, AssumptionCache *_AC,
@ -1304,8 +1422,15 @@ bool NewGVN::runGVN(Function &F, DominatorTree *_DT, AssumptionCache *_AC,
// Walk through all the instructions in all the blocks in RPO.
for (int InstrNum = TouchedInstructions.find_first(); InstrNum != -1;
InstrNum = TouchedInstructions.find_next(InstrNum)) {
Instruction *I = DFSToInstr[InstrNum];
BasicBlock *CurrBlock = I->getParent();
Value *V = DFSToInstr[InstrNum];
BasicBlock *CurrBlock = nullptr;
if (Instruction *I = dyn_cast<Instruction>(V))
CurrBlock = I->getParent();
else if (MemoryPhi *MP = dyn_cast<MemoryPhi>(V))
CurrBlock = MP->getBlock();
else
llvm_unreachable("DFSToInstr gave us an unknown type of instruction");
// If we hit a new block, do reachability processing.
if (CurrBlock != LastBlock) {
@ -1323,22 +1448,16 @@ bool NewGVN::runGVN(Function &F, DominatorTree *_DT, AssumptionCache *_AC,
}
updateProcessedCount(CurrBlock);
}
DEBUG(dbgs() << "Processing instruction " << *I << "\n");
if (I->use_empty() && !I->getType()->isVoidTy()) {
DEBUG(dbgs() << "Skipping unused instruction\n");
if (isInstructionTriviallyDead(I, TLI))
markInstructionForDeletion(I);
TouchedInstructions.reset(InstrNum);
continue;
}
updateProcessedCount(I);
if (!I->isTerminator()) {
const Expression *Symbolized = performSymbolicEvaluation(I, CurrBlock);
performCongruenceFinding(I, Symbolized);
if (MemoryPhi *MP = dyn_cast<MemoryPhi>(V)) {
DEBUG(dbgs() << "Processing MemoryPhi " << *MP << "\n");
valueNumberMemoryPhi(MP);
} else if (Instruction *I = dyn_cast<Instruction>(V)) {
valueNumberInstruction(I);
} else {
processOutgoingEdges(dyn_cast<TerminatorInst>(I), CurrBlock);
llvm_unreachable("Should have been a MemoryPhi or Instruction");
}
updateProcessedCount(V);
// Reset after processing (because we may mark ourselves as touched when
// we propagate equalities).
TouchedInstructions.reset(InstrNum);

View File

@ -0,0 +1,80 @@
; RUN: opt -newgvn -S < %s | FileCheck %s
; RUN: opt -passes=newgvn -S -o - %s | FileCheck %s
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
;; All the loads in this testcase are useless, but it requires understanding that repeated
;; stores of the same value do not change the memory state to eliminate them.
define i32 @foo(i32*, i32) {
; CHECK-LABEL: @foo
store i32 5, i32* %0, align 4
%3 = icmp ne i32 %1, 0
br i1 %3, label %4, label %7
; <label>:4: ; preds = %2
; CHECK-NOT: load
%5 = load i32, i32* %0, align 4
; CHECK-NOT: add
%6 = add nsw i32 5, %5
br label %7
; <label>:7: ; preds = %4, %2
%.0 = phi i32 [ %6, %4 ], [ 5, %2 ]
; CHECK: phi i32 [ 10, %4 ], [ 5, %2 ]
store i32 5, i32* %0, align 4
; CHECK-NOT: icmp
%8 = icmp ne i32 %1, 0
; CHECK: br i1 %3
br i1 %8, label %9, label %12
; <label>:9: ; preds = %7
; CHECK-NOT: load
%10 = load i32, i32* %0, align 4
; CHECK: add nsw i32 %.0, 5
%11 = add nsw i32 %.0, %10
br label %12
; <label>:12: ; preds = %9, %7
%.1 = phi i32 [ %11, %9 ], [ %.0, %7 ]
ret i32 %.1
}
;; This is similar to the above, but it is a conditional store of the same value
;; which requires value numbering MemoryPhi properly to resolve.
define i32 @foo2(i32*, i32) {
; CHECK-LABEL: @foo2
store i32 5, i32* %0, align 4
%3 = icmp ne i32 %1, 0
br i1 %3, label %4, label %7
; <label>:4: ; preds = %2
; CHECK-NOT: load
%5 = load i32, i32* %0, align 4
; CHECK-NOT: add
%6 = add nsw i32 5, %5
br label %8
; <label>:7: ; preds = %2
store i32 5, i32* %0, align 4
br label %8
; <label>:8: ; preds = %7, %4
; CHECK: phi i32 [ 10, %4 ], [ 5, %5 ]
%.0 = phi i32 [ %6, %4 ], [ 5, %7 ]
; CHECK-NOT: icmp
%9 = icmp ne i32 %1, 0
; CHECK: br i1 %3
br i1 %9, label %10, label %13
; <label>:10: ; preds = %8
; CHECK-NOT: load
%11 = load i32, i32* %0, align 4
; CHECK: add nsw i32 %.0, 5
%12 = add nsw i32 %.0, %11
br label %13
; <label>:13: ; preds = %10, %8
%.1 = phi i32 [ %12, %10 ], [ %.0, %8 ]
ret i32 %.1
}