diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp --- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp +++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp @@ -1279,8 +1279,10 @@ return true; } -static bool deleteIfDead( - GlobalValue &GV, SmallPtrSetImpl &NotDiscardableComdats) { +static bool +deleteIfDead(GlobalValue &GV, + SmallPtrSetImpl &NotDiscardableComdats, + function_ref DeleteFnCallback = nullptr) { GV.removeDeadConstantUsers(); if (!GV.isDiscardableIfUnused() && !GV.isDeclaration()) @@ -1299,6 +1301,10 @@ return false; LLVM_DEBUG(dbgs() << "GLOBAL DEAD: " << GV << "\n"); + if (auto *F = dyn_cast(&GV)) { + if (DeleteFnCallback) + DeleteFnCallback(*F); + } GV.eraseFromParent(); ++NumDeleted; return true; @@ -1906,7 +1912,9 @@ function_ref GetTTI, function_ref GetBFI, function_ref LookupDomTree, - SmallPtrSetImpl &NotDiscardableComdats) { + SmallPtrSetImpl &NotDiscardableComdats, + function_ref ChangedCFGCallback, + function_ref DeleteFnCallback) { bool Changed = false; @@ -1926,7 +1934,7 @@ if (!F.hasName() && !F.isDeclaration() && !F.hasLocalLinkage()) F.setLinkage(GlobalValue::InternalLinkage); - if (deleteIfDead(F, NotDiscardableComdats)) { + if (deleteIfDead(F, NotDiscardableComdats, DeleteFnCallback)) { Changed = true; continue; } @@ -1939,13 +1947,11 @@ // So, remove unreachable blocks from the function, because a) there's // no point in analyzing them and b) GlobalOpt should otherwise grow // some more complicated logic to break these cycles. - // Removing unreachable blocks might invalidate the dominator so we - // recalculate it. + // Notify the analysis manager that we've modified the function's CFG. if (!F.isDeclaration()) { if (removeUnreachableBlocks(F)) { - auto &DT = LookupDomTree(F); - DT.recalculate(F); Changed = true; + ChangedCFGCallback(F); } } @@ -2408,12 +2414,14 @@ return Changed; } -static bool optimizeGlobalsInModule( - Module &M, const DataLayout &DL, - function_ref GetTLI, - function_ref GetTTI, - function_ref GetBFI, - function_ref LookupDomTree) { +static bool +optimizeGlobalsInModule(Module &M, const DataLayout &DL, + function_ref GetTLI, + function_ref GetTTI, + function_ref GetBFI, + function_ref LookupDomTree, + function_ref ChangedCFGCallback, + function_ref DeleteFnCallback) { SmallPtrSet NotDiscardableComdats; bool Changed = false; bool LocalChange = true; @@ -2438,7 +2446,8 @@ // Delete functions that are trivially dead, ccc -> fastcc LocalChange |= OptimizeFunctions(M, GetTLI, GetTTI, GetBFI, LookupDomTree, - NotDiscardableComdats); + NotDiscardableComdats, ChangedCFGCallback, + DeleteFnCallback); // Optimize global_ctors list. LocalChange |= @@ -2491,10 +2500,23 @@ auto GetBFI = [&FAM](Function &F) -> BlockFrequencyInfo & { return FAM.getResult(F); }; + auto ChangedCFGCallback = [&FAM](Function &F) { + FAM.invalidate(F, PreservedAnalyses::none()); + }; + auto DeleteFnCallback = [&FAM](Function &F) { FAM.clear(F, F.getName()); }; - if (!optimizeGlobalsInModule(M, DL, GetTLI, GetTTI, GetBFI, LookupDomTree)) + if (!optimizeGlobalsInModule(M, DL, GetTLI, GetTTI, GetBFI, LookupDomTree, + ChangedCFGCallback, DeleteFnCallback)) return PreservedAnalyses::all(); - return PreservedAnalyses::none(); + + PreservedAnalyses PA = PreservedAnalyses::none(); + // We made sure to clear analyses for deleted functions. + PA.preserve(); + // The only place we modify the CFG is when calling + // removeUnreachableBlocks(), but there we make sure to invalidate analyses + // for modified functions. + PA.preserveSet(); + return PA; } namespace { @@ -2525,8 +2547,13 @@ return this->getAnalysis(F).getBFI(); }; - return optimizeGlobalsInModule(M, DL, GetTLI, GetTTI, GetBFI, - LookupDomTree); + auto ChangedCFGCallback = [&LookupDomTree](Function &F) { + auto &DT = LookupDomTree(F); + DT.recalculate(F); + }; + + return optimizeGlobalsInModule(M, DL, GetTLI, GetTTI, GetBFI, LookupDomTree, + ChangedCFGCallback, nullptr); } void getAnalysisUsage(AnalysisUsage &AU) const override { diff --git a/llvm/test/Transforms/GlobalOpt/analysis-invalidation.ll b/llvm/test/Transforms/GlobalOpt/analysis-invalidation.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/GlobalOpt/analysis-invalidation.ll @@ -0,0 +1,8 @@ +; RUN: opt -passes='function(require),globalopt' %s -debug-pass-manager -S 2>&1 | FileCheck %s + +; CHECK: Clearing all analysis results for: f +; CHECK-NOT: @f + +define internal void @f() { + ret void +}