aboutsummaryrefslogtreecommitdiff
path: root/lib/Transforms/IPO
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2011-05-02 19:34:44 +0000
committerDimitry Andric <dim@FreeBSD.org>2011-05-02 19:34:44 +0000
commit6b943ff3a3f8617113ecbf611cf0f8957e4e19d2 (patch)
treefc5f365fb9035b2d0c622bbf06c9bbe8627d7279 /lib/Transforms/IPO
parentd0e4e96dc17a6c1c6de3340842c80f0e187ba349 (diff)
Notes
Diffstat (limited to 'lib/Transforms/IPO')
-rw-r--r--lib/Transforms/IPO/ArgumentPromotion.cpp4
-rw-r--r--lib/Transforms/IPO/CMakeLists.txt1
-rw-r--r--lib/Transforms/IPO/DeadArgumentElimination.cpp14
-rw-r--r--lib/Transforms/IPO/GlobalOpt.cpp164
-rw-r--r--lib/Transforms/IPO/IPConstantPropagation.cpp2
-rw-r--r--lib/Transforms/IPO/IPO.cpp1
-rw-r--r--lib/Transforms/IPO/Inliner.cpp1
-rw-r--r--lib/Transforms/IPO/Internalize.cpp5
-rw-r--r--lib/Transforms/IPO/LowerSetJmp.cpp2
-rw-r--r--lib/Transforms/IPO/MergeFunctions.cpp5
-rw-r--r--lib/Transforms/IPO/PartialInlining.cpp2
-rw-r--r--lib/Transforms/IPO/PruneEH.cpp1
-rw-r--r--lib/Transforms/IPO/StructRetPromotion.cpp357
13 files changed, 156 insertions, 403 deletions
diff --git a/lib/Transforms/IPO/ArgumentPromotion.cpp b/lib/Transforms/IPO/ArgumentPromotion.cpp
index 0c650cfe6440..54a7f679e01c 100644
--- a/lib/Transforms/IPO/ArgumentPromotion.cpp
+++ b/lib/Transforms/IPO/ArgumentPromotion.cpp
@@ -771,8 +771,8 @@ CallGraphNode *ArgPromotion::DoPromotion(Function *F,
// function empty.
NF->getBasicBlockList().splice(NF->begin(), F->getBasicBlockList());
- // Loop over the argument list, transfering uses of the old arguments over to
- // the new arguments, also transfering over the names as well.
+ // Loop over the argument list, transferring uses of the old arguments over to
+ // the new arguments, also transferring over the names as well.
//
for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(),
I2 = NF->arg_begin(); I != E; ++I) {
diff --git a/lib/Transforms/IPO/CMakeLists.txt b/lib/Transforms/IPO/CMakeLists.txt
index efdeec564051..179b150c1478 100644
--- a/lib/Transforms/IPO/CMakeLists.txt
+++ b/lib/Transforms/IPO/CMakeLists.txt
@@ -20,5 +20,4 @@ add_llvm_library(LLVMipo
PruneEH.cpp
StripDeadPrototypes.cpp
StripSymbols.cpp
- StructRetPromotion.cpp
)
diff --git a/lib/Transforms/IPO/DeadArgumentElimination.cpp b/lib/Transforms/IPO/DeadArgumentElimination.cpp
index b42322116a98..d4eaf0c4a3ec 100644
--- a/lib/Transforms/IPO/DeadArgumentElimination.cpp
+++ b/lib/Transforms/IPO/DeadArgumentElimination.cpp
@@ -49,7 +49,7 @@ namespace {
/// Struct that represents (part of) either a return value or a function
/// argument. Used so that arguments and return values can be used
- /// interchangably.
+ /// interchangeably.
struct RetOrArg {
RetOrArg(const Function *F, unsigned Idx, bool IsArg) : F(F), Idx(Idx),
IsArg(IsArg) {}
@@ -273,8 +273,8 @@ bool DAE::DeleteDeadVarargs(Function &Fn) {
// function empty.
NF->getBasicBlockList().splice(NF->begin(), Fn.getBasicBlockList());
- // Loop over the argument list, transfering uses of the old arguments over to
- // the new arguments, also transfering over the names as well. While we're at
+ // Loop over the argument list, transferring uses of the old arguments over to
+ // the new arguments, also transferring over the names as well. While we're at
// it, remove the dead arguments from the DeadArguments list.
//
for (Function::arg_iterator I = Fn.arg_begin(), E = Fn.arg_end(),
@@ -294,7 +294,7 @@ bool DAE::DeleteDeadVarargs(Function &Fn) {
/// instead.
bool DAE::RemoveDeadArgumentsFromCallers(Function &Fn)
{
- if (Fn.isDeclaration())
+ if (Fn.isDeclaration() || Fn.mayBeOverridden())
return false;
// Functions with local linkage should already have been handled.
@@ -379,7 +379,7 @@ DAE::Liveness DAE::SurveyUse(Value::const_use_iterator U,
// The value is returned from a function. It's only live when the
// function's return value is live. We use RetValNum here, for the case
// that U is really a use of an insertvalue instruction that uses the
- // orginal Use.
+ // original Use.
RetOrArg Use = CreateRet(RI->getParent()->getParent(), RetValNum);
// We might be live, depending on the liveness of Use.
return MarkIfNotLive(Use, MaybeLiveUses);
@@ -894,8 +894,8 @@ bool DAE::RemoveDeadStuffFromFunction(Function *F) {
// function empty.
NF->getBasicBlockList().splice(NF->begin(), F->getBasicBlockList());
- // Loop over the argument list, transfering uses of the old arguments over to
- // the new arguments, also transfering over the names as well.
+ // Loop over the argument list, transferring uses of the old arguments over to
+ // the new arguments, also transferring over the names as well.
i = 0;
for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(),
I2 = NF->arg_begin(); I != E; ++I, ++i)
diff --git a/lib/Transforms/IPO/GlobalOpt.cpp b/lib/Transforms/IPO/GlobalOpt.cpp
index d4cb71272f76..ded58aca75fc 100644
--- a/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/lib/Transforms/IPO/GlobalOpt.cpp
@@ -21,6 +21,7 @@
#include "llvm/Instructions.h"
#include "llvm/IntrinsicInst.h"
#include "llvm/Module.h"
+#include "llvm/Operator.h"
#include "llvm/Pass.h"
#include "llvm/Analysis/ConstantFolding.h"
#include "llvm/Analysis/MemoryBuiltins.h"
@@ -54,6 +55,7 @@ STATISTIC(NumCtorsEvaluated, "Number of static ctors evaluated");
STATISTIC(NumNestRemoved , "Number of nest attributes removed");
STATISTIC(NumAliasesResolved, "Number of global aliases resolved");
STATISTIC(NumAliasesRemoved, "Number of global aliases eliminated");
+STATISTIC(NumCXXDtorsRemoved, "Number of global C++ destructors removed");
namespace {
struct GlobalStatus;
@@ -77,6 +79,7 @@ namespace {
bool ProcessInternalGlobal(GlobalVariable *GV,Module::global_iterator &GVI,
const SmallPtrSet<const PHINode*, 16> &PHIUsers,
const GlobalStatus &GS);
+ bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn);
};
}
@@ -1191,9 +1194,11 @@ static Value *GetHeapSROAValue(Value *V, unsigned FieldNo,
const StructType *ST =
cast<StructType>(cast<PointerType>(PN->getType())->getElementType());
- Result =
+ PHINode *NewPN =
PHINode::Create(PointerType::getUnqual(ST->getElementType(FieldNo)),
+ PN->getNumIncomingValues(),
PN->getName()+".f"+Twine(FieldNo), PN);
+ Result = NewPN;
PHIsToRewrite.push_back(std::make_pair(PN, FieldNo));
} else {
llvm_unreachable("Unknown usable value");
@@ -1940,36 +1945,24 @@ bool GlobalOpt::OptimizeGlobalVars(Module &M) {
return Changed;
}
-/// FindGlobalCtors - Find the llvm.globalctors list, verifying that all
+/// FindGlobalCtors - Find the llvm.global_ctors list, verifying that all
/// initializers have an init priority of 65535.
GlobalVariable *GlobalOpt::FindGlobalCtors(Module &M) {
GlobalVariable *GV = M.getGlobalVariable("llvm.global_ctors");
if (GV == 0) return 0;
- // Found it, verify it's an array of { int, void()* }.
- const ArrayType *ATy =dyn_cast<ArrayType>(GV->getType()->getElementType());
- if (!ATy) return 0;
- const StructType *STy = dyn_cast<StructType>(ATy->getElementType());
- if (!STy || STy->getNumElements() != 2 ||
- !STy->getElementType(0)->isIntegerTy(32)) return 0;
- const PointerType *PFTy = dyn_cast<PointerType>(STy->getElementType(1));
- if (!PFTy) return 0;
- const FunctionType *FTy = dyn_cast<FunctionType>(PFTy->getElementType());
- if (!FTy || !FTy->getReturnType()->isVoidTy() ||
- FTy->isVarArg() || FTy->getNumParams() != 0)
- return 0;
-
// Verify that the initializer is simple enough for us to handle. We are
// only allowed to optimize the initializer if it is unique.
if (!GV->hasUniqueInitializer()) return 0;
-
- ConstantArray *CA = dyn_cast<ConstantArray>(GV->getInitializer());
- if (!CA) return 0;
-
+
+ if (isa<ConstantAggregateZero>(GV->getInitializer()))
+ return GV;
+ ConstantArray *CA = cast<ConstantArray>(GV->getInitializer());
+
for (User::op_iterator i = CA->op_begin(), e = CA->op_end(); i != e; ++i) {
- ConstantStruct *CS = dyn_cast<ConstantStruct>(*i);
- if (CS == 0) return 0;
-
+ if (isa<ConstantAggregateZero>(*i))
+ continue;
+ ConstantStruct *CS = cast<ConstantStruct>(*i);
if (isa<ConstantPointerNull>(CS->getOperand(1)))
continue;
@@ -1978,8 +1971,8 @@ GlobalVariable *GlobalOpt::FindGlobalCtors(Module &M) {
return 0;
// Init priority must be standard.
- ConstantInt *CI = dyn_cast<ConstantInt>(CS->getOperand(0));
- if (!CI || CI->getZExtValue() != 65535)
+ ConstantInt *CI = cast<ConstantInt>(CS->getOperand(0));
+ if (CI->getZExtValue() != 65535)
return 0;
}
@@ -1989,6 +1982,8 @@ GlobalVariable *GlobalOpt::FindGlobalCtors(Module &M) {
/// ParseGlobalCtors - Given a llvm.global_ctors list that we can understand,
/// return a list of the functions and null terminator as a vector.
static std::vector<Function*> ParseGlobalCtors(GlobalVariable *GV) {
+ if (GV->getInitializer()->isNullValue())
+ return std::vector<Function*>();
ConstantArray *CA = cast<ConstantArray>(GV->getInitializer());
std::vector<Function*> Result;
Result.reserve(CA->getNumOperands());
@@ -2019,7 +2014,7 @@ static GlobalVariable *InstallGlobalCtors(GlobalVariable *GCL,
const PointerType *PFTy = PointerType::getUnqual(FTy);
CSVals[1] = Constant::getNullValue(PFTy);
CSVals[0] = ConstantInt::get(Type::getInt32Ty(GCL->getContext()),
- 2147483647);
+ 0x7fffffff);
}
CAList.push_back(ConstantStruct::get(GCL->getContext(), CSVals, false));
}
@@ -2696,12 +2691,126 @@ bool GlobalOpt::OptimizeGlobalAliases(Module &M) {
return Changed;
}
+static Function *FindCXAAtExit(Module &M) {
+ Function *Fn = M.getFunction("__cxa_atexit");
+
+ if (!Fn)
+ return 0;
+
+ const FunctionType *FTy = Fn->getFunctionType();
+
+ // Checking that the function has the right return type, the right number of
+ // parameters and that they all have pointer types should be enough.
+ if (!FTy->getReturnType()->isIntegerTy() ||
+ FTy->getNumParams() != 3 ||
+ !FTy->getParamType(0)->isPointerTy() ||
+ !FTy->getParamType(1)->isPointerTy() ||
+ !FTy->getParamType(2)->isPointerTy())
+ return 0;
+
+ return Fn;
+}
+
+/// cxxDtorIsEmpty - Returns whether the given function is an empty C++
+/// destructor and can therefore be eliminated.
+/// Note that we assume that other optimization passes have already simplified
+/// the code so we only look for a function with a single basic block, where
+/// the only allowed instructions are 'ret' or 'call' to empty C++ dtor.
+static bool cxxDtorIsEmpty(const Function &Fn,
+ SmallPtrSet<const Function *, 8> &CalledFunctions) {
+ // FIXME: We could eliminate C++ destructors if they're readonly/readnone and
+ // nounwind, but that doesn't seem worth doing.
+ if (Fn.isDeclaration())
+ return false;
+
+ if (++Fn.begin() != Fn.end())
+ return false;
+
+ const BasicBlock &EntryBlock = Fn.getEntryBlock();
+ for (BasicBlock::const_iterator I = EntryBlock.begin(), E = EntryBlock.end();
+ I != E; ++I) {
+ if (const CallInst *CI = dyn_cast<CallInst>(I)) {
+ // Ignore debug intrinsics.
+ if (isa<DbgInfoIntrinsic>(CI))
+ continue;
+
+ const Function *CalledFn = CI->getCalledFunction();
+
+ if (!CalledFn)
+ return false;
+
+ SmallPtrSet<const Function *, 8> NewCalledFunctions(CalledFunctions);
+
+ // Don't treat recursive functions as empty.
+ if (!NewCalledFunctions.insert(CalledFn))
+ return false;
+
+ if (!cxxDtorIsEmpty(*CalledFn, NewCalledFunctions))
+ return false;
+ } else if (isa<ReturnInst>(*I))
+ return true;
+ else
+ return false;
+ }
+
+ return false;
+}
+
+bool GlobalOpt::OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) {
+ /// Itanium C++ ABI p3.3.5:
+ ///
+ /// After constructing a global (or local static) object, that will require
+ /// destruction on exit, a termination function is registered as follows:
+ ///
+ /// extern "C" int __cxa_atexit ( void (*f)(void *), void *p, void *d );
+ ///
+ /// This registration, e.g. __cxa_atexit(f,p,d), is intended to cause the
+ /// call f(p) when DSO d is unloaded, before all such termination calls
+ /// registered before this one. It returns zero if registration is
+ /// successful, nonzero on failure.
+
+ // This pass will look for calls to __cxa_atexit where the function is trivial
+ // and remove them.
+ bool Changed = false;
+
+ for (Function::use_iterator I = CXAAtExitFn->use_begin(),
+ E = CXAAtExitFn->use_end(); I != E;) {
+ // We're only interested in calls. Theoretically, we could handle invoke
+ // instructions as well, but neither llvm-gcc nor clang generate invokes
+ // to __cxa_atexit.
+ CallInst *CI = dyn_cast<CallInst>(*I++);
+ if (!CI)
+ continue;
+
+ Function *DtorFn =
+ dyn_cast<Function>(CI->getArgOperand(0)->stripPointerCasts());
+ if (!DtorFn)
+ continue;
+
+ SmallPtrSet<const Function *, 8> CalledFunctions;
+ if (!cxxDtorIsEmpty(*DtorFn, CalledFunctions))
+ continue;
+
+ // Just remove the call.
+ CI->replaceAllUsesWith(Constant::getNullValue(CI->getType()));
+ CI->eraseFromParent();
+
+ ++NumCXXDtorsRemoved;
+
+ Changed |= true;
+ }
+
+ return Changed;
+}
+
bool GlobalOpt::runOnModule(Module &M) {
bool Changed = false;
// Try to find the llvm.globalctors list.
GlobalVariable *GlobalCtors = FindGlobalCtors(M);
+ Function *CXAAtExitFn = FindCXAAtExit(M);
+
bool LocalChange = true;
while (LocalChange) {
LocalChange = false;
@@ -2718,6 +2827,11 @@ bool GlobalOpt::runOnModule(Module &M) {
// Resolve aliases, when possible.
LocalChange |= OptimizeGlobalAliases(M);
+
+ // Try to remove trivial global destructors.
+ if (CXAAtExitFn)
+ LocalChange |= OptimizeEmptyGlobalCXXDtors(CXAAtExitFn);
+
Changed |= LocalChange;
}
diff --git a/lib/Transforms/IPO/IPConstantPropagation.cpp b/lib/Transforms/IPO/IPConstantPropagation.cpp
index c7c293987a58..25c01346642b 100644
--- a/lib/Transforms/IPO/IPConstantPropagation.cpp
+++ b/lib/Transforms/IPO/IPConstantPropagation.cpp
@@ -186,7 +186,7 @@ bool IPCP::PropagateConstantReturn(Function &F) {
// Find the returned value
Value *V;
if (!STy)
- V = RI->getOperand(i);
+ V = RI->getOperand(0);
else
V = FindInsertedValue(RI->getOperand(0), i);
diff --git a/lib/Transforms/IPO/IPO.cpp b/lib/Transforms/IPO/IPO.cpp
index fbe90ce67591..21dcb519d9c9 100644
--- a/lib/Transforms/IPO/IPO.cpp
+++ b/lib/Transforms/IPO/IPO.cpp
@@ -45,7 +45,6 @@ void llvm::initializeIPO(PassRegistry &Registry) {
initializeStripDebugDeclarePass(Registry);
initializeStripDeadDebugInfoPass(Registry);
initializeStripNonDebugSymbolsPass(Registry);
- initializeSRETPromotionPass(Registry);
}
void LLVMInitializeIPO(LLVMPassRegistryRef R) {
diff --git a/lib/Transforms/IPO/Inliner.cpp b/lib/Transforms/IPO/Inliner.cpp
index 37eafd723bf8..57f3e772b569 100644
--- a/lib/Transforms/IPO/Inliner.cpp
+++ b/lib/Transforms/IPO/Inliner.cpp
@@ -29,7 +29,6 @@
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/Statistic.h"
-#include <set>
using namespace llvm;
STATISTIC(NumInlined, "Number of functions inlined");
diff --git a/lib/Transforms/IPO/Internalize.cpp b/lib/Transforms/IPO/Internalize.cpp
index 9b9ebad47225..7cb1d18f933d 100644
--- a/lib/Transforms/IPO/Internalize.cpp
+++ b/lib/Transforms/IPO/Internalize.cpp
@@ -126,6 +126,8 @@ bool InternalizePass::runOnModule(Module &M) {
// FIXME: maybe use private linkage?
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
if (!I->isDeclaration() && // Function must be defined here
+ // Available externally is really just a "declaration with a body".
+ !I->hasAvailableExternallyLinkage() &&
!I->hasLocalLinkage() && // Can't already have internal linkage
!ExternalNames.count(I->getName())) {// Not marked to keep external?
I->setLinkage(GlobalValue::InternalLinkage);
@@ -144,9 +146,6 @@ bool InternalizePass::runOnModule(Module &M) {
// Never internalize anchors used by the machine module info, else the info
// won't find them. (see MachineModuleInfo.)
- ExternalNames.insert("llvm.dbg.compile_units");
- ExternalNames.insert("llvm.dbg.global_variables");
- ExternalNames.insert("llvm.dbg.subprograms");
ExternalNames.insert("llvm.global_ctors");
ExternalNames.insert("llvm.global_dtors");
ExternalNames.insert("llvm.noinline");
diff --git a/lib/Transforms/IPO/LowerSetJmp.cpp b/lib/Transforms/IPO/LowerSetJmp.cpp
index b545f0bb267d..52ecf17b8f9b 100644
--- a/lib/Transforms/IPO/LowerSetJmp.cpp
+++ b/lib/Transforms/IPO/LowerSetJmp.cpp
@@ -430,7 +430,7 @@ void LowerSetJmp::TransformSetJmpCall(CallInst* Inst)
// This PHI node will be in the new block created from the
// splitBasicBlock call.
- PHINode* PHI = PHINode::Create(Type::getInt32Ty(Inst->getContext()),
+ PHINode* PHI = PHINode::Create(Type::getInt32Ty(Inst->getContext()), 2,
"SetJmpReturn", Inst);
// Coming from a call to setjmp, the return is 0.
diff --git a/lib/Transforms/IPO/MergeFunctions.cpp b/lib/Transforms/IPO/MergeFunctions.cpp
index cccffca6e384..f74144338a61 100644
--- a/lib/Transforms/IPO/MergeFunctions.cpp
+++ b/lib/Transforms/IPO/MergeFunctions.cpp
@@ -55,6 +55,7 @@
#include "llvm/Instructions.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
+#include "llvm/Operator.h"
#include "llvm/Pass.h"
#include "llvm/Support/CallSite.h"
#include "llvm/Support/Debug.h"
@@ -125,7 +126,7 @@ private:
const ComparableFunction ComparableFunction::EmptyKey = ComparableFunction(0);
const ComparableFunction ComparableFunction::TombstoneKey =
ComparableFunction(1);
-TargetData * const ComparableFunction::LookupOnly = (TargetData*)(-1);
+TargetData *const ComparableFunction::LookupOnly = (TargetData*)(-1);
}
@@ -212,7 +213,7 @@ bool FunctionComparator::isEquivalentType(const Type *Ty1,
return false;
}
- switch(Ty1->getTypeID()) {
+ switch (Ty1->getTypeID()) {
default:
llvm_unreachable("Unknown type!");
// Fall through in Release mode.
diff --git a/lib/Transforms/IPO/PartialInlining.cpp b/lib/Transforms/IPO/PartialInlining.cpp
index 2afd02985764..d9d1d106111e 100644
--- a/lib/Transforms/IPO/PartialInlining.cpp
+++ b/lib/Transforms/IPO/PartialInlining.cpp
@@ -95,7 +95,7 @@ Function* PartialInliner::unswitchFunction(Function* F) {
PHINode* OldPhi = dyn_cast<PHINode>(I);
if (!OldPhi) break;
- PHINode* retPhi = PHINode::Create(OldPhi->getType(), "", Ins);
+ PHINode* retPhi = PHINode::Create(OldPhi->getType(), 2, "", Ins);
OldPhi->replaceAllUsesWith(retPhi);
Ins = newReturnBlock->getFirstNonPHI();
diff --git a/lib/Transforms/IPO/PruneEH.cpp b/lib/Transforms/IPO/PruneEH.cpp
index d91c2c403aae..9470180c5657 100644
--- a/lib/Transforms/IPO/PruneEH.cpp
+++ b/lib/Transforms/IPO/PruneEH.cpp
@@ -27,7 +27,6 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/CFG.h"
-#include <set>
#include <algorithm>
using namespace llvm;
diff --git a/lib/Transforms/IPO/StructRetPromotion.cpp b/lib/Transforms/IPO/StructRetPromotion.cpp
deleted file mode 100644
index 584deacaff1b..000000000000
--- a/lib/Transforms/IPO/StructRetPromotion.cpp
+++ /dev/null
@@ -1,357 +0,0 @@
-//===-- StructRetPromotion.cpp - Promote sret arguments -------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This pass finds functions that return a struct (using a pointer to the struct
-// as the first argument of the function, marked with the 'sret' attribute) and
-// replaces them with a new function that simply returns each of the elements of
-// that struct (using multiple return values).
-//
-// This pass works under a number of conditions:
-// 1. The returned struct must not contain other structs
-// 2. The returned struct must only be used to load values from
-// 3. The placeholder struct passed in is the result of an alloca
-//
-//===----------------------------------------------------------------------===//
-
-#define DEBUG_TYPE "sretpromotion"
-#include "llvm/Transforms/IPO.h"
-#include "llvm/Constants.h"
-#include "llvm/DerivedTypes.h"
-#include "llvm/LLVMContext.h"
-#include "llvm/Module.h"
-#include "llvm/CallGraphSCCPass.h"
-#include "llvm/Instructions.h"
-#include "llvm/Analysis/CallGraph.h"
-#include "llvm/Support/CallSite.h"
-#include "llvm/Support/CFG.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/ADT/Statistic.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/Statistic.h"
-#include "llvm/Support/raw_ostream.h"
-using namespace llvm;
-
-STATISTIC(NumRejectedSRETUses , "Number of sret rejected due to unexpected uses");
-STATISTIC(NumSRET , "Number of sret promoted");
-namespace {
- /// SRETPromotion - This pass removes sret parameter and updates
- /// function to use multiple return value.
- ///
- struct SRETPromotion : public CallGraphSCCPass {
- virtual void getAnalysisUsage(AnalysisUsage &AU) const {
- CallGraphSCCPass::getAnalysisUsage(AU);
- }
-
- virtual bool runOnSCC(CallGraphSCC &SCC);
- static char ID; // Pass identification, replacement for typeid
- SRETPromotion() : CallGraphSCCPass(ID) {
- initializeSRETPromotionPass(*PassRegistry::getPassRegistry());
- }
-
- private:
- CallGraphNode *PromoteReturn(CallGraphNode *CGN);
- bool isSafeToUpdateAllCallers(Function *F);
- Function *cloneFunctionBody(Function *F, const StructType *STy);
- CallGraphNode *updateCallSites(Function *F, Function *NF);
- };
-}
-
-char SRETPromotion::ID = 0;
-INITIALIZE_PASS_BEGIN(SRETPromotion, "sretpromotion",
- "Promote sret arguments to multiple ret values", false, false)
-INITIALIZE_AG_DEPENDENCY(CallGraph)
-INITIALIZE_PASS_END(SRETPromotion, "sretpromotion",
- "Promote sret arguments to multiple ret values", false, false)
-
-Pass *llvm::createStructRetPromotionPass() {
- return new SRETPromotion();
-}
-
-bool SRETPromotion::runOnSCC(CallGraphSCC &SCC) {
- bool Changed = false;
-
- for (CallGraphSCC::iterator I = SCC.begin(), E = SCC.end(); I != E; ++I)
- if (CallGraphNode *NewNode = PromoteReturn(*I)) {
- SCC.ReplaceNode(*I, NewNode);
- Changed = true;
- }
-
- return Changed;
-}
-
-/// PromoteReturn - This method promotes function that uses StructRet paramater
-/// into a function that uses multiple return values.
-CallGraphNode *SRETPromotion::PromoteReturn(CallGraphNode *CGN) {
- Function *F = CGN->getFunction();
-
- if (!F || F->isDeclaration() || !F->hasLocalLinkage())
- return 0;
-
- // Make sure that function returns struct.
- if (F->arg_size() == 0 || !F->hasStructRetAttr() || F->doesNotReturn())
- return 0;
-
- DEBUG(dbgs() << "SretPromotion: Looking at sret function "
- << F->getName() << "\n");
-
- assert(F->getReturnType()->isVoidTy() && "Invalid function return type");
- Function::arg_iterator AI = F->arg_begin();
- const llvm::PointerType *FArgType = dyn_cast<PointerType>(AI->getType());
- assert(FArgType && "Invalid sret parameter type");
- const llvm::StructType *STy =
- dyn_cast<StructType>(FArgType->getElementType());
- assert(STy && "Invalid sret parameter element type");
-
- // Check if it is ok to perform this promotion.
- if (isSafeToUpdateAllCallers(F) == false) {
- DEBUG(dbgs() << "SretPromotion: Not all callers can be updated\n");
- ++NumRejectedSRETUses;
- return 0;
- }
-
- DEBUG(dbgs() << "SretPromotion: sret argument will be promoted\n");
- ++NumSRET;
- // [1] Replace use of sret parameter
- AllocaInst *TheAlloca = new AllocaInst(STy, NULL, "mrv",
- F->getEntryBlock().begin());
- Value *NFirstArg = F->arg_begin();
- NFirstArg->replaceAllUsesWith(TheAlloca);
-
- // [2] Find and replace ret instructions
- for (Function::iterator FI = F->begin(), FE = F->end(); FI != FE; ++FI)
- for(BasicBlock::iterator BI = FI->begin(), BE = FI->end(); BI != BE; ) {
- Instruction *I = BI;
- ++BI;
- if (isa<ReturnInst>(I)) {
- Value *NV = new LoadInst(TheAlloca, "mrv.ld", I);
- ReturnInst *NR = ReturnInst::Create(F->getContext(), NV, I);
- I->replaceAllUsesWith(NR);
- I->eraseFromParent();
- }
- }
-
- // [3] Create the new function body and insert it into the module.
- Function *NF = cloneFunctionBody(F, STy);
-
- // [4] Update all call sites to use new function
- CallGraphNode *NF_CFN = updateCallSites(F, NF);
-
- CallGraph &CG = getAnalysis<CallGraph>();
- NF_CFN->stealCalledFunctionsFrom(CG[F]);
-
- delete CG.removeFunctionFromModule(F);
- return NF_CFN;
-}
-
-// Check if it is ok to perform this promotion.
-bool SRETPromotion::isSafeToUpdateAllCallers(Function *F) {
-
- if (F->use_empty())
- // No users. OK to modify signature.
- return true;
-
- for (Value::use_iterator FnUseI = F->use_begin(), FnUseE = F->use_end();
- FnUseI != FnUseE; ++FnUseI) {
- // The function is passed in as an argument to (possibly) another function,
- // we can't change it!
- CallSite CS(*FnUseI);
- Instruction *Call = CS.getInstruction();
- // The function is used by something else than a call or invoke instruction,
- // we can't change it!
- if (!Call || !CS.isCallee(FnUseI))
- return false;
- CallSite::arg_iterator AI = CS.arg_begin();
- Value *FirstArg = *AI;
-
- if (!isa<AllocaInst>(FirstArg))
- return false;
-
- // Check FirstArg's users.
- for (Value::use_iterator ArgI = FirstArg->use_begin(),
- ArgE = FirstArg->use_end(); ArgI != ArgE; ++ArgI) {
- User *U = *ArgI;
- // If FirstArg user is a CallInst that does not correspond to current
- // call site then this function F is not suitable for sret promotion.
- if (CallInst *CI = dyn_cast<CallInst>(U)) {
- if (CI != Call)
- return false;
- }
- // If FirstArg user is a GEP whose all users are not LoadInst then
- // this function F is not suitable for sret promotion.
- else if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(U)) {
- // TODO : Use dom info and insert PHINodes to collect get results
- // from multiple call sites for this GEP.
- if (GEP->getParent() != Call->getParent())
- return false;
- for (Value::use_iterator GEPI = GEP->use_begin(), GEPE = GEP->use_end();
- GEPI != GEPE; ++GEPI)
- if (!isa<LoadInst>(*GEPI))
- return false;
- }
- // Any other FirstArg users make this function unsuitable for sret
- // promotion.
- else
- return false;
- }
- }
-
- return true;
-}
-
-/// cloneFunctionBody - Create a new function based on F and
-/// insert it into module. Remove first argument. Use STy as
-/// the return type for new function.
-Function *SRETPromotion::cloneFunctionBody(Function *F,
- const StructType *STy) {
-
- const FunctionType *FTy = F->getFunctionType();
- std::vector<const Type*> Params;
-
- // Attributes - Keep track of the parameter attributes for the arguments.
- SmallVector<AttributeWithIndex, 8> AttributesVec;
- const AttrListPtr &PAL = F->getAttributes();
-
- // Add any return attributes.
- if (Attributes attrs = PAL.getRetAttributes())
- AttributesVec.push_back(AttributeWithIndex::get(0, attrs));
-
- // Skip first argument.
- Function::arg_iterator I = F->arg_begin(), E = F->arg_end();
- ++I;
- // 0th parameter attribute is reserved for return type.
- // 1th parameter attribute is for first 1st sret argument.
- unsigned ParamIndex = 2;
- while (I != E) {
- Params.push_back(I->getType());
- if (Attributes Attrs = PAL.getParamAttributes(ParamIndex))
- AttributesVec.push_back(AttributeWithIndex::get(ParamIndex - 1, Attrs));
- ++I;
- ++ParamIndex;
- }
-
- // Add any fn attributes.
- if (Attributes attrs = PAL.getFnAttributes())
- AttributesVec.push_back(AttributeWithIndex::get(~0, attrs));
-
-
- FunctionType *NFTy = FunctionType::get(STy, Params, FTy->isVarArg());
- Function *NF = Function::Create(NFTy, F->getLinkage());
- NF->takeName(F);
- NF->copyAttributesFrom(F);
- NF->setAttributes(AttrListPtr::get(AttributesVec.begin(), AttributesVec.end()));
- F->getParent()->getFunctionList().insert(F, NF);
- NF->getBasicBlockList().splice(NF->begin(), F->getBasicBlockList());
-
- // Replace arguments
- I = F->arg_begin();
- E = F->arg_end();
- Function::arg_iterator NI = NF->arg_begin();
- ++I;
- while (I != E) {
- I->replaceAllUsesWith(NI);
- NI->takeName(I);
- ++I;
- ++NI;
- }
-
- return NF;
-}
-
-/// updateCallSites - Update all sites that call F to use NF.
-CallGraphNode *SRETPromotion::updateCallSites(Function *F, Function *NF) {
- CallGraph &CG = getAnalysis<CallGraph>();
- SmallVector<Value*, 16> Args;
-
- // Attributes - Keep track of the parameter attributes for the arguments.
- SmallVector<AttributeWithIndex, 8> ArgAttrsVec;
-
- // Get a new callgraph node for NF.
- CallGraphNode *NF_CGN = CG.getOrInsertFunction(NF);
-
- while (!F->use_empty()) {
- CallSite CS(*F->use_begin());
- Instruction *Call = CS.getInstruction();
-
- const AttrListPtr &PAL = F->getAttributes();
- // Add any return attributes.
- if (Attributes attrs = PAL.getRetAttributes())
- ArgAttrsVec.push_back(AttributeWithIndex::get(0, attrs));
-
- // Copy arguments, however skip first one.
- CallSite::arg_iterator AI = CS.arg_begin(), AE = CS.arg_end();
- Value *FirstCArg = *AI;
- ++AI;
- // 0th parameter attribute is reserved for return type.
- // 1th parameter attribute is for first 1st sret argument.
- unsigned ParamIndex = 2;
- while (AI != AE) {
- Args.push_back(*AI);
- if (Attributes Attrs = PAL.getParamAttributes(ParamIndex))
- ArgAttrsVec.push_back(AttributeWithIndex::get(ParamIndex - 1, Attrs));
- ++ParamIndex;
- ++AI;
- }
-
- // Add any function attributes.
- if (Attributes attrs = PAL.getFnAttributes())
- ArgAttrsVec.push_back(AttributeWithIndex::get(~0, attrs));
-
- AttrListPtr NewPAL = AttrListPtr::get(ArgAttrsVec.begin(), ArgAttrsVec.end());
-
- // Build new call instruction.
- Instruction *New;
- if (InvokeInst *II = dyn_cast<InvokeInst>(Call)) {
- New = InvokeInst::Create(NF, II->getNormalDest(), II->getUnwindDest(),
- Args.begin(), Args.end(), "", Call);
- cast<InvokeInst>(New)->setCallingConv(CS.getCallingConv());
- cast<InvokeInst>(New)->setAttributes(NewPAL);
- } else {
- New = CallInst::Create(NF, Args.begin(), Args.end(), "", Call);
- cast<CallInst>(New)->setCallingConv(CS.getCallingConv());
- cast<CallInst>(New)->setAttributes(NewPAL);
- if (cast<CallInst>(Call)->isTailCall())
- cast<CallInst>(New)->setTailCall();
- }
- Args.clear();
- ArgAttrsVec.clear();
- New->takeName(Call);
-
- // Update the callgraph to know that the callsite has been transformed.
- CallGraphNode *CalleeNode = CG[Call->getParent()->getParent()];
- CalleeNode->removeCallEdgeFor(Call);
- CalleeNode->addCalledFunction(New, NF_CGN);
-
- // Update all users of sret parameter to extract value using extractvalue.
- for (Value::use_iterator UI = FirstCArg->use_begin(),
- UE = FirstCArg->use_end(); UI != UE; ) {
- User *U2 = *UI++;
- CallInst *C2 = dyn_cast<CallInst>(U2);
- if (C2 && (C2 == Call))
- continue;
-
- GetElementPtrInst *UGEP = cast<GetElementPtrInst>(U2);
- ConstantInt *Idx = cast<ConstantInt>(UGEP->getOperand(2));
- Value *GR = ExtractValueInst::Create(New, Idx->getZExtValue(),
- "evi", UGEP);
- while(!UGEP->use_empty()) {
- // isSafeToUpdateAllCallers has checked that all GEP uses are
- // LoadInsts
- LoadInst *L = cast<LoadInst>(*UGEP->use_begin());
- L->replaceAllUsesWith(GR);
- L->eraseFromParent();
- }
- UGEP->eraseFromParent();
- continue;
- }
- Call->eraseFromParent();
- }
-
- return NF_CGN;
-}
-