summaryrefslogtreecommitdiff
path: root/lib/Transforms/Scalar/EarlyCSE.cpp
diff options
context:
space:
mode:
authorChris Lattner <sabre@nondot.org>2011-01-03 03:18:43 +0000
committerChris Lattner <sabre@nondot.org>2011-01-03 03:18:43 +0000
commit8e7f0d70c7dd3864126c746378a7b928d57f971f (patch)
tree869b2b51e3648bedb947e5093c660db2a9097144 /lib/Transforms/Scalar/EarlyCSE.cpp
parent152096275ad45bb13d5652f7019f48be5ccd67f8 (diff)
downloadllvm-8e7f0d70c7dd3864126c746378a7b928d57f971f.tar.gz
llvm-8e7f0d70c7dd3864126c746378a7b928d57f971f.tar.bz2
llvm-8e7f0d70c7dd3864126c746378a7b928d57f971f.tar.xz
Teach EarlyCSE to do trivial CSE of loads and read-only calls.
On 176.gcc, this catches 13090 loads and calls, and increases the number of simple instructions CSE'd from 29658 to 36208. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@122727 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Transforms/Scalar/EarlyCSE.cpp')
-rw-r--r--lib/Transforms/Scalar/EarlyCSE.cpp174
1 files changed, 152 insertions, 22 deletions
diff --git a/lib/Transforms/Scalar/EarlyCSE.cpp b/lib/Transforms/Scalar/EarlyCSE.cpp
index 7875ca79d1..557cd6d2b5 100644
--- a/lib/Transforms/Scalar/EarlyCSE.cpp
+++ b/lib/Transforms/Scalar/EarlyCSE.cpp
@@ -27,7 +27,12 @@
using namespace llvm;
STATISTIC(NumSimplify, "Number of insts simplified or DCE'd");
-STATISTIC(NumCSE, "Number of insts CSE'd");
+STATISTIC(NumCSE, "Number of insts CSE'd");
+STATISTIC(NumCSEMem, "Number of load and call insts CSE'd");
+
+static unsigned getHash(const void *V) {
+ return DenseMapInfo<const void*>::getHashValue(V);
+}
//===----------------------------------------------------------------------===//
// SimpleValue
@@ -78,10 +83,6 @@ template<> struct DenseMapInfo<SimpleValue> {
};
}
-unsigned getHash(const void *V) {
- return DenseMapInfo<const void*>::getHashValue(V);
-}
-
unsigned DenseMapInfo<SimpleValue>::getHashValue(SimpleValue Val) {
Instruction *Inst = Val.Inst;
@@ -124,9 +125,77 @@ bool DenseMapInfo<SimpleValue>::isEqual(SimpleValue LHS, SimpleValue RHS) {
return LHSI->isIdenticalTo(RHSI);
}
+//===----------------------------------------------------------------------===//
+// MemoryValue
+//===----------------------------------------------------------------------===//
+
+namespace {
+ /// MemoryValue - Instances of this struct represent available load and call
+ /// values in the scoped hash table.
+ struct MemoryValue {
+ Instruction *Inst;
+
+ bool isSentinel() const {
+ return Inst == DenseMapInfo<Instruction*>::getEmptyKey() ||
+ Inst == DenseMapInfo<Instruction*>::getTombstoneKey();
+ }
+
+ static bool canHandle(Instruction *Inst) {
+ if (LoadInst *LI = dyn_cast<LoadInst>(Inst))
+ return !LI->isVolatile();
+ if (CallInst *CI = dyn_cast<CallInst>(Inst))
+ return CI->onlyReadsMemory();
+ return false;
+ }
+
+ static MemoryValue get(Instruction *I) {
+ MemoryValue X; X.Inst = I;
+ assert((X.isSentinel() || canHandle(I)) && "Inst can't be handled!");
+ return X;
+ }
+ };
+}
+
+namespace llvm {
+ // MemoryValue is POD.
+ template<> struct isPodLike<MemoryValue> {
+ static const bool value = true;
+ };
+
+ template<> struct DenseMapInfo<MemoryValue> {
+ static inline MemoryValue getEmptyKey() {
+ return MemoryValue::get(DenseMapInfo<Instruction*>::getEmptyKey());
+ }
+ static inline MemoryValue getTombstoneKey() {
+ return MemoryValue::get(DenseMapInfo<Instruction*>::getTombstoneKey());
+ }
+ static unsigned getHashValue(MemoryValue Val);
+ static bool isEqual(MemoryValue LHS, MemoryValue RHS);
+ };
+}
+unsigned DenseMapInfo<MemoryValue>::getHashValue(MemoryValue Val) {
+ Instruction *Inst = Val.Inst;
+ // Hash in all of the operands as pointers.
+ unsigned Res = 0;
+ for (unsigned i = 0, e = Inst->getNumOperands(); i != e; ++i)
+ Res ^= getHash(Inst->getOperand(i)) << i;
+ // Mix in the opcode.
+ return (Res << 1) ^ Inst->getOpcode();
+}
+
+bool DenseMapInfo<MemoryValue>::isEqual(MemoryValue LHS, MemoryValue RHS) {
+ Instruction *LHSI = LHS.Inst, *RHSI = RHS.Inst;
+
+ if (LHS.isSentinel() || RHS.isSentinel())
+ return LHSI == RHSI;
+
+ if (LHSI->getOpcode() != RHSI->getOpcode()) return false;
+ return LHSI->isIdenticalTo(RHSI);
+}
+
//===----------------------------------------------------------------------===//
-// EarlyCSE pass
+// EarlyCSE pass.
//===----------------------------------------------------------------------===//
namespace {
@@ -152,7 +221,21 @@ public:
/// we find, otherwise we insert them so that dominated values can succeed in
/// their lookup.
ScopedHTType *AvailableValues;
-
+
+ typedef ScopedHashTable<MemoryValue, std::pair<Value*, unsigned> > MemHTType;
+ /// AvailableMemValues - This scoped hash table contains the current values of
+ /// loads and other read-only memory values. This allows us to get efficient
+ /// access to dominating loads we we find a fully redundant load. In addition
+ /// to the most recent load, we keep track of a generation count of the read,
+ /// which is compared against the current generation count. The current
+ /// generation count is incremented after every possibly writing memory
+ /// operation, which ensures that we only CSE loads with other loads that have
+ /// no intervening store.
+ MemHTType *AvailableMemValues;
+
+ /// CurrentGeneration - This is the current generation of the memory value.
+ unsigned CurrentGeneration;
+
static char ID;
explicit EarlyCSE() : FunctionPass(ID) {
initializeEarlyCSEPass(*PassRegistry::getPassRegistry());
@@ -187,11 +270,23 @@ bool EarlyCSE::processNode(DomTreeNode *Node) {
// Define a scope in the scoped hash table. When we are done processing this
// domtree node and recurse back up to our parent domtree node, this will pop
// off all the values we install.
- ScopedHashTableScope<SimpleValue, Value*, DenseMapInfo<SimpleValue>,
- AllocatorTy> Scope(*AvailableValues);
+ ScopedHTType::ScopeTy Scope(*AvailableValues);
+
+ // Define a scope for the memory values so that anything we add will get
+ // popped when we recurse back up to our parent domtree node.
+ MemHTType::ScopeTy MemScope(*AvailableMemValues);
BasicBlock *BB = Node->getBlock();
+ // If this block has a single predecessor, then the predecessor is the parent
+ // of the domtree node and all of the live out memory values are still current
+ // in this block. If this block has multiple predecessors, then they could
+ // have invalidated the live-out memory values of our parent value. For now,
+ // just be conservative and invalidate memory if this block has multiple
+ // predecessors.
+ if (BB->getSinglePredecessor() == 0)
+ ++CurrentGeneration;
+
bool Changed = false;
// See if any instructions in the block can be eliminated. If so, do it. If
@@ -219,27 +314,58 @@ bool EarlyCSE::processNode(DomTreeNode *Node) {
continue;
}
- // If this instruction is something that we can't value number, ignore it.
- if (!SimpleValue::canHandle(Inst))
+ // If this is a simple instruction that we can value number, process it.
+ if (SimpleValue::canHandle(Inst)) {
+ // See if the instruction has an available value. If so, use it.
+ if (Value *V = AvailableValues->lookup(SimpleValue::get(Inst))) {
+ DEBUG(dbgs() << "EarlyCSE CSE: " << *Inst << " to: " << *V << '\n');
+ Inst->replaceAllUsesWith(V);
+ Inst->eraseFromParent();
+ Changed = true;
+ ++NumCSE;
+ continue;
+ }
+
+ // Otherwise, just remember that this value is available.
+ AvailableValues->insert(SimpleValue::get(Inst), Inst);
continue;
+ }
- // See if the instruction has an available value. If so, use it.
- if (Value *V = AvailableValues->lookup(SimpleValue::get(Inst))) {
- DEBUG(dbgs() << "EarlyCSE CSE: " << *Inst << " to: " << *V << '\n');
- Inst->replaceAllUsesWith(V);
- Inst->eraseFromParent();
- Changed = true;
- ++NumCSE;
+ // If this is a read-only memory value, process it.
+ if (MemoryValue::canHandle(Inst)) {
+ // If we have an available version of this value, and if it is the right
+ // generation, replace this instruction.
+ std::pair<Value*, unsigned> InVal =
+ AvailableMemValues->lookup(MemoryValue::get(Inst));
+ if (InVal.first != 0 && InVal.second == CurrentGeneration) {
+ DEBUG(dbgs() << "EarlyCSE CSE MEM: " << *Inst << " to: "
+ << *InVal.first << '\n');
+ if (!Inst->use_empty()) Inst->replaceAllUsesWith(InVal.first);
+ Inst->eraseFromParent();
+ Changed = true;
+ ++NumCSEMem;
+ continue;
+ }
+
+ // Otherwise, remember that we have this instruction.
+ AvailableMemValues->insert(MemoryValue::get(Inst),
+ std::pair<Value*, unsigned>(Inst, CurrentGeneration));
continue;
}
- // Otherwise, just remember that this value is available.
- AvailableValues->insert(SimpleValue::get(Inst), Inst);
+ // Okay, this isn't something we can CSE at all. Check to see if it is
+ // something that could modify memory. If so, our available memory values
+ // cannot be used so bump the generation count.
+ if (Inst->mayWriteToMemory())
+ ++CurrentGeneration;
}
-
- for (DomTreeNode::iterator I = Node->begin(), E = Node->end(); I != E; ++I)
+ unsigned LiveOutGeneration = CurrentGeneration;
+ for (DomTreeNode::iterator I = Node->begin(), E = Node->end(); I != E; ++I) {
Changed |= processNode(*I);
+ // Pop any generation changes off the stack from the recursive walk.
+ CurrentGeneration = LiveOutGeneration;
+ }
return Changed;
}
@@ -250,5 +376,9 @@ bool EarlyCSE::runOnFunction(Function &F) {
ScopedHTType AVTable;
AvailableValues = &AVTable;
+ MemHTType MemTable;
+ AvailableMemValues = &MemTable;
+
+ CurrentGeneration = 0;
return processNode(DT->getRootNode());
}