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

Reid Spencer reid at x10sys.com
Wed Apr 27 00:54:51 PDT 2005



Changes in directory llvm/lib/Transforms/IPO:

SimplifyLibCalls.cpp updated: 1.16 -> 1.17
---
Log message:

This is a cleanup commit:
* Correct stale documentation in a few places
* Re-order the file to better associate things and reduce line count
* Make the pass thread safe by caching the Function* objects needed by the
  optimizers in the pass object instead of globally.
* Provide the SimplifyLibCalls pass object to the optimizer classes so they
  can access cached Function* objects and TargetData info
* Make sure the pass resets its cache if the Module passed to runOnModule
  changes
* Rename CallOptimizer LibCallOptimization. All the classes are named 
  *Optimization while the objects are *Optimizer.
* Don't cache Function* in the optimizer objects because they could be used
  by multiple PassManager's running in multiple threads
* Add an optimization for strcpy which is similar to strcat
* Add a "TODO" list at the end of the file for ideas on additional libcall
  optimizations that could be added (get ideas from other compilers).

Sorry for the huge diff. Its mostly reorganization of code. That won't
happen again as I believe the design and infrastructure for this pass is
now done or close to it.


---
Diffs of the changes:  (+411 -306)

 SimplifyLibCalls.cpp |  717 +++++++++++++++++++++++++++++----------------------
 1 files changed, 411 insertions(+), 306 deletions(-)


