[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