Add support for preserving specific analyses in the analysis manager. Passes can now preserve specific analyses via 'markAnalysesPreserved'.

Example:

markAnalysesPreserved<DominanceInfo>();
markAnalysesPreserved<DominanceInfo, PostDominanceInfo>();

PiperOrigin-RevId: 237081454
This commit is contained in:
River Riddle 2019-03-06 11:04:22 -08:00 committed by jpienaar
parent b2fe39977e
commit 1d87b62afe
5 changed files with 200 additions and 11 deletions

View File

@ -44,9 +44,33 @@ public:
/// Mark all analyses as preserved. /// Mark all analyses as preserved.
void preserveAll() { preservedIDs.insert(&allAnalysesID); } void preserveAll() { preservedIDs.insert(&allAnalysesID); }
/// Returns if all analyses were marked preserved. /// Returns true if all analyses were marked preserved.
bool isAll() const { return preservedIDs.count(&allAnalysesID); } bool isAll() const { return preservedIDs.count(&allAnalysesID); }
/// Returns true if no analyses were marked preserved.
bool isNone() const { return preservedIDs.empty(); }
/// Preserve the given analyses.
template <typename AnalysisT> void preserve() {
preserve(AnalysisID::getID<AnalysisT>());
}
template <typename AnalysisT, typename AnalysisT2, typename... OtherAnalysesT>
void preserve() {
preserve<AnalysisT>();
preserve<AnalysisT2, OtherAnalysesT...>();
}
void preserve(const AnalysisID *id) { preservedIDs.insert(id); }
/// Returns if the given analysis has been marked as preserved. Note that this
/// simply checks for the presence of a given analysis ID and should not be
/// used as a general preservation checker.
template <typename AnalysisT> bool isPreserved() const {
return isPreserved(AnalysisID::getID<AnalysisT>());
}
bool isPreserved(const AnalysisID *id) const {
return preservedIDs.count(id);
}
private: private:
/// An identifier used to represent all potential analyses. /// An identifier used to represent all potential analyses.
constexpr static AnalysisID allAnalysesID = {}; constexpr static AnalysisID allAnalysesID = {};
@ -112,11 +136,12 @@ public:
/// Invalidate any cached analyses based upon the given set of preserved /// Invalidate any cached analyses based upon the given set of preserved
/// analyses. /// analyses.
void invalidate(const detail::PreservedAnalyses &pa) { void invalidate(const detail::PreservedAnalyses &pa) {
// If all analyses were preserved, then there is nothing to do here. // Remove any analyses not marked as preserved.
if (pa.isAll()) for (auto it = results.begin(), e = results.end(); it != e;) {
return; auto curIt = it++;
// TODO: Fine grain invalidation of analyses. if (!pa.isPreserved(curIt->first))
clear(); results.erase(curIt);
}
} }
private: private:
@ -154,7 +179,12 @@ public:
} }
/// Invalidate any non preserved analyses, /// Invalidate any non preserved analyses,
void invalidate(const detail::PreservedAnalyses &pa) { impl->invalidate(pa); } void invalidate(const detail::PreservedAnalyses &pa) {
// If all analyses were preserved, then there is nothing to do here.
if (pa.isAll())
return;
impl->invalidate(pa);
}
/// Clear any held analyses. /// Clear any held analyses.
void clear() { impl->clear(); } void clear() { impl->clear(); }
@ -189,13 +219,23 @@ public:
return slice(function).getResult<AnalysisT>(); return slice(function).getResult<AnalysisT>();
} }
/// Query for a cached analysis of a function, or return null.
template <typename AnalysisT>
llvm::Optional<std::reference_wrapper<AnalysisT>>
getCachedFunctionResult(Function *function) const {
auto it = functionAnalyses.find(function);
if (it == functionAnalyses.end())
return llvm::None;
return it->second.getCachedResult<AnalysisT>();
}
/// Query for the analysis of a module. The analysis is computed if it does /// Query for the analysis of a module. The analysis is computed if it does
/// not exist. /// not exist.
template <typename AnalysisT> AnalysisT &getResult() { template <typename AnalysisT> AnalysisT &getResult() {
return moduleAnalyses.getResult<AnalysisT>(); return moduleAnalyses.getResult<AnalysisT>();
} }
/// Query for a cached analysis for the module, or return nullptr. /// Query for a cached analysis for the module, or return null.
template <typename AnalysisT> template <typename AnalysisT>
llvm::Optional<std::reference_wrapper<AnalysisT>> getCachedResult() const { llvm::Optional<std::reference_wrapper<AnalysisT>> getCachedResult() const {
return moduleAnalyses.getCachedResult<AnalysisT>(); return moduleAnalyses.getCachedResult<AnalysisT>();

View File

@ -207,6 +207,14 @@ protected:
void markAllAnalysesPreserved() { void markAllAnalysesPreserved() {
this->getPassState().preservedAnalyses.preserveAll(); this->getPassState().preservedAnalyses.preserveAll();
} }
/// Mark the provided analyses as preserved.
template <typename... AnalysesT> void markAnalysesPreserved() {
this->getPassState().preservedAnalyses.template preserve<AnalysesT...>();
}
void markAnalysesPreserved(const AnalysisID *id) {
this->getPassState().preservedAnalyses.preserve(id);
}
}; };
} // end namespace detail } // end namespace detail
@ -242,6 +250,14 @@ struct ModulePass : public detail::PassModel<Module, T, ModulePassBase> {
AnalysisT &getFunctionAnalysisResult(Function *f) { AnalysisT &getFunctionAnalysisResult(Function *f) {
return this->getAnalysisManager().template getFunctionResult<AnalysisT>(f); return this->getAnalysisManager().template getFunctionResult<AnalysisT>(f);
} }
/// Returns an existing analysis result for a child function if it exists.
template <typename AnalysisT>
llvm::Optional<std::reference_wrapper<AnalysisT>>
getCachedFunctionAnalysisResult(Function *f) {
return this->getAnalysisManager()
.template getCachedFunctionResult<AnalysisT>(f);
}
}; };
} // end namespace mlir } // end namespace mlir

