aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/llvm/lib/Transforms/IPO/GlobalOpt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/llvm/lib/Transforms/IPO/GlobalOpt.cpp')
-rw-r--r--contrib/llvm-project/llvm/lib/Transforms/IPO/GlobalOpt.cpp169
1 files changed, 127 insertions, 42 deletions
diff --git a/contrib/llvm-project/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/contrib/llvm-project/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index 951372adcfa9..ab1e41ebf9a9 100644
--- a/contrib/llvm-project/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/contrib/llvm-project/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -87,8 +87,11 @@ 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");
+STATISTIC(NumAtExitRemoved, "Number of atexit handlers removed");
STATISTIC(NumInternalFunc, "Number of internal functions");
STATISTIC(NumColdCC, "Number of functions marked coldcc");
+STATISTIC(NumIFuncsResolved, "Number of statically resolved IFuncs");
+STATISTIC(NumIFuncsDeleted, "Number of IFuncs removed");
static cl::opt<bool>
EnableColdCCStressTest("enable-coldcc-stress-test",
@@ -294,7 +297,7 @@ static bool CleanupConstantGlobalUsers(GlobalVariable *GV,
// A load from a uniform value is always the same, regardless of any
// applied offset.
Type *Ty = LI->getType();
- if (Constant *Res = ConstantFoldLoadFromUniformValue(Init, Ty)) {
+ if (Constant *Res = ConstantFoldLoadFromUniformValue(Init, Ty, DL)) {
LI->replaceAllUsesWith(Res);
EraseFromParent(LI);
continue;
@@ -304,6 +307,10 @@ static bool CleanupConstantGlobalUsers(GlobalVariable *GV,
APInt Offset(DL.getIndexTypeSizeInBits(PtrOp->getType()), 0);
PtrOp = PtrOp->stripAndAccumulateConstantOffsets(
DL, Offset, /* AllowNonInbounds */ true);
+ if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(PtrOp)) {
+ if (II->getIntrinsicID() == Intrinsic::threadlocal_address)
+ PtrOp = II->getArgOperand(0);
+ }
if (PtrOp == GV) {
if (auto *Value = ConstantFoldLoadFromConst(Init, Ty, Offset, DL)) {
LI->replaceAllUsesWith(Value);
@@ -316,6 +323,9 @@ static bool CleanupConstantGlobalUsers(GlobalVariable *GV,
} else if (MemIntrinsic *MI = dyn_cast<MemIntrinsic>(U)) { // memset/cpy/mv
if (getUnderlyingObject(MI->getRawDest()) == GV)
EraseFromParent(MI);
+ } else if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(U)) {
+ if (II->getIntrinsicID() == Intrinsic::threadlocal_address)
+ append_range(WorkList, II->users());
}
}
@@ -951,7 +961,7 @@ OptimizeGlobalAddressOfAllocation(GlobalVariable *GV, CallInst *CI,
GV->getContext(),
!isa<ConstantPointerNull>(SI->getValueOperand())),
InitBool, false, Align(1), SI->getOrdering(),
- SI->getSyncScopeID(), SI);
+ SI->getSyncScopeID(), SI->getIterator());
SI->eraseFromParent();
continue;
}
@@ -968,7 +978,8 @@ OptimizeGlobalAddressOfAllocation(GlobalVariable *GV, CallInst *CI,
// Replace the cmp X, 0 with a use of the bool value.
Value *LV = new LoadInst(InitBool->getValueType(), InitBool,
InitBool->getName() + ".val", false, Align(1),
- LI->getOrdering(), LI->getSyncScopeID(), LI);
+ LI->getOrdering(), LI->getSyncScopeID(),
+ LI->getIterator());
InitBoolUsed = true;
switch (ICI->getPredicate()) {
default: llvm_unreachable("Unknown ICmp Predicate!");
@@ -980,7 +991,7 @@ OptimizeGlobalAddressOfAllocation(GlobalVariable *GV, CallInst *CI,
break;
case ICmpInst::ICMP_ULE:
case ICmpInst::ICMP_EQ:
- LV = BinaryOperator::CreateNot(LV, "notinit", ICI);
+ LV = BinaryOperator::CreateNot(LV, "notinit", ICI->getIterator());
break;
case ICmpInst::ICMP_NE:
case ICmpInst::ICMP_UGT:
@@ -1202,7 +1213,7 @@ static bool TryToShrinkGlobalToBoolean(GlobalVariable *GV, Constant *OtherVal) {
for(auto *GVe : GVs){
DIGlobalVariable *DGV = GVe->getVariable();
DIExpression *E = GVe->getExpression();
- const DataLayout &DL = GV->getParent()->getDataLayout();
+ const DataLayout &DL = GV->getDataLayout();
unsigned SizeInOctets =
DL.getTypeAllocSizeInBits(NewGV->getValueType()) / 8;
@@ -1258,9 +1269,10 @@ static bool TryToShrinkGlobalToBoolean(GlobalVariable *GV, Constant *OtherVal) {
if (LoadInst *LI = dyn_cast<LoadInst>(StoredVal)) {
assert(LI->getOperand(0) == GV && "Not a copy!");
// Insert a new load, to preserve the saved value.
- StoreVal = new LoadInst(NewGV->getValueType(), NewGV,
- LI->getName() + ".b", false, Align(1),
- LI->getOrdering(), LI->getSyncScopeID(), LI);
+ StoreVal =
+ new LoadInst(NewGV->getValueType(), NewGV, LI->getName() + ".b",
+ false, Align(1), LI->getOrdering(),
+ LI->getSyncScopeID(), LI->getIterator());
} else {
assert((isa<CastInst>(StoredVal) || isa<SelectInst>(StoredVal)) &&
"This is not a form that we understand!");
@@ -1270,19 +1282,19 @@ static bool TryToShrinkGlobalToBoolean(GlobalVariable *GV, Constant *OtherVal) {
}
StoreInst *NSI =
new StoreInst(StoreVal, NewGV, false, Align(1), SI->getOrdering(),
- SI->getSyncScopeID(), SI);
+ SI->getSyncScopeID(), SI->getIterator());
NSI->setDebugLoc(SI->getDebugLoc());
} else {
// Change the load into a load of bool then a select.
LoadInst *LI = cast<LoadInst>(UI);
- LoadInst *NLI = new LoadInst(NewGV->getValueType(), NewGV,
- LI->getName() + ".b", false, Align(1),
- LI->getOrdering(), LI->getSyncScopeID(), LI);
+ LoadInst *NLI = new LoadInst(
+ NewGV->getValueType(), NewGV, LI->getName() + ".b", false, Align(1),
+ LI->getOrdering(), LI->getSyncScopeID(), LI->getIterator());
Instruction *NSI;
if (IsOneZero)
- NSI = new ZExtInst(NLI, LI->getType(), "", LI);
+ NSI = new ZExtInst(NLI, LI->getType(), "", LI->getIterator());
else
- NSI = SelectInst::Create(NLI, OtherVal, InitVal, "", LI);
+ NSI = SelectInst::Create(NLI, OtherVal, InitVal, "", LI->getIterator());
NSI->takeName(LI);
// Since LI is split into two instructions, NLI and NSI both inherit the
// same DebugLoc
@@ -1344,7 +1356,7 @@ static bool isPointerValueDeadOnEntryToFunction(
//
// We don't do an exhaustive search for memory operations - simply look
// through bitcasts as they're quite common and benign.
- const DataLayout &DL = GV->getParent()->getDataLayout();
+ const DataLayout &DL = GV->getDataLayout();
SmallVector<LoadInst *, 4> Loads;
SmallVector<StoreInst *, 4> Stores;
for (auto *U : GV->users()) {
@@ -1440,7 +1452,7 @@ processInternalGlobal(GlobalVariable *GV, const GlobalStatus &GS,
function_ref<TargetTransformInfo &(Function &)> GetTTI,
function_ref<TargetLibraryInfo &(Function &)> GetTLI,
function_ref<DominatorTree &(Function &)> LookupDomTree) {
- auto &DL = GV->getParent()->getDataLayout();
+ auto &DL = GV->getDataLayout();
// If this is a first class global and has only one accessing function and
// this function is non-recursive, we replace the global with a local alloca
// in this function.
@@ -1457,17 +1469,17 @@ processInternalGlobal(GlobalVariable *GV, const GlobalStatus &GS,
GS.AccessingFunction->doesNotRecurse() &&
isPointerValueDeadOnEntryToFunction(GS.AccessingFunction, GV,
LookupDomTree)) {
- const DataLayout &DL = GV->getParent()->getDataLayout();
+ const DataLayout &DL = GV->getDataLayout();
LLVM_DEBUG(dbgs() << "LOCALIZING GLOBAL: " << *GV << "\n");
- Instruction &FirstI = const_cast<Instruction&>(*GS.AccessingFunction
- ->getEntryBlock().begin());
+ BasicBlock::iterator FirstI =
+ GS.AccessingFunction->getEntryBlock().begin().getNonConst();
Type *ElemTy = GV->getValueType();
// FIXME: Pass Global's alignment when globals have alignment
- AllocaInst *Alloca = new AllocaInst(ElemTy, DL.getAllocaAddrSpace(), nullptr,
- GV->getName(), &FirstI);
+ AllocaInst *Alloca = new AllocaInst(ElemTy, DL.getAllocaAddrSpace(),
+ nullptr, GV->getName(), FirstI);
if (!isa<UndefValue>(GV->getInitializer()))
- new StoreInst(GV->getInitializer(), Alloca, &FirstI);
+ new StoreInst(GV->getInitializer(), Alloca, FirstI);
GV->replaceAllUsesWith(Alloca);
GV->eraseFromParent();
@@ -1528,7 +1540,7 @@ processInternalGlobal(GlobalVariable *GV, const GlobalStatus &GS,
++NumMarked;
}
if (!GV->getInitializer()->getType()->isSingleValueType()) {
- const DataLayout &DL = GV->getParent()->getDataLayout();
+ const DataLayout &DL = GV->getDataLayout();
if (SRAGlobal(GV, DL))
return true;
}
@@ -1857,7 +1869,7 @@ static void RemovePreallocated(Function *F) {
assert((isa<CallInst>(CB) || isa<InvokeInst>(CB)) &&
"Unknown indirect call type");
- CallBase *NewCB = CallBase::Create(CB, OpBundles, CB);
+ CallBase *NewCB = CallBase::Create(CB, OpBundles, CB->getIterator());
CB->replaceAllUsesWith(NewCB);
NewCB->takeName(CB);
CB->eraseFromParent();
@@ -2212,6 +2224,9 @@ static bool mayHaveOtherReferences(GlobalValue &GV, const LLVMUsed &U) {
static bool hasUsesToReplace(GlobalAlias &GA, const LLVMUsed &U,
bool &RenameTarget) {
+ if (GA.isWeakForLinker())
+ return false;
+
RenameTarget = false;
bool Ret = false;
if (hasUseOtherThanLLVMUsed(GA, U))
@@ -2317,18 +2332,19 @@ OptimizeGlobalAliases(Module &M,
}
static Function *
-FindCXAAtExit(Module &M, function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
+FindAtExitLibFunc(Module &M,
+ function_ref<TargetLibraryInfo &(Function &)> GetTLI,
+ LibFunc Func) {
// Hack to get a default TLI before we have actual Function.
auto FuncIter = M.begin();
if (FuncIter == M.end())
return nullptr;
auto *TLI = &GetTLI(*FuncIter);
- LibFunc F = LibFunc_cxa_atexit;
- if (!TLI->has(F))
+ if (!TLI->has(Func))
return nullptr;
- Function *Fn = M.getFunction(TLI->getName(F));
+ Function *Fn = M.getFunction(TLI->getName(Func));
if (!Fn)
return nullptr;
@@ -2336,17 +2352,18 @@ FindCXAAtExit(Module &M, function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
TLI = &GetTLI(*Fn);
// Make sure that the function has the correct prototype.
- if (!TLI->getLibFunc(*Fn, F) || F != LibFunc_cxa_atexit)
+ LibFunc F;
+ if (!TLI->getLibFunc(*Fn, F) || F != Func)
return nullptr;
return Fn;
}
-/// 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 simply check for 'ret'.
-static bool cxxDtorIsEmpty(const Function &Fn) {
+/// Returns whether the given function is an empty C++ destructor or atexit
+/// handler and can therefore be eliminated. Note that we assume that other
+/// optimization passes have already simplified the code so we simply check for
+/// 'ret'.
+static bool IsEmptyAtExitFunction(const Function &Fn) {
// FIXME: We could eliminate C++ destructors if they're readonly/readnone and
// nounwind, but that doesn't seem worth doing.
if (Fn.isDeclaration())
@@ -2362,7 +2379,7 @@ static bool cxxDtorIsEmpty(const Function &Fn) {
return false;
}
-static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) {
+static bool OptimizeEmptyGlobalAtExitDtors(Function *CXAAtExitFn, bool isCXX) {
/// Itanium C++ ABI p3.3.5:
///
/// After constructing a global (or local static) object, that will require
@@ -2375,8 +2392,8 @@ static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) {
/// 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.
+ // This pass will look for calls to __cxa_atexit or atexit where the function
+ // is trivial and remove them.
bool Changed = false;
for (User *U : llvm::make_early_inc_range(CXAAtExitFn->users())) {
@@ -2389,14 +2406,17 @@ static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) {
Function *DtorFn =
dyn_cast<Function>(CI->getArgOperand(0)->stripPointerCasts());
- if (!DtorFn || !cxxDtorIsEmpty(*DtorFn))
+ if (!DtorFn || !IsEmptyAtExitFunction(*DtorFn))
continue;
// Just remove the call.
CI->replaceAllUsesWith(Constant::getNullValue(CI->getType()));
CI->eraseFromParent();
- ++NumCXXDtorsRemoved;
+ if (isCXX)
+ ++NumCXXDtorsRemoved;
+ else
+ ++NumAtExitRemoved;
Changed |= true;
}
@@ -2404,6 +2424,62 @@ static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) {
return Changed;
}
+static Function *hasSideeffectFreeStaticResolution(GlobalIFunc &IF) {
+ if (IF.isInterposable())
+ return nullptr;
+
+ Function *Resolver = IF.getResolverFunction();
+ if (!Resolver)
+ return nullptr;
+
+ if (Resolver->isInterposable())
+ return nullptr;
+
+ // Only handle functions that have been optimized into a single basic block.
+ auto It = Resolver->begin();
+ if (++It != Resolver->end())
+ return nullptr;
+
+ BasicBlock &BB = Resolver->getEntryBlock();
+
+ if (any_of(BB, [](Instruction &I) { return I.mayHaveSideEffects(); }))
+ return nullptr;
+
+ auto *Ret = dyn_cast<ReturnInst>(BB.getTerminator());
+ if (!Ret)
+ return nullptr;
+
+ return dyn_cast<Function>(Ret->getReturnValue());
+}
+
+/// Find IFuncs that have resolvers that always point at the same statically
+/// known callee, and replace their callers with a direct call.
+static bool OptimizeStaticIFuncs(Module &M) {
+ bool Changed = false;
+ for (GlobalIFunc &IF : M.ifuncs())
+ if (Function *Callee = hasSideeffectFreeStaticResolution(IF))
+ if (!IF.use_empty() &&
+ (!Callee->isDeclaration() ||
+ none_of(IF.users(), [](User *U) { return isa<GlobalAlias>(U); }))) {
+ IF.replaceAllUsesWith(Callee);
+ NumIFuncsResolved++;
+ Changed = true;
+ }
+ return Changed;
+}
+
+static bool
+DeleteDeadIFuncs(Module &M,
+ SmallPtrSetImpl<const Comdat *> &NotDiscardableComdats) {
+ bool Changed = false;
+ for (GlobalIFunc &IF : make_early_inc_range(M.ifuncs()))
+ if (deleteIfDead(IF, NotDiscardableComdats)) {
+ NumIFuncsDeleted++;
+ Changed = true;
+ }
+ return Changed;
+}
+
static bool
optimizeGlobalsInModule(Module &M, const DataLayout &DL,
function_ref<TargetLibraryInfo &(Function &)> GetTLI,
@@ -2460,9 +2536,18 @@ optimizeGlobalsInModule(Module &M, const DataLayout &DL,
// Try to remove trivial global destructors if they are not removed
// already.
- Function *CXAAtExitFn = FindCXAAtExit(M, GetTLI);
- if (CXAAtExitFn)
- LocalChange |= OptimizeEmptyGlobalCXXDtors(CXAAtExitFn);
+ if (Function *CXAAtExitFn =
+ FindAtExitLibFunc(M, GetTLI, LibFunc_cxa_atexit))
+ LocalChange |= OptimizeEmptyGlobalAtExitDtors(CXAAtExitFn, true);
+
+ if (Function *AtExitFn = FindAtExitLibFunc(M, GetTLI, LibFunc_atexit))
+ LocalChange |= OptimizeEmptyGlobalAtExitDtors(AtExitFn, false);
+
+ // Optimize IFuncs whose callee's are statically known.
+ LocalChange |= OptimizeStaticIFuncs(M);
+
+ // Remove any IFuncs that are now dead.
+ LocalChange |= DeleteDeadIFuncs(M, NotDiscardableComdats);
Changed |= LocalChange;
}