[llvm-commits] [llvm] r122727 - in /llvm/trunk: lib/Transforms/Scalar/EarlyCSE.cpp test/Transforms/EarlyCSE/basic.ll
Chris Lattner
sabre at nondot.org
Sun Jan 2 19:18:43 PST 2011
Author: lattner
Date: Sun Jan 2 21:18:43 2011
New Revision: 122727
URL: http://llvm.org/viewvc/llvm-project?rev=122727&view=rev
Log:
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.
Modified:
llvm/trunk/lib/Transforms/Scalar/EarlyCSE.cpp
llvm/trunk/test/Transforms/EarlyCSE/basic.ll
Modified: llvm/trunk/lib/Transforms/Scalar/EarlyCSE.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Scalar/EarlyCSE.cpp?rev=122727&r1=122726&r2=122727&view=diff
==============================================================================
--- llvm/trunk/lib/Transforms/Scalar/EarlyCSE.cpp (original)
+++ llvm/trunk/lib/Transforms/Scalar/EarlyCSE.cpp Sun Jan 2 21:18:43 2011
@@ -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 @@
};
}
-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 @@
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 @@
/// 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 @@
// 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 @@
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 @@
ScopedHTType AVTable;
AvailableValues = &AVTable;
+ MemHTType MemTable;
+ AvailableMemValues = &MemTable;
+
+ CurrentGeneration = 0;
return processNode(DT->getRootNode());
}
Modified: llvm/trunk/test/Transforms/EarlyCSE/basic.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/EarlyCSE/basic.ll?rev=122727&r1=122726&r2=122727&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/EarlyCSE/basic.ll (original)
+++ llvm/trunk/test/Transforms/EarlyCSE/basic.ll Sun Jan 2 21:18:43 2011
@@ -30,3 +30,48 @@
; CHECK-NEXT: volatile store i32 %G
ret void
}
+
+
+;; Simple load value numbering.
+; CHECK: @test2
+define i32 @test2(i32 *%P) {
+ %V1 = load i32* %P
+ %V2 = load i32* %P
+ %Diff = sub i32 %V1, %V2
+ ret i32 %Diff
+ ; CHECK: ret i32 0
+}
+
+;; Cross block load value numbering.
+; CHECK: @test3
+define i32 @test3(i32 *%P, i1 %Cond) {
+ %V1 = load i32* %P
+ br i1 %Cond, label %T, label %F
+T:
+ store i32 4, i32* %P
+ ret i32 42
+F:
+ %V2 = load i32* %P
+ %Diff = sub i32 %V1, %V2
+ ret i32 %Diff
+ ; CHECK: F:
+ ; CHECK: ret i32 0
+}
+
+;; Cross block load value numbering stops when stores happen.
+; CHECK: @test4
+define i32 @test4(i32 *%P, i1 %Cond) {
+ %V1 = load i32* %P
+ br i1 %Cond, label %T, label %F
+T:
+ ret i32 42
+F:
+ ; Clobbers V1
+ store i32 42, i32* %P
+
+ %V2 = load i32* %P
+ %Diff = sub i32 %V1, %V2
+ ret i32 %Diff
+ ; CHECK: F:
+ ; CHECK: ret i32 %Diff
+}
More information about the llvm-commits
mailing list