Index: llvm/lib/Transforms/IPO/SimplifyLibCalls.cpp
diff -u llvm/lib/Transforms/IPO/SimplifyLibCalls.cpp:1.16 llvm/lib/Transforms/IPO/SimplifyLibCalls.cpp:1.17
--- llvm/lib/Transforms/IPO/SimplifyLibCalls.cpp:1.16	Tue Apr 26 19:20:23 2005
+++ llvm/lib/Transforms/IPO/SimplifyLibCalls.cpp	Wed Apr 27 02:54:40 2005
@@ -31,192 +31,192 @@
 using namespace llvm;
 
 namespace {
-  Statistic<> SimplifiedLibCalls("simplified-lib-calls", 
-      "Number of well-known library calls simplified");
 
-  /// This class is the base class for a set of small but important 
-  /// optimizations of calls to well-known functions, such as those in the c
-  /// library. This class provides the basic infrastructure for handling 
-  /// runOnModule. Subclasses register themselves and provide two methods:
-  /// RecognizeCall and OptimizeCall. Whenever this class finds a function call,
-  /// it asks the subclasses to recognize the call. If it is recognized, then
-  /// the OptimizeCall method is called on that subclass instance. In this way
-  /// the subclasses implement the calling conditions on which they trigger and
-  /// the action to perform, making it easy to add new optimizations of this
-  /// form.
-  /// @brief A ModulePass for optimizing well-known function calls
-  struct SimplifyLibCalls : public ModulePass {
-
-    /// We need some target data for accurate signature details that are
-    /// target dependent. So we require target data in our AnalysisUsage.
-    virtual void getAnalysisUsage(AnalysisUsage& Info) const;
-
-    /// For this pass, process all of the function calls in the module, calling
-    /// RecognizeCall and OptimizeCall as appropriate.
-    virtual bool runOnModule(Module &M);
-
-  };
-
-  RegisterOpt<SimplifyLibCalls> 
-    X("simplify-libcalls","Simplify well-known library calls");
-
-  struct CallOptimizer
-  {
-    /// @brief Constructor that registers the optimization
-    CallOptimizer(const char * fname );
-
-    virtual ~CallOptimizer();
-
-    /// The implementation of this function in subclasses should determine if
-    /// \p F is suitable for the optimization. This method is called by 
-    /// runOnModule to short circuit visiting all the call sites of such a
-    /// function if that function is not suitable in the first place.
-    /// If the called function is suitabe, this method should return true;
-    /// false, otherwise. This function should also perform any lazy 
-    /// initialization that the CallOptimizer needs to do, if its to return 
-    /// true. This avoids doing initialization until the optimizer is actually
-    /// going to be called upon to do some optimization.
-    virtual bool ValidateCalledFunction(
-      const Function* F,   ///< The function that is the target of call sites
-      const TargetData& TD ///< Information about the target
-    ) = 0;
-
-    /// The implementations of this function in subclasses is the heart of the 
-    /// SimplifyLibCalls algorithm. Sublcasses of this class implement 
-    /// OptimizeCall to determine if (a) the conditions are right for optimizing
-    /// the call and (b) to perform the optimization. If an action is taken 
-    /// against ci, the subclass is responsible for returning true and ensuring
-    /// that ci is erased from its parent.
-    /// @param ci the call instruction under consideration
-    /// @param f the function that ci calls.
-    /// @brief Optimize a call, if possible.
-    virtual bool OptimizeCall(
-      CallInst* ci,         ///< The call instruction that should be optimized.
-      const TargetData& TD  ///< Information about the target
-    ) = 0;
-
-    const char * getFunctionName() const { return func_name; }
-
-#ifndef NDEBUG
-    void activate() { ++activations; }
-#endif
-
-  private:
-    const char* func_name;
-#ifndef NDEBUG
-    std::string stat_name;
-    Statistic<> activations; 
-#endif
-  };
-
-  /// @brief The list of optimizations deriving from CallOptimizer
-
-  hash_map<std::string,CallOptimizer*> optlist;
-
-  CallOptimizer::CallOptimizer(const char* fname)
+/// This statistic keeps track of the total number of library calls that have
+/// been simplified regardless of which call it is.
+Statistic<> SimplifiedLibCalls("simplify-libcalls", 
+  "Number of well-known library calls simplified");
+
+/// @brief The list of optimizations deriving from LibCallOptimization
+class LibCallOptimization;
+class SimplifyLibCalls;
+hash_map<std::string,LibCallOptimization*> optlist;
+
+/// This class is the abstract base class for the set of optimizations that
+/// corresponds to one library call. The SimplifyLibCall pass will call the
+/// ValidateCalledFunction method to ask the optimization if a given Function
+/// is the kind that the optimization can handle. It will also call the
+/// OptimizeCall method to perform, or attempt to perform, the optimization(s)
+/// for the library call. Subclasses of this class are located toward the
+/// end of this file.
+/// @brief Base class for library call optimizations
+struct LibCallOptimization
+{
+  /// @brief Constructor that registers the optimization. The \p fname argument
+  /// must be the name of the library function being optimized by the subclass.
+  LibCallOptimization(const char * fname )
     : func_name(fname)
 #ifndef NDEBUG
     , stat_name(std::string("simplify-libcalls:")+fname)
-    , activations(stat_name.c_str(),"Number of calls simplified") 
+    , occurrences(stat_name.c_str(),"Number of calls simplified") 
 #endif
   {
     // Register this call optimizer
     optlist[func_name] = this;
   }
 
-  /// Make sure we get our virtual table in this file.
-  CallOptimizer::~CallOptimizer() { }
+  /// @brief Destructor
+  virtual ~LibCallOptimization() {}
 
-}
-
-ModulePass *llvm::createSimplifyLibCallsPass() 
-{ 
-  return new SimplifyLibCalls(); 
-}
+  /// The implementation of this function in subclasses should determine if
+  /// \p F is suitable for the optimization. This method is called by 
+  /// runOnModule to short circuit visiting all the call sites of such a
+  /// function if that function is not suitable in the first place.
+  /// If the called function is suitabe, this method should return true;
+  /// false, otherwise. This function should also perform any lazy 
+  /// initialization that the LibCallOptimization needs to do, if its to return 
+  /// true. This avoids doing initialization until the optimizer is actually
+  /// going to be called upon to do some optimization.
+  virtual bool ValidateCalledFunction(
+    const Function* F,    ///< The function that is the target of call sites
+    SimplifyLibCalls& SLC ///< The pass object invoking us
+  ) = 0;
+
+  /// The implementations of this function in subclasses is the heart of the 
+  /// SimplifyLibCalls algorithm. Sublcasses of this class implement 
+  /// OptimizeCall to determine if (a) the conditions are right for optimizing
+  /// the call and (b) to perform the optimization. If an action is taken 
+  /// against ci, the subclass is responsible for returning true and ensuring
+  /// that ci is erased from its parent.
+  /// @param ci the call instruction under consideration
+  /// @param f the function that ci calls.
+  /// @brief Optimize a call, if possible.
+  virtual bool OptimizeCall(
+    CallInst* ci,          ///< The call instruction that should be optimized.
+    SimplifyLibCalls& SLC  ///< The pass object invoking us
+  ) = 0;
 
-void SimplifyLibCalls::getAnalysisUsage(AnalysisUsage& Info) const
-{
-  // Ask that the TargetData analysis be performed before us so we can use
-  // the target data.
-  Info.addRequired<TargetData>();
-}
+  /// @brief Get the name of the library call being optimized
+  const char * getFunctionName() const { return func_name; }
 
-bool SimplifyLibCalls::runOnModule(Module &M) 
-{
-  TargetData& TD = getAnalysis<TargetData>();
+#ifndef NDEBUG
+  void occurred() { ++occurrences; }
+#endif
 
-  bool result = false;
+private:
+  const char* func_name; ///< Name of the library call we optimize
+#ifndef NDEBUG
+  std::string stat_name; ///< Holder for debug statistic name
+  Statistic<> occurrences; ///< debug statistic (-debug-only=simplify-libcalls)
+#endif
+};
 
-  // The call optimizations can be recursive. That is, the optimization might
-  // generate a call to another function which can also be optimized. This way
-  // we make the CallOptimizer instances very specific to the case they handle.
-  // It also means we need to keep running over the function calls in the module
-  // until we don't get any more optimizations possible.
-  bool found_optimization = false;
-  do
-  {
-    found_optimization = false;
-    for (Module::iterator FI = M.begin(), FE = M.end(); FI != FE; ++FI)
+/// This class is the base class for a set of small but important 
+/// optimizations of calls to well-known functions, such as those in the c
+/// library. 
+
+/// This class is an LLVM Pass that applies each of the LibCallOptimization 
+/// instances to all the call sites in a module, relatively efficiently. The
+/// purpose of this pass is to provide optimizations for calls to well-known 
+/// functions with well-known semantics, such as those in the c library. The
+/// class provides the basic infrastructure for handling runOnModule.  
+/// Whenever this pass finds a function call, it asks the subclasses to 
+/// validate the call by calling ValidateLibraryCall. If it is validated, then
+/// the OptimizeCall method is called. 
+/// @brief A ModulePass for optimizing well-known function calls.
+struct SimplifyLibCalls : public ModulePass 
+{
+  /// We need some target data for accurate signature details that are
+  /// target dependent. So we require target data in our AnalysisUsage.
+  virtual void getAnalysisUsage(AnalysisUsage& Info) const
+  {
+    // Ask that the TargetData analysis be performed before us so we can use
+    // the target data.
+    Info.addRequired<TargetData>();
+  }
+
+  /// For this pass, process all of the function calls in the module, calling
+  /// ValidateLibraryCall and OptimizeCall as appropriate.
+  virtual bool runOnModule(Module &M)
+  {
+    reset(M);
+
+    bool result = false;
+
+    // The call optimizations can be recursive. That is, the optimization might
+    // generate a call to another function which can also be optimized. This way
+    // we make the LibCallOptimization instances very specific to the case they 
+    // handle. It also means we need to keep running over the function calls in 
+    // the module until we don't get any more optimizations possible.
+    bool found_optimization = false;
+    do
     {
-      // All the "well-known" functions are external and have external linkage
-      // because they live in a runtime library somewhere and were (probably) 
-      // not compiled by LLVM.  So, we only act on external functions that have 
-      // external linkage and non-empty uses.
-      if (!FI->isExternal() || !FI->hasExternalLinkage() || FI->use_empty())
-        continue;
-
-      // Get the optimization class that pertains to this function
-      CallOptimizer* CO = optlist[FI->getName().c_str()];
-      if (!CO)
-        continue;
-
-      // Make sure the called function is suitable for the optimization
-      if (!CO->ValidateCalledFunction(FI,TD))
-        continue;
-
-      // Loop over each of the uses of the function
-      for (Value::use_iterator UI = FI->use_begin(), UE = FI->use_end(); 
-           UI != UE ; )
+      found_optimization = false;
+      for (Module::iterator FI = M.begin(), FE = M.end(); FI != FE; ++FI)
       {
-        // If the use of the function is a call instruction
-        if (CallInst* CI = dyn_cast<CallInst>(*UI++))
+        // All the "well-known" functions are external and have external linkage
+        // because they live in a runtime library somewhere and were (probably) 
+        // not compiled by LLVM.  So, we only act on external functions that have 
+        // external linkage and non-empty uses.
+        if (!FI->isExternal() || !FI->hasExternalLinkage() || FI->use_empty())
+          continue;
+
+        // Get the optimization class that pertains to this function
+        LibCallOptimization* CO = optlist[FI->getName().c_str()];
+        if (!CO)
+          continue;
+
+        // Make sure the called function is suitable for the optimization
+        if (!CO->ValidateCalledFunction(FI,*this))
+          continue;
+
+        // Loop over each of the uses of the function
+        for (Value::use_iterator UI = FI->use_begin(), UE = FI->use_end(); 
+             UI != UE ; )
         {
-          // Do the optimization on the CallOptimizer.
-          if (CO->OptimizeCall(CI,TD))
+          // If the use of the function is a call instruction
+          if (CallInst* CI = dyn_cast<CallInst>(*UI++))
           {
-            ++SimplifiedLibCalls;
-            found_optimization = result = true;
+            // Do the optimization on the LibCallOptimization.
+            if (CO->OptimizeCall(CI,*this))
+            {
+              ++SimplifiedLibCalls;
+              found_optimization = result = true;
 #ifndef NDEBUG
-            CO->activate();
+              CO->occurred();
 #endif
+            }
           }
         }
       }
-    }
-  } while (found_optimization);
-  return result;
-}
+    } while (found_optimization);
+    return result;
+  }
 
