[llvm-commits] CVS: llvm/lib/Transforms/IPO/ArgumentPromotion.cpp

Chris Lattner lattner at cs.uiuc.edu
Sun Mar 7 19:05:01 PST 2004


Changes in directory llvm/lib/Transforms/IPO:

ArgumentPromotion.cpp updated: 1.4 -> 1.5

---
Log message:

Implement ArgumentPromotion/aggregate-promote.ll

This allows pointers to aggregate objects, whose elements are only read, to
be promoted and passed in by element instead of by reference.  This can
enable a LOT of subsequent optimizations in the caller function.

It's worth pointing out that this stuff happens a LOT of C++ programs, because
objects in templates are generally passed around by reference.  When these 
templates are instantiated on small aggregate or scalar types, however, it is 
more efficient to pass them in by value than by reference.

This transformation triggers most on C++ codes (e.g. 334 times on eon), but
does happen on C codes as well.  For example, on mesa it triggers 72 times,
and on gcc it triggers 35 times.  this is amazingly good considering that
we are using 'basicaa' so far.



---
Diffs of the changes:  (+145 -25)

Index: llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
diff -u llvm/lib/Transforms/IPO/ArgumentPromotion.cpp:1.4 llvm/lib/Transforms/IPO/ArgumentPromotion.cpp:1.5
--- llvm/lib/Transforms/IPO/ArgumentPromotion.cpp:1.4	Sun Mar  7 16:52:53 2004
+++ llvm/lib/Transforms/IPO/ArgumentPromotion.cpp	Sun Mar  7 19:04:36 2004
@@ -15,13 +15,16 @@
 // of code, and lead to the elimination of allocas, especially in C++ template
 // code like the STL.
 //
+// This pass also handles aggregate arguments that are passed into a function,
+// scalarizing them if the elements of the aggregate are only loaded.  Note that
+// we refuse to scalarize aggregates which would require passing in more than
+// three operands to the function, because we don't want to pass thousands of
+// operands for a large array or something!
+//
 // Note that this transformation could also be done for arguments that are only
 // stored to (returning the value instead), but we do not currently handle that
-// case.
-//
-// Note that we should be able to promote pointers to structures that are only
-// loaded from as well.  The danger is creating way to many arguments, so this
-// transformation should be limited to 3 element structs or something.
+// case.  This case would be best handled when and if we start supporting
+// multiple return values from functions.
 //
 //===----------------------------------------------------------------------===//
 
@@ -38,12 +41,15 @@
 #include "Support/Debug.h"
 #include "Support/DepthFirstIterator.h"
 #include "Support/Statistic.h"
+#include "Support/StringExtras.h"
 #include <set>
 using namespace llvm;
 
 namespace {
   Statistic<> NumArgumentsPromoted("argpromotion",
                                    "Number of pointer arguments promoted");
+  Statistic<> NumAggregatesPromoted("argpromotion",
+                                    "Number of aggregate arguments promoted");
   Statistic<> NumArgumentsDead("argpromotion",
                                "Number of dead pointer args eliminated");
 
@@ -166,20 +172,57 @@
 }
 
 bool ArgPromotion::isSafeToPromoteArgument(Argument *Arg) const {
-  // We can only promote this argument if all of the uses are loads...
+  // We can only promote this argument if all of the uses are loads, or are GEP
+  // instructions (with constant indices) that are subsequently loaded.
   std::vector<LoadInst*> Loads;
+  std::vector<std::vector<Constant*> > GEPIndices;
   for (Value::use_iterator UI = Arg->use_begin(), E = Arg->use_end();
        UI != E; ++UI)
     if (LoadInst *LI = dyn_cast<LoadInst>(*UI)) {
       if (LI->isVolatile()) return false;  // Don't hack volatile loads
       Loads.push_back(LI);
-    } else
-      return false;
+    } else if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(*UI)) {
+      if (GEP->use_empty()) {
+        // Dead GEP's cause trouble later.  Just remove them if we run into
+        // them.
+        GEP->getParent()->getInstList().erase(GEP);
+        return isSafeToPromoteArgument(Arg);
+      }
+      // Ensure that all of the indices are constants.
+      std::vector<Constant*> Operands;
+      for (unsigned i = 1, e = GEP->getNumOperands(); i != e; ++i)
+        if (Constant *C = dyn_cast<Constant>(GEP->getOperand(i)))
+          Operands.push_back(C);
+        else
+          return false;  // Not a constant operand GEP!
+
+      // Ensure that the only users of the GEP are load instructions.
+      for (Value::use_iterator UI = GEP->use_begin(), E = GEP->use_end();
+           UI != E; ++UI)
+        if (LoadInst *LI = dyn_cast<LoadInst>(*UI)) {
+          if (LI->isVolatile()) return false;  // Don't hack volatile loads
+          Loads.push_back(LI);
+        } else {
+          return false;
+        }
 
-  if (Loads.empty()) return true;  // No users, dead argument.
+      // See if there is already a GEP with these indices.  If so, check to make
+      // sure that we aren't promoting too many elements.  If not, nothing to
+      // do.
+      if (std::find(GEPIndices.begin(), GEPIndices.end(), Operands) ==
+          GEPIndices.end()) {
+        if (GEPIndices.size() == 3) {
+          // We limit aggregate promotion to only promoting up to three elements
+          // of the aggregate.
+          return false;
+        }
+        GEPIndices.push_back(Operands);
+      }
+    } else {
+      return false;  // Not a load or a GEP.
+    }
 