View File

@ -309,10 +309,21 @@ FunctionAnalysisManager ModuleAnalysisManager::slice(Function *function) {
/// Invalidate any non preserved analyses. /// Invalidate any non preserved analyses.
void ModuleAnalysisManager::invalidate(const detail::PreservedAnalyses &pa) { void ModuleAnalysisManager::invalidate(const detail::PreservedAnalyses &pa) {
// If all analyses were preserved, then there is nothing to do here.
if (pa.isAll()) if (pa.isAll())
return; return;
// TODO: Fine grain invalidation of analyses. // Invalidate the module analyses directly.
moduleAnalyses.clear(); moduleAnalyses.invalidate(pa);
functionAnalyses.clear();
// If no analyses were preserved, then just simply clear out the function
// analysis results.
if (pa.isNone()) {
functionAnalyses.clear();
return;
}
// Otherwise, invalidate each function analyses.
for (auto &analysisPair : functionAnalyses)
analysisPair.second.invalidate(pa);
} }

View File

@ -124,6 +124,12 @@ private:
/// Attempt to eliminate a redundant operation. /// Attempt to eliminate a redundant operation.
bool CSE::simplifyOperation(Instruction *op) { bool CSE::simplifyOperation(Instruction *op) {
// Don't simplify operations with nested blocks. We don't currently model
// equality comparisons correctly among other things. It is also unclear
// whether we would want to CSE such operations.
if (op->getNumBlockLists() != 0)
return false;
// TODO(riverriddle) We currently only eliminate non side-effecting // TODO(riverriddle) We currently only eliminate non side-effecting
// operations. // operations.
if (!op->hasNoSideEffect()) if (!op->hasNoSideEffect())
@ -230,6 +236,10 @@ void CSE::runOnFunction() {
for (auto *op : opsToErase) for (auto *op : opsToErase)
op->erase(); op->erase();
opsToErase.clear(); opsToErase.clear();
// We currently don't remove region operations, so mark dominance as
// preserved.
markAnalysesPreserved<DominanceInfo, PostDominanceInfo>();
} }
FunctionPassBase *mlir::createCSEPass() { return new CSE(); } FunctionPassBase *mlir::createCSEPass() { return new CSE(); }

View File

@ -0,0 +1,112 @@
//===- AnalysisManagerTest.cpp - AnalysisManager unit tests ---------------===//
//
// Copyright 2019 The MLIR Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
#include "mlir/Pass/AnalysisManager.h"
#include "mlir/IR/Builders.h"
#include "gtest/gtest.h"
using namespace mlir;
using namespace mlir::detail;
namespace {
/// Minimal class definitions for two analyses.
struct MyAnalysis {
MyAnalysis(Function *) {}
MyAnalysis(Module *) {}
};
struct OtherAnalysis {
OtherAnalysis(Function *) {}
OtherAnalysis(Module *) {}
};
TEST(AnalysisManagerTest, FineGrainModuleAnalysisPreservation) {
MLIRContext context;
// Test fine grain invalidation of the module analysis manager.
std::unique_ptr<Module> module(new Module(&context));
ModuleAnalysisManager mam(&*module);
// Query two different analyses, but only preserve one before invalidating.
mam.getResult<MyAnalysis>();
mam.getResult<OtherAnalysis>();
detail::PreservedAnalyses pa;
pa.preserve<MyAnalysis>();
mam.invalidate(pa);
// Check that only MyAnalysis is preserved.
EXPECT_TRUE(mam.getCachedResult<MyAnalysis>().hasValue());
EXPECT_FALSE(mam.getCachedResult<OtherAnalysis>().hasValue());
}
TEST(AnalysisManagerTest, FineGrainFunctionAnalysisPreservation) {
MLIRContext context;
Builder builder(&context);
// Create a function and a module.
std::unique_ptr<Module> module(new Module(&context));
Function *func1 =
new Function(builder.getUnknownLoc(), "foo",
builder.getFunctionType(llvm::None, llvm::None));
module->getFunctions().push_back(func1);
// Test fine grain invalidation of the function analysis manager.
ModuleAnalysisManager mam(&*module);
FunctionAnalysisManager fam = mam.slice(func1);
// Query two different analyses, but only preserve one before invalidating.
fam.getResult<MyAnalysis>();
fam.getResult<OtherAnalysis>();
detail::PreservedAnalyses pa;
pa.preserve<MyAnalysis>();
fam.invalidate(pa);
// Check that only MyAnalysis is preserved.
EXPECT_TRUE(fam.getCachedResult<MyAnalysis>().hasValue());
EXPECT_FALSE(fam.getCachedResult<OtherAnalysis>().hasValue());
}
TEST(AnalysisManagerTest, FineGrainChildFunctionAnalysisPreservation) {
MLIRContext context;
Builder builder(&context);
// Create a function and a module.
std::unique_ptr<Module> module(new Module(&context));
Function *func1 =
new Function(builder.getUnknownLoc(), "foo",
builder.getFunctionType(llvm::None, llvm::None));
module->getFunctions().push_back(func1);
// Test fine grain invalidation of a function analysis from within a module
// analysis manager.
ModuleAnalysisManager mam(&*module);
// Query two different analyses, but only preserve one before invalidating.
mam.getFunctionResult<MyAnalysis>(func1);
mam.getFunctionResult<OtherAnalysis>(func1);
detail::PreservedAnalyses pa;
pa.preserve<MyAnalysis>();
mam.invalidate(pa);
// Check that only MyAnalysis is preserved.
EXPECT_TRUE(mam.getCachedFunctionResult<MyAnalysis>(func1).hasValue());
EXPECT_FALSE(mam.getCachedFunctionResult<OtherAnalysis>(func1).hasValue());
}
} // end namespace