-namespace {
+  /// @brief Return the *current* module we're working on.
+  Module* getModule() { return M; }
+
+  /// @brief Return the *current* target data for the module we're working on.
+  TargetData* getTargetData() { return TD; }
 
-  /// Provide some functions for accessing standard library prototypes and
-  /// caching them so we don't have to keep recomputing them
-  FunctionType* get_strlen(const Type* IntPtrTy)
+  /// @brief Return a Function* for the strlen libcall
+  Function* get_strlen()
   {
-    static FunctionType* strlen_type = 0;
-    if (!strlen_type)
+    if (!strlen_func)
     {
       std::vector<const Type*> args;
       args.push_back(PointerType::get(Type::SByteTy));
-      strlen_type = FunctionType::get(IntPtrTy, args, false);
+      FunctionType* strlen_type = 
+        FunctionType::get(TD->getIntPtrType(), args, false);
+      strlen_func = M->getOrInsertFunction("strlen",strlen_type);
     }
-    return strlen_type;
+    return strlen_func;
   }
 
-  FunctionType* get_memcpy()
+  /// @brief Return a Function* for the memcpy libcall
+  Function* get_memcpy()
   {
-    static FunctionType* memcpy_type = 0;
-    if (!memcpy_type)
+    if (!memcpy_func)
     {
       // Note: this is for llvm.memcpy intrinsic
       std::vector<const Type*> args;
@@ -224,120 +224,65 @@
       args.push_back(PointerType::get(Type::SByteTy));
       args.push_back(Type::IntTy);
       args.push_back(Type::IntTy);
-      memcpy_type = FunctionType::get(Type::VoidTy, args, false);
+      FunctionType* memcpy_type = FunctionType::get(Type::VoidTy, args, false);
+      memcpy_func = M->getOrInsertFunction("llvm.memcpy",memcpy_type);
     }
-    return memcpy_type;
+    return memcpy_func;
   }
 
-  /// A function to compute the length of a null-terminated string of integers.
-  /// This function can't rely on the size of the constant array because there 
-  /// could be a null terminator in the middle of the array. We also have to 
-  /// bail out if we find a non-integer constant initializer of one of the 
-  /// elements or if there is no null-terminator. The logic below checks
-  bool getConstantStringLength(Value* V, uint64_t& len )
-  {
-    assert(V != 0 && "Invalid args to getConstantStringLength");
-    len = 0; // make sure we initialize this 
-    User* GEP = 0;
-    // If the value is not a GEP instruction nor a constant expression with a 
-    // GEP instruction, then return false because ConstantArray can't occur 
-    // any other way
-    if (GetElementPtrInst* GEPI = dyn_cast<GetElementPtrInst>(V))
-      GEP = GEPI;
-    else if (ConstantExpr* CE = dyn_cast<ConstantExpr>(V))
-      if (CE->getOpcode() == Instruction::GetElementPtr)
-        GEP = CE;
-      else
-        return false;
-    else
-      return false;
-
-    // Make sure the GEP has exactly three arguments.
-    if (GEP->getNumOperands() != 3)
-      return false;
-
-    // Check to make sure that the first operand of the GEP is an integer and
-    // has value 0 so that we are sure we're indexing into the initializer. 
-    if (ConstantInt* op1 = dyn_cast<ConstantInt>(GEP->getOperand(1)))
-    {
-      if (!op1->isNullValue())
-        return false;
-    }
-    else
-      return false;
-
-    // Ensure that the second operand is a ConstantInt. If it isn't then this
-    // GEP is wonky and we're not really sure what were referencing into and 
-    // better of not optimizing it. While we're at it, get the second index
-    // value. We'll need this later for indexing the ConstantArray.
-    uint64_t start_idx = 0;
-    if (ConstantInt* CI = dyn_cast<ConstantInt>(GEP->getOperand(2)))
-      start_idx = CI->getRawValue();
-    else
-      return false;
+  /// @brief Compute length of constant string
+  bool getConstantStringLength(Value* V, uint64_t& len );
 
-    // The GEP instruction, constant or instruction, must reference a global
-    // variable that is a constant and is initialized. The referenced constant
-    // initializer is the array that we'll use for optimization.
-    GlobalVariable* GV = dyn_cast<GlobalVariable>(GEP->getOperand(0));
-    if (!GV || !GV->isConstant() || !GV->hasInitializer())
-      return false;
+private:
+  void reset(Module& mod)
+  {
+    M = &mod;
+    TD = &getAnalysis<TargetData>();
+    memcpy_func = 0;
+    strlen_func = 0;
+  }
 
-    // Get the initializer.
-    Constant* INTLZR = GV->getInitializer();
+private:
+  Function* memcpy_func;
+  Function* strlen_func;
+  Module* M;
+  TargetData* TD;
+};
 
-    // Handle the ConstantAggregateZero case
-    if (ConstantAggregateZero* CAZ = dyn_cast<ConstantAggregateZero>(INTLZR))
-    {
-      // This is a degenerate case. The initializer is constant zero so the
-      // length of the string must be zero.
-      len = 0;
-      return true;
-    }
+// Register the pass
+RegisterOpt<SimplifyLibCalls> 
+X("simplify-libcalls","Simplify well-known library calls");
 
-    // Must be a Constant Array
-    ConstantArray* A = dyn_cast<ConstantArray>(INTLZR);
-    if (!A)
-      return false;
+} // anonymous namespace
 
