Reland [GlobalOpt] Preserve CFG analyses

The only place we modify the CFG is when calling
removeUnreachableBlocks(), so insert a callback there which invalidates
analyses for that function (or recomputes DT in the legacy PM).

We may delete functions, make sure to clear analyses for those
functions. (this was missed in the original revision)

Small compile time wins across the board:
https://llvm-compile-time-tracker.com/compare.php?from=f444ea8ce0aaaa5ec1a4129809389da15cc41396&to=698f41f4fc26cbf1006ed5d88e9d658edfc5b749&stat=instructions

Reviewed By: nikic

Differential Revision: https://reviews.llvm.org/D128145
This commit is contained in:
Arthur Eubanks 2022-06-19 12:20:11 -07:00
parent 7c5c4e781b
commit b5db65e0da
2 changed files with 54 additions and 19 deletions

View File

@ -1279,8 +1279,10 @@ static bool TryToShrinkGlobalToBoolean(GlobalVariable *GV, Constant *OtherVal) {
return true;
}
static bool deleteIfDead(
GlobalValue &GV, SmallPtrSetImpl<const Comdat *> &NotDiscardableComdats) {
static bool
deleteIfDead(GlobalValue &GV,
SmallPtrSetImpl<const Comdat *> &NotDiscardableComdats,
function_ref<void(Function &)> DeleteFnCallback = nullptr) {
GV.removeDeadConstantUsers();
if (!GV.isDiscardableIfUnused() && !GV.isDeclaration())
@ -1299,6 +1301,10 @@ static bool deleteIfDead(
return false;
LLVM_DEBUG(dbgs() << "GLOBAL DEAD: " << GV << "\n");
if (auto *F = dyn_cast<Function>(&GV)) {
if (DeleteFnCallback)
DeleteFnCallback(*F);
}
GV.eraseFromParent();
++NumDeleted;
return true;
@ -1906,7 +1912,9 @@ OptimizeFunctions(Module &M,
function_ref<TargetTransformInfo &(Function &)> GetTTI,
function_ref<BlockFrequencyInfo &(Function &)> GetBFI,
function_ref<DominatorTree &(Function &)> LookupDomTree,
SmallPtrSetImpl<const Comdat *> &NotDiscardableComdats) {
SmallPtrSetImpl<const Comdat *> &NotDiscardableComdats,
function_ref<void(Function &F)> ChangedCFGCallback,
function_ref<void(Function &F)> DeleteFnCallback) {
bool Changed = false;
@ -1926,7 +1934,7 @@ OptimizeFunctions(Module &M,
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 @@ OptimizeFunctions(Module &M,
// 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 @@ static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) {
return Changed;
}
static bool optimizeGlobalsInModule(
Module &M, const DataLayout &DL,
function_ref<TargetLibraryInfo &(Function &)> GetTLI,
function_ref<TargetTransformInfo &(Function &)> GetTTI,
function_ref<BlockFrequencyInfo &(Function &)> GetBFI,
function_ref<DominatorTree &(Function &)> LookupDomTree) {
static bool
optimizeGlobalsInModule(Module &M, const DataLayout &DL,
function_ref<TargetLibraryInfo &(Function &)> GetTLI,
function_ref<TargetTransformInfo &(Function &)> GetTTI,
function_ref<BlockFrequencyInfo &(Function &)> GetBFI,
function_ref<DominatorTree &(Function &)> LookupDomTree,
function_ref<void(Function &F)> ChangedCFGCallback,
function_ref<void(Function &F)> DeleteFnCallback) {
SmallPtrSet<const Comdat *, 8> NotDiscardableComdats;
bool Changed = false;
bool LocalChange = true;
@ -2438,7 +2446,8 @@ static bool optimizeGlobalsInModule(
// 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 @@ PreservedAnalyses GlobalOptPass::run(Module &M, ModuleAnalysisManager &AM) {
auto GetBFI = [&FAM](Function &F) -> BlockFrequencyInfo & {
return FAM.getResult<BlockFrequencyAnalysis>(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<FunctionAnalysisManagerModuleProxy>();
// The only place we modify the CFG is when calling
// removeUnreachableBlocks(), but there we make sure to invalidate analyses
// for modified functions.
PA.preserveSet<CFGAnalyses>();
return PA;
}
namespace {
@ -2525,8 +2547,13 @@ struct GlobalOptLegacyPass : public ModulePass {
return this->getAnalysis<BlockFrequencyInfoWrapperPass>(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 {

View File

@ -0,0 +1,8 @@
; RUN: opt -passes='function(require<no-op-function>),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
}