[llvm-commits] [llvm] r45886 - in /llvm/trunk: lib/Transforms/IPO/ArgumentPromotion.cpp test/Transforms/ArgumentPromotion/byval.ll

Chris Lattner sabre at nondot.org
Fri Jan 11 14:31:43 PST 2008


Author: lattner
Date: Fri Jan 11 16:31:41 2008
New Revision: 45886

URL: http://llvm.org/viewvc/llvm-project?rev=45886&view=rev
Log:
Teach argpromote to ruthlessly hack small byval structs when it can
get away with it, which exposes opportunities to eliminate the memory
objects entirely.  For example, we now compile byval.ll to:

define internal void @f1(i32 %b.0, i64 %b.1) {
entry:
	%tmp2 = add i32 %b.0, 1		; <i32> [#uses=0]
	ret void
}

define i32 @main() nounwind  {
entry:
	call void @f1( i32 1, i64 2 )
	ret i32 0
}

This seems like it would trigger a lot for code that passes around small
structs (e.g. SDOperand's or _Complex)...


Added:
    llvm/trunk/test/Transforms/ArgumentPromotion/byval.ll
Modified:
    llvm/trunk/lib/Transforms/IPO/ArgumentPromotion.cpp

Modified: llvm/trunk/lib/Transforms/IPO/ArgumentPromotion.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/ArgumentPromotion.cpp?rev=45886&r1=45885&r2=45886&view=diff

==============================================================================
--- llvm/trunk/lib/Transforms/IPO/ArgumentPromotion.cpp (original)
+++ llvm/trunk/lib/Transforms/IPO/ArgumentPromotion.cpp Fri Jan 11 16:31:41 2008
@@ -51,6 +51,7 @@
 
 STATISTIC(NumArgumentsPromoted , "Number of pointer arguments promoted");
 STATISTIC(NumAggregatesPromoted, "Number of aggregate arguments promoted");
+STATISTIC(NumByValArgsPromoted , "Number of byval arguments promoted");
 STATISTIC(NumArgumentsDead     , "Number of dead pointer args eliminated");
 
 namespace {
@@ -71,7 +72,8 @@
     bool PromoteArguments(CallGraphNode *CGN);
     bool isSafeToPromoteArgument(Argument *Arg, bool isByVal) const;
     Function *DoPromotion(Function *F, 
-                          SmallPtrSet<Argument*, 8> &ArgsToPromote);
+                          SmallPtrSet<Argument*, 8> &ArgsToPromote,
+                          SmallPtrSet<Argument*, 8> &ByValArgsToTransform);
   };
 
   char ArgPromotion::ID = 0;
@@ -134,16 +136,44 @@
   // Check to see which arguments are promotable.  If an argument is promotable,
   // add it to ArgsToPromote.
   SmallPtrSet<Argument*, 8> ArgsToPromote;
+  SmallPtrSet<Argument*, 8> ByValArgsToTransform;
   for (unsigned i = 0; i != PointerArgs.size(); ++i) {
-    bool isByVal = F->paramHasAttr(PointerArgs[i].second, ParamAttr::ByVal);
-    if (isSafeToPromoteArgument(PointerArgs[i].first, isByVal))
-      ArgsToPromote.insert(PointerArgs[i].first);
+    bool isByVal = F->paramHasAttr(PointerArgs[i].second+1, ParamAttr::ByVal);
+    
+    // If this is a byval argument, and if the aggregate type is small, just
+    // pass the elements, which is always safe.
+    Argument *PtrArg = PointerArgs[i].first;
+    if (isByVal) {
+      const Type *AgTy = cast<PointerType>(PtrArg->getType())->getElementType();
+      if (const StructType *STy = dyn_cast<StructType>(AgTy))
+        if (STy->getNumElements() <= 3) {
+          // If all the elements are first class types, we can promote it.
+          bool AllSimple = true;
+          for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i)
+            if (!STy->getElementType(i)->isFirstClassType()) {
+              AllSimple = false;
+              break;
+            }
+          
+          // Safe to transform, don't even bother trying to "promote" it.
+          // Passing the elements as a scalar will allow scalarrepl to hack on
+          // the new alloca we introduce.
+          if (AllSimple) {
+            ByValArgsToTransform.insert(PtrArg);
+            continue;
+          }
+        }
+    }
+    
+    // Otherwise, see if we can promote the pointer to its value.
+    if (isSafeToPromoteArgument(PtrArg, isByVal))
+      ArgsToPromote.insert(PtrArg);
   }
   
   // No promotable pointer arguments.
-  if (ArgsToPromote.empty()) return false;
+  if (ArgsToPromote.empty() && ByValArgsToTransform.empty()) return false;
 
-  Function *NewF = DoPromotion(F, ArgsToPromote);
+  Function *NewF = DoPromotion(F, ArgsToPromote, ByValArgsToTransform);
 
   // Update the call graph to know that the function has been transformed.
   getAnalysis<CallGraph>().changeFunction(F, NewF);