-    // Get the number of elements in the array
-    uint64_t max_elems = A->getType()->getNumElements();
+// The only public symbol in this file which just instantiates the pass object
+ModulePass *llvm::createSimplifyLibCallsPass() 
+{ 
+  return new SimplifyLibCalls(); 
+}
 
-    // Traverse the constant array from start_idx (derived above) which is
-    // the place the GEP refers to in the array. 
-    for ( len = start_idx; len < max_elems; len++)
-    {
-      if (ConstantInt* CI = dyn_cast<ConstantInt>(A->getOperand(len)))
-      {
-        // Check for the null terminator
-        if (CI->isNullValue())
-          break; // we found end of string
-      }
-      else
-        return false; // This array isn't suitable, non-int initializer
-    }
-    if (len >= max_elems)
-      return false; // This array isn't null terminated
+// Classes below here, in the anonymous namespace, are all subclasses of the
+// LibCallOptimization class, each implementing all optimizations possible for a
+// single well-known library call. Each has a static singleton instance that
+// auto registers it into the "optlist" global above. 
+namespace {
 
-    // Subtract out the initial value from the length
-    len -= start_idx;
-    return true; // success!
-  }
+bool getConstantStringLength(Value* V, uint64_t& len );
 
-/// This CallOptimizer will find instances of a call to "exit" that occurs
+/// This LibCallOptimization will find instances of a call to "exit" that occurs
 /// within the "main" function and change it to a simple "ret" instruction with
 /// the same value as passed to the exit function. It assumes that the 
 /// instructions after the call to exit(3) can be deleted since they are 
 /// unreachable anyway.
 /// @brief Replace calls to exit in main with a simple return
