diff options
Diffstat (limited to 'contrib/llvm-project/llvm/lib/Transforms/IPO/GlobalOpt.cpp')
-rw-r--r-- | contrib/llvm-project/llvm/lib/Transforms/IPO/GlobalOpt.cpp | 169 |
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; } |