@@ -344,7 +374,8 @@
 /// arguments, and returns the new function.  At this point, we know that it's
 /// safe to do so.
 Function *ArgPromotion::DoPromotion(Function *F,
-                                    SmallPtrSet<Argument*, 8> &ArgsToPromote) {
+                                    SmallPtrSet<Argument*, 8> &ArgsToPromote,
+                              SmallPtrSet<Argument*, 8> &ByValArgsToTransform) {
 
   // Start by computing a new prototype for the function, which is the same as
   // the old function, but has modified arguments.
@@ -375,15 +406,18 @@
 
   unsigned index = 1;
   for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E;
-       ++I, ++index)
-    if (!ArgsToPromote.count(I)) {
+       ++I, ++index) {
+    if (ByValArgsToTransform.count(I)) {
+      // Just add all the struct element types.
+      const Type *AgTy = cast<PointerType>(I->getType())->getElementType();
+      const StructType *STy = cast<StructType>(AgTy);
+      for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i)
+        Params.push_back(STy->getElementType(i));
+      ++NumByValArgsPromoted;
+    } else if (!ArgsToPromote.count(I)) {
       Params.push_back(I->getType());
-      if (PAL) {
-        unsigned attrs = PAL->getParamAttrs(index);
-        if (attrs)
-          ParamAttrsVec.push_back(ParamAttrsWithIndex::get(Params.size(),
-                                  attrs));
-      }
+      if (unsigned attrs = PAL ? PAL->getParamAttrs(index) : 0)
+        ParamAttrsVec.push_back(ParamAttrsWithIndex::get(Params.size(), attrs));
     } else if (I->use_empty()) {
       ++NumArgumentsDead;
     } else {
@@ -416,6 +450,7 @@
       else
         ++NumAggregatesPromoted;
     }
+  }
 
   const Type *RetTy = FTy->getReturnType();
 
@@ -462,9 +497,22 @@
     CallSite::arg_iterator AI = CS.arg_begin();
     for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end();
          I != E; ++I, ++AI)
-      if (!ArgsToPromote.count(I))
+      if (!ArgsToPromote.count(I) && !ByValArgsToTransform.count(I)) {
         Args.push_back(*AI);          // Unmodified argument
-      else if (!I->use_empty()) {
+      } else if (ByValArgsToTransform.count(I)) {
+        // Emit a GEP and load for each element of the struct.
+        const Type *AgTy = cast<PointerType>(I->getType())->getElementType();
+        const StructType *STy = cast<StructType>(AgTy);
+        Value *Idxs[2] = { ConstantInt::get(Type::Int32Ty, 0), 0 };
+        for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) {
+          Idxs[1] = ConstantInt::get(Type::Int32Ty, i);
+          Value *Idx = new GetElementPtrInst(*AI, Idxs, Idxs+2,
+                                             (*AI)->getName()+"."+utostr(i),
+                                             Call);
+          // TODO: Tell AA about the new values?
+          Args.push_back(new LoadInst(Idx, Idx->getName()+".val", Call));
+        }        
+      } else if (!I->use_empty()) {
         // Non-dead argument: insert GEPs and loads as appropriate.
         ScalarizeTable &ArgIndices = ScalarizedElements[I];
         for (ScalarizeTable::iterator SI = ArgIndices.begin(),
@@ -526,71 +574,103 @@
   // the new arguments, also transfering over the names as well.
   //
   for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(),
-       I2 = NF->arg_begin(); I != E; ++I)
-    if (!ArgsToPromote.count(I)) {
+       I2 = NF->arg_begin(); I != E; ++I) {
+    if (!ArgsToPromote.count(I) && !ByValArgsToTransform.count(I)) {
       // If this is an unmodified argument, move the name and users over to the
       // new version.
       I->replaceAllUsesWith(I2);
       I2->takeName(I);
       AA.replaceWithNewValue(I, I2);
       ++I2;
-    } else if (I->use_empty()) {
+      continue;
+    }
+    
+    if (ByValArgsToTransform.count(I)) {
+      // In the callee, we create an alloca, and store each of the new incoming
+      // arguments into the alloca.
+      Instruction *InsertPt = NF->begin()->begin();
+      
+      // Just add all the struct element types.
+      const Type *AgTy = cast<PointerType>(I->getType())->getElementType();
+      Value *TheAlloca = new AllocaInst(AgTy, 0, "", InsertPt);
+      const StructType *STy = cast<StructType>(AgTy);
+      Value *Idxs[2] = { ConstantInt::get(Type::Int32Ty, 0), 0 };
+      
+      for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) {
+        Idxs[1] = ConstantInt::get(Type::Int32Ty, i);
+        Value *Idx = new GetElementPtrInst(TheAlloca, Idxs, Idxs+2,
+                                           TheAlloca->getName()+"."+utostr(i),
+                                           InsertPt);
+        I2->setName(I->getName()+"."+utostr(i));
+        new StoreInst(I2++, Idx, InsertPt);
+      }
+      
+      // Anything that used the arg should now use the alloca.
+      I->replaceAllUsesWith(TheAlloca);
+      TheAlloca->takeName(I);
+      AA.replaceWithNewValue(I, TheAlloca);
+      continue;
+    } 
+    
+    if (I->use_empty()) {
       AA.deleteValue(I);
-    } else {
-      // Otherwise, if we promoted this argument, then all users are load
-      // instructions, and all loads should be using the new argument that we
-      // added.
-      ScalarizeTable &ArgIndices = ScalarizedElements[I];
-
-      while (!I->use_empty()) {
-        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);
-          AA.replaceWithNewValue(LI, I2);
-          LI->eraseFromParent();
-          DOUT << "*** Promoted load of argument '" << I->getName()
-               << "' in function '" << F->getName() << "'\n";
-        } else {
-          GetElementPtrInst *GEP = cast<GetElementPtrInst>(I->use_back());
-          std::vector<Value*> Operands(GEP->op_begin()+1, GEP->op_end());
-
-          Function::arg_iterator TheArg = I2;
-          for (ScalarizeTable::iterator It = ArgIndices.begin();
-               *It != Operands; ++It, ++TheArg) {
-            assert(It != ArgIndices.end() && "GEP not handled??");
-          }
+      continue;
+    }
+    
+    // Otherwise, if we promoted this argument, then all users are load
+    // instructions, and all loads should be using the new argument that we
+    // added.
+    ScalarizeTable &ArgIndices = ScalarizedElements[I];
+
+    while (!I->use_empty()) {
+      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);
+        AA.replaceWithNewValue(LI, I2);
+        LI->eraseFromParent();
+        DOUT << "*** Promoted load of argument '" << I->getName()
+             << "' in function '" << F->getName() << "'\n";
+      } else {
+        GetElementPtrInst *GEP = cast<GetElementPtrInst>(I->use_back());
+        std::vector<Value*> Operands(GEP->op_begin()+1, GEP->op_end());
+
+        Function::arg_iterator TheArg = I2;
+        for (ScalarizeTable::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 += "." + CI->getValue().toStringUnsigned(10);
-            else
-              NewName += ".x";
-          TheArg->setName(NewName+".val");
-
-          DOUT << "*** 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);
-            AA.replaceWithNewValue(L, TheArg);
-            L->eraseFromParent();
-          }
-          AA.deleteValue(GEP);
-          GEP->eraseFromParent();
+        std::string NewName = I->getName();
+        for (unsigned i = 0, e = Operands.size(); i != e; ++i)
+          if (ConstantInt *CI = dyn_cast<ConstantInt>(Operands[i]))
+            NewName += "." + CI->getValue().toStringUnsigned(10);
+          else
+            NewName += ".x";
+        TheArg->setName(NewName+".val");
+
+        DOUT << "*** 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);
+          AA.replaceWithNewValue(L, TheArg);
+          L->eraseFromParent();
         }