-struct ExitInMainOptimization : public CallOptimizer
+struct ExitInMainOptimization : public LibCallOptimization
 {
-  ExitInMainOptimization() : CallOptimizer("exit") {}
+  ExitInMainOptimization() : LibCallOptimization("exit") {}
   virtual ~ExitInMainOptimization() {}
 
   // Make sure the called function looks like exit (int argument, int return
   // type, external linkage, not varargs). 
-  virtual bool ValidateCalledFunction(const Function* f, const TargetData& TD)
+  virtual bool ValidateCalledFunction(const Function* f, SimplifyLibCalls& SLC)
   {
     if (f->arg_size() >= 1)
       if (f->arg_begin()->getType()->isInteger())
@@ -345,7 +290,7 @@
     return false;
   }
 
-  virtual bool OptimizeCall(CallInst* ci, const TargetData& TD)
+  virtual bool OptimizeCall(CallInst* ci, SimplifyLibCalls& SLC)
   {
     // To be careful, we check that the call to exit is coming from "main", that
     // main has external linkage, and the return type of main and the argument
@@ -386,41 +331,23 @@
   }
 } ExitInMainOptimizer;
 
-/// This CallOptimizer will simplify a call to the strcat library function. The
-/// simplification is possible only if the string being concatenated is a 
-/// constant array or a constant expression that results in a constant array. In
-/// this case, if the array is small, we can generate a series of inline store
-/// instructions to effect the concatenation without calling strcat.
+/// This LibCallOptimization will simplify a call to the strcat library 
+/// function. The simplification is possible only if the string being 
+/// concatenated is a constant array or a constant expression that results in 
+/// a constant array. In this case, if the array is small, we can generate a 
+/// series of inline store instructions to effect the concatenation without 
+/// calling strcat.
 /// @brief Simplify the strcat library function.