-  const Type *LoadTy = cast<PointerType>(Arg->getType())->getElementType();
-  unsigned LoadSize = getAnalysis<TargetData>().getTypeSize(LoadTy);
+  if (Loads.empty()) return true;  // No users, dead argument.
 
   // Okay, now we know that the argument is only used by load instructions.
   // Check to see if the pointer is guaranteed to not be modified from entry of
@@ -191,12 +234,18 @@
   std::set<BasicBlock*> TranspBlocks;
 
   AliasAnalysis &AA = getAnalysis<AliasAnalysis>();
+  TargetData &TD = getAnalysis<TargetData>();
 
   for (unsigned i = 0, e = Loads.size(); i != e; ++i) {
     // Check to see if the load is invalidated from the start of the block to
     // the load itself.
     LoadInst *Load = Loads[i];
     BasicBlock *BB = Load->getParent();
+
+    const PointerType *LoadTy =
+      cast<PointerType>(Load->getOperand(0)->getType());
+    unsigned LoadSize = TD.getTypeSize(LoadTy->getElementType());
+
     if (AA.canInstructionRangeModify(BB->front(), *Load, Arg, LoadSize))
       return false;  // Pointer is invalidated!
 
@@ -225,12 +274,39 @@
   const FunctionType *FTy = F->getFunctionType();
   std::vector<const Type*> Params;
 
+  // ScalarizedElements - If we are promoting a pointer that has elements
+  // accessed out of it, keep track of which elements are accessed so that we
+  // can add one argument for each.
+  //
+  // Arguments that are directly loaded will have a zero element value here, to
+  // handle cases where there are both a direct load and GEP accesses.
+  //
+  std::map<Argument*, std::set<std::vector<Value*> > > ScalarizedElements;
+
   for (Function::aiterator I = F->abegin(), E = F->aend(); I != E; ++I)
     if (!ArgsToPromote.count(I)) {
       Params.push_back(I->getType());
     } else if (!I->use_empty()) {
-      Params.push_back(cast<PointerType>(I->getType())->getElementType());
-      ++NumArgumentsPromoted;
+      // Okay, this is being promoted.  Check to see if there are any GEP uses
+      // of the argument.
+      std::set<std::vector<Value*> > &ArgIndices = ScalarizedElements[I];
+      for (Value::use_iterator UI = I->use_begin(), E = I->use_end(); UI != E;
+           ++UI) {
+        Instruction *User = cast<Instruction>(*UI);
+        assert(isa<LoadInst>(User) || isa<GetElementPtrInst>(User));
+        ArgIndices.insert(std::vector<Value*>(User->op_begin()+1,
+                                              User->op_end()));
+      }
+
+      // Add a parameter to the function for each element passed in.
+      for (std::set<std::vector<Value*> >::iterator SI = ArgIndices.begin(),
+             E = ArgIndices.end(); SI != E; ++SI)
+        Params.push_back(GetElementPtrInst::getIndexedType(I->getType(), *SI));
+
+      if (ArgIndices.size() == 1 && ArgIndices.begin()->empty())
+        ++NumArgumentsPromoted;
+      else
+        ++NumAggregatesPromoted;
     } else {
       ++NumArgumentsDead;
     }
@@ -268,8 +344,16 @@
       if (!ArgsToPromote.count(I))
         Args.push_back(*AI);          // Unmodified argument
       else if (!I->use_empty()) {
-        // Non-dead instruction
-        Args.push_back(new LoadInst(*AI, (*AI)->getName()+".val", Call));
+        // Non-dead argument.
+        std::set<std::vector<Value*> > &ArgIndices = ScalarizedElements[I];
+        for (std::set<std::vector<Value*> >::iterator SI = ArgIndices.begin(),
+               E = ArgIndices.end(); SI != E; ++SI) {
+          Value *V = *AI;
+          if (!SI->empty())
+            V = new GetElementPtrInst(V, *SI, V->getName()+".idx", Call);
+
+          Args.push_back(new LoadInst(V, V->getName()+".val", Call));
+        }
       }
 
     if (ExtraArgHack)
@@ -320,20 +404,56 @@
       // Otherwise, if we promoted this argument, then all users are load
       // instructions, and all loads should be using the new argument that we
       // added.
-      DEBUG(std::cerr << "*** Promoted argument '" << I->getName()
-                      << "' of function '" << F->getName() << "'\n");
-      I2->setName(I->getName()+".val");
+      std::set<std::vector<Value*> > &ArgIndices = ScalarizedElements[I];
+
       while (!I->use_empty()) {
-        LoadInst *LI = cast<LoadInst>(I->use_back());
-        LI->replaceAllUsesWith(I2);
-        LI->getParent()->getInstList().erase(LI);
+        if (LoadInst *LI = dyn_cast<LoadInst>(I->use_back())) {
+          assert(ArgIndices.begin()->empty() &&
+                 "Load element should sort to front!");
+          I2->setName(I->getName()+".val");
+          LI->replaceAllUsesWith(I2);
+          LI->getParent()->getInstList().erase(LI);
+          DEBUG(std::cerr << "*** Promoted argument '" << I->getName()
+                          << "' of function '" << F->getName() << "'\n");
+        } else {
+          GetElementPtrInst *GEP = cast<GetElementPtrInst>(I->use_back());
+          std::vector<Value*> Operands(GEP->op_begin()+1, GEP->op_end());
+
+          unsigned ArgNo = 0;
+          Function::aiterator TheArg = I2;
+          for (std::set<std::vector<Value*> >::iterator It = ArgIndices.begin();
+               *It != Operands; ++It, ++TheArg) {
+            assert(It != ArgIndices.end() && "GEP not handled??");
+          }
+
+          std::string NewName = I->getName();
+          for (unsigned i = 0, e = Operands.size(); i != e; ++i)
+            if (ConstantInt *CI = dyn_cast<ConstantInt>(Operands[i]))
+              NewName += "."+itostr((int64_t)CI->getRawValue());
+            else
+              NewName += ".x";
+          TheArg->setName(NewName+".val");
+
+          DEBUG(std::cerr << "*** Promoted agg argument '" << TheArg->getName()
+                          << "' of function '" << F->getName() << "'\n");
+
+          // All of the uses must be load instructions.  Replace them all with
+          // the argument specified by ArgNo.
+          while (!GEP->use_empty()) {
+            LoadInst *L = cast<LoadInst>(GEP->use_back());
+            L->replaceAllUsesWith(TheArg);
+            L->getParent()->getInstList().erase(L);
+          }
+          GEP->getParent()->getInstList().erase(GEP);
+        }
       }
 
       // If we inserted a new pointer type, it's possible that IT could be
-      // promoted too.
-      if (isa<PointerType>(I2->getType()))
-        WorkList.insert(NF);
-      ++I2;
+      // promoted too.  Also, increment I2 past all of the arguments for this
+      // pointer.
+      for (unsigned i = 0, e = ArgIndices.size(); i != e; ++i, ++I2)
+        if (isa<PointerType>(I2->getType()))
+          WorkList.insert(NF);
     }
 
   // Now that the old function is dead, delete it.





More information about the llvm-commits mailing list