+        AA.deleteValue(GEP);
+        GEP->eraseFromParent();
       }
-
-      // Increment I2 past all of the arguments added for this promoted pointer.
-      for (unsigned i = 0, e = ArgIndices.size(); i != e; ++i)
-        ++I2;
     }
 
+    // Increment I2 past all of the arguments added for this promoted pointer.
+    for (unsigned i = 0, e = ArgIndices.size(); i != e; ++i)
+      ++I2;
+  }
+
   // Notify the alias analysis implementation that we inserted a new argument.
   if (ExtraArgHack)
     AA.copyValue(Constant::getNullValue(Type::Int32Ty), NF->arg_begin());

Added: llvm/trunk/test/Transforms/ArgumentPromotion/byval.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/ArgumentPromotion/byval.ll?rev=45886&view=auto

==============================================================================
--- llvm/trunk/test/Transforms/ArgumentPromotion/byval.ll (added)
+++ llvm/trunk/test/Transforms/ArgumentPromotion/byval.ll Fri Jan 11 16:31:41 2008
@@ -0,0 +1,24 @@
+; RUN: llvm-as < %s | opt -argpromotion -scalarrepl | llvm-dis | not grep load
+; Argpromote + scalarrepl should change this to passing the two integers by value.
+
+	%struct.ss = type { i32, i64 }
+
+define internal void @f(%struct.ss* byval  %b) nounwind  {
+entry:
+	%tmp = getelementptr %struct.ss* %b, i32 0, i32 0		; <i32*> [#uses=2]
+	%tmp1 = load i32* %tmp, align 4		; <i32> [#uses=1]
+	%tmp2 = add i32 %tmp1, 1		; <i32> [#uses=1]
+	store i32 %tmp2, i32* %tmp, align 4
+	ret void
+}
+
+define i32 @main() nounwind  {
+entry:
+	%S = alloca %struct.ss		; <%struct.ss*> [#uses=4]
+	%tmp1 = getelementptr %struct.ss* %S, i32 0, i32 0		; <i32*> [#uses=1]
+	store i32 1, i32* %tmp1, align 8
+	%tmp4 = getelementptr %struct.ss* %S, i32 0, i32 1		; <i64*> [#uses=1]
+	store i64 2, i64* %tmp4, align 4
+	call void @f( %struct.ss* byval  %S ) nounwind 
+	ret i32 0
+}





More information about the llvm-commits mailing list