-struct StrCatOptimization : public CallOptimizer
+struct StrCatOptimization : public LibCallOptimization
 {
-private:
-  Function* strlen_func;
-  Function* memcpy_func;
 public:
-  StrCatOptimization() 
-    : CallOptimizer("strcat") 
-    , strlen_func(0)
-    , memcpy_func(0)
-    {}
-  virtual ~StrCatOptimization() {}
-
-  inline Function* get_strlen_func(Module*M,const Type* IntPtrTy)
-  {
-    if (strlen_func)
-      return strlen_func;
-    return strlen_func = M->getOrInsertFunction("strlen",get_strlen(IntPtrTy));
-  }
+  StrCatOptimization() : LibCallOptimization("strcat") {}
 
-  inline Function* get_memcpy_func(Module* M) 
-  {
-    if (memcpy_func)
-      return memcpy_func;
-    return memcpy_func = M->getOrInsertFunction("llvm.memcpy",get_memcpy());
-  }
+public:
+  virtual ~StrCatOptimization() {}
 
   /// @brief Make sure that the "strcat" function has the right prototype
-  virtual bool ValidateCalledFunction(const Function* f, const TargetData& TD) 
+  virtual bool ValidateCalledFunction(const Function* f, SimplifyLibCalls& SLC) 
   {
     if (f->getReturnType() == PointerType::get(Type::SByteTy))
       if (f->arg_size() == 2) 
@@ -429,13 +356,6 @@
         if (AI++->getType() == PointerType::get(Type::SByteTy))
           if (AI->getType() == PointerType::get(Type::SByteTy))
           {
-            // Invalidate the pre-computed strlen_func and memcpy_func Functions
-            // because, by definition, this method is only called when a new
-            // Module is being traversed. Invalidation causes re-computation for
-            // the new Module (if necessary).
-            strlen_func = 0;
-            memcpy_func = 0;
-
             // Indicate this is a suitable call type.
             return true;
           }
@@ -443,9 +363,8 @@
     return false;
   }
 
-  /// Perform the optimization if the length of the string concatenated
-  /// is reasonably short and it is a constant array.
-  virtual bool OptimizeCall(CallInst* ci, const TargetData& TD)
+  /// @brief Optimize the strcat library function
+  virtual bool OptimizeCall(CallInst* ci, SimplifyLibCalls& SLC)
   {
     // Extract the initializer (while making numerous checks) from the 
     // source operand of the call to strcat. If we get null back, one of
@@ -471,11 +390,10 @@
 
     // We need to find the end of the destination string.  That's where the 
     // memory is to be moved to. We just generate a call to strlen (further 
-    // optimized in another pass). Note that the get_strlen_func() call 
+    // optimized in another pass).  Note that the SLC.get_strlen() call 
     // caches the Function* for us.
     CallInst* strlen_inst = 
-      new CallInst(get_strlen_func(M,TD.getIntPtrType()),
-                   ci->getOperand(1),"",ci);
+      new CallInst(SLC.get_strlen(), ci->getOperand(1),"",ci);
 
     // Now that we have the destination's length, we must index into the 
     // destination's pointer to get the actual memcpy destination (end of
@@ -492,7 +410,7 @@
     vals.push_back(ci->getOperand(2)); // source
     vals.push_back(ConstantSInt::get(Type::IntTy,len)); // length
     vals.push_back(ConstantSInt::get(Type::IntTy,1)); // alignment
-    CallInst* memcpy_inst = new CallInst(get_memcpy_func(M), vals, "", ci);
+    CallInst* memcpy_inst = new CallInst(SLC.get_memcpy(), vals, "", ci);
 
     // Finally, substitute the first operand of the strcat call for the 
     // strcat call itself since strcat returns its first operand; and, 
@@ -503,19 +421,108 @@
   }
 } StrCatOptimizer;
 
-/// This CallOptimizer will simplify a call to the strlen library function by
+/// This LibCallOptimization will simplify a call to the strcpy library function. 
+/// Several optimizations are possible: 
+/// (1) If src and dest are the same and not volatile, just return dest
+/// (2) If the src is a constant then we can convert to llvm.memmove
+/// @brief Simplify the strcpy library function.
+struct StrCpyOptimization : public LibCallOptimization
+{
+public:
+  StrCpyOptimization() : LibCallOptimization("strcpy") {}
+  virtual ~StrCpyOptimization() {}
+
+  /// @brief Make sure that the "strcpy" function has the right prototype
+  virtual bool ValidateCalledFunction(const Function* f, SimplifyLibCalls& SLC) 
+  {
+    if (f->getReturnType() == PointerType::get(Type::SByteTy))
+      if (f->arg_size() == 2) 
+      {
+        Function::const_arg_iterator AI = f->arg_begin();
+        if (AI++->getType() == PointerType::get(Type::SByteTy))
+          if (AI->getType() == PointerType::get(Type::SByteTy))
+          {
+            // Indicate this is a suitable call type.
+            return true;
+          }
+      }
+    return false;
+  }
+
+  /// @brief Perform the strcpy optimization
+  virtual bool OptimizeCall(CallInst* ci, SimplifyLibCalls& SLC)
+  {
+    // First, check to see if src and destination are the same. If they are,
+    // then the optimization is to replace the CallInst with the destination
+    // because the call is a no-op. Note that this corresponds to the 
+    // degenerate strcpy(X,X) case which should have "undefined" results
+    // according to the C specification. However, it occurs sometimes and
+    // we optimize it as a no-op.
+    Value* dest = ci->getOperand(1);
+    Value* src = ci->getOperand(2);
+    if (dest == src)
+    {
+      ci->replaceAllUsesWith(dest);
+      ci->eraseFromParent();
+      return true;
+    }
+    
+    // Get the length of the constant string referenced by the second operand,
+    // the "src" parameter. Fail the optimization if we can't get the length
+    // (note that getConstantStringLength does lots of checks to make sure this
+    // is valid).
+    uint64_t len = 0;
+    if (!getConstantStringLength(ci->getOperand(2),len))
+      return false;
+
+    // If the constant string's length is zero we can optimize this by just
+    // doing a store of 0 at the first byte of the destination
+    if (len == 0)
+    {
+      new StoreInst(ConstantInt::get(Type::SByteTy,0),ci->getOperand(1),ci);
+      ci->replaceAllUsesWith(dest);
+      ci->eraseFromParent();
+      return true;
+    }
+
+    // Increment the length because we actually want to memcpy the null
+    // terminator as well.
+    len++;
+
+    // Extract some information from the instruction
+    Module* M = ci->getParent()->getParent()->getParent();
+
+    // We have enough information to now generate the memcpy call to
+    // do the concatenation for us.
+    std::vector<Value*> vals;
+    vals.push_back(dest); // destination
+    vals.push_back(src); // source
+    vals.push_back(ConstantSInt::get(Type::IntTy,len)); // length
+    vals.push_back(ConstantSInt::get(Type::IntTy,1)); // alignment
+    CallInst* memcpy_inst = new CallInst(SLC.get_memcpy(), vals, "", ci);
+
+    // Finally, substitute the first operand of the strcat call for the 
+    // strcat call itself since strcat returns its first operand; and, 
+    // kill the strcat CallInst.
+    ci->replaceAllUsesWith(dest);
+    ci->eraseFromParent();
+    return true;
+  }
+} StrCpyOptimizer;
+
+/// This LibCallOptimization will simplify a call to the strlen library function by
 /// replacing it with a constant value if the string provided to it is a 
 /// constant array.
 /// @brief Simplify the strlen library function.
-struct StrLenOptimization : public CallOptimizer
+struct StrLenOptimization : public LibCallOptimization
 {
-  StrLenOptimization() : CallOptimizer("strlen") {}
+  StrLenOptimization() : LibCallOptimization("strlen") {}
   virtual ~StrLenOptimization() {}
 
   /// @brief Make sure that the "strlen" function has the right prototype
-  virtual bool ValidateCalledFunction(const Function* f, const TargetData& TD)
+  virtual bool ValidateCalledFunction(const Function* f, SimplifyLibCalls& SLC)
   {
-    if (f->getReturnType() == TD.getIntPtrType())
+    if (f->getReturnType() == SLC.getTargetData()->getIntPtrType())
       if (f->arg_size() == 1) 
         if (Function::const_arg_iterator AI = f->arg_begin())
           if (AI->getType() == PointerType::get(Type::SByteTy))
@@ -524,33 +531,34 @@
   }
 
   /// @brief Perform the strlen optimization
-  virtual bool OptimizeCall(CallInst* ci, const TargetData& TD)
+  virtual bool OptimizeCall(CallInst* ci, SimplifyLibCalls& SLC)
   {
     // Get the length of the string
     uint64_t len = 0;
     if (!getConstantStringLength(ci->getOperand(1),len))
       return false;
 
-    ci->replaceAllUsesWith(ConstantInt::get(TD.getIntPtrType(),len));
+    ci->replaceAllUsesWith(
+        ConstantInt::get(SLC.getTargetData()->getIntPtrType(),len));
     ci->eraseFromParent();
     return true;
   }
 } StrLenOptimizer;
 
-/// This CallOptimizer will simplify a call to the memcpy library function by
-/// expanding it out to a small set of stores if the copy source is a constant
-/// array. 
+/// This LibCallOptimization will simplify a call to the memcpy library function by
+/// expanding it out to a single store of size 0, 1, 2, 4, or 8 bytes depending
+/// on the length of the string and the alignment.
 /// @brief Simplify the memcpy library function.
-struct MemCpyOptimization : public CallOptimizer
+struct MemCpyOptimization : public LibCallOptimization
 {
-  MemCpyOptimization() : CallOptimizer("llvm.memcpy") {}
+  MemCpyOptimization() : LibCallOptimization("llvm.memcpy") {}
 protected:
-  MemCpyOptimization(const char* fname) : CallOptimizer(fname) {}
+  MemCpyOptimization(const char* fname) : LibCallOptimization(fname) {}
 public:
   virtual ~MemCpyOptimization() {}
 
   /// @brief Make sure that the "memcpy" function has the right prototype
-  virtual bool ValidateCalledFunction(const Function* f, const TargetData& TD)
+  virtual bool ValidateCalledFunction(const Function* f, SimplifyLibCalls& TD)
   {
     // Just make sure this has 4 arguments per LLVM spec.
     return (f->arg_size() == 4);
@@ -562,7 +570,7 @@
   /// alignment match the sizes of our intrinsic types so we can do a load and
   /// store instead of the memcpy call.
   /// @brief Perform the memcpy optimization.
-  virtual bool OptimizeCall(CallInst* ci, const TargetData& TD)
+  virtual bool OptimizeCall(CallInst* ci, SimplifyLibCalls& TD)
   {
     // Make sure we have constant int values to work with
     ConstantInt* LEN = dyn_cast<ConstantInt>(ci->getOperand(3));
@@ -614,8 +622,7 @@
   }
 } MemCpyOptimizer;
 
-/// This CallOptimizer will simplify a call to the memmove library function. It
-/// is identical to MemCopyOptimization except for the name of the intrinsic.
+/// This LibCallOptimization will simplify a call to the memmove library function. /// It is identical to MemCopyOptimization except for the name of the intrinsic.
 /// @brief Simplify the memmove library function.
 struct MemMoveOptimization : public MemCpyOptimization
 {
@@ -623,4 +630,102 @@
 
 } MemMoveOptimizer;
 
+/// A function to compute the length of a null-terminated string of integers.
+/// This function can't rely on the size of the constant array because there 
+/// could be a null terminator in the middle of the array. We also have to 
+/// bail out if we find a non-integer constant initializer of one of the 
+/// elements or if there is no null-terminator. The logic below checks
+bool getConstantStringLength(Value* V, uint64_t& len )
+{
+  assert(V != 0 && "Invalid args to getConstantStringLength");
+  len = 0; // make sure we initialize this 
+  User* GEP = 0;
+  // If the value is not a GEP instruction nor a constant expression with a 
+  // GEP instruction, then return false because ConstantArray can't occur 
+  // any other way
+  if (GetElementPtrInst* GEPI = dyn_cast<GetElementPtrInst>(V))
+    GEP = GEPI;
+  else if (ConstantExpr* CE = dyn_cast<ConstantExpr>(V))
+    if (CE->getOpcode() == Instruction::GetElementPtr)
+      GEP = CE;
+    else
+      return false;
+  else
+    return false;
+
+  // Make sure the GEP has exactly three arguments.
+  if (GEP->getNumOperands() != 3)
+    return false;
+
+  // Check to make sure that the first operand of the GEP is an integer and
+  // has value 0 so that we are sure we're indexing into the initializer. 
+  if (ConstantInt* op1 = dyn_cast<ConstantInt>(GEP->getOperand(1)))
+  {
+    if (!op1->isNullValue())
+      return false;
+  }
+  else
+    return false;
+
+  // Ensure that the second operand is a ConstantInt. If it isn't then this
+  // GEP is wonky and we're not really sure what were referencing into and 
+  // better of not optimizing it. While we're at it, get the second index
+  // value. We'll need this later for indexing the ConstantArray.
+  uint64_t start_idx = 0;
+  if (ConstantInt* CI = dyn_cast<ConstantInt>(GEP->getOperand(2)))
+    start_idx = CI->getRawValue();
+  else
+    return false;
+
+  // The GEP instruction, constant or instruction, must reference a global
+  // variable that is a constant and is initialized. The referenced constant
+  // initializer is the array that we'll use for optimization.
+  GlobalVariable* GV = dyn_cast<GlobalVariable>(GEP->getOperand(0));
+  if (!GV || !GV->isConstant() || !GV->hasInitializer())
+    return false;
+
+  // Get the initializer.
+  Constant* INTLZR = GV->getInitializer();
+
+  // Handle the ConstantAggregateZero case
+  if (ConstantAggregateZero* CAZ = dyn_cast<ConstantAggregateZero>(INTLZR))
+  {
+    // This is a degenerate case. The initializer is constant zero so the
+    // length of the string must be zero.
+    len = 0;
+    return true;
+  }
+
+  // Must be a Constant Array
+  ConstantArray* A = dyn_cast<ConstantArray>(INTLZR);
+  if (!A)
+    return false;
+
+  // Get the number of elements in the array
+  uint64_t max_elems = A->getType()->getNumElements();
+
+  // Traverse the constant array from start_idx (derived above) which is
+  // the place the GEP refers to in the array. 
+  for ( len = start_idx; len < max_elems; len++)
+  {
+    if (ConstantInt* CI = dyn_cast<ConstantInt>(A->getOperand(len)))
+    {
+      // Check for the null terminator
+      if (CI->isNullValue())
+        break; // we found end of string
+    }
+    else
+      return false; // This array isn't suitable, non-int initializer
+  }
+  if (len >= max_elems)
+    return false; // This array isn't null terminated
+
+  // Subtract out the initial value from the length
+  len -= start_idx;
+  return true; // success!
+}
+
+
+// TODO: Additional cases that we need to add to this file:
+// 1. memmove -> memcpy if src is a global constant array
 }






More information about the llvm-commits mailing list