blob: 471cd011c40f427ae5b1cb3387c2abb49f84f036 [file] [log] [blame]
//===- AnalysisManager.h - Analysis Management Infrastructure ---*- C++ -*-===//
//
// Part of the MLIR Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_PASS_ANALYSISMANAGER_H
#define MLIR_PASS_ANALYSISMANAGER_H
#include "mlir/IR/Module.h"
#include "mlir/Pass/PassInstrumentation.h"
#include "mlir/Support/LLVM.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Support/TypeName.h"
namespace mlir {
/// A special type used by analyses to provide an address that identifies a
/// particular analysis set or a concrete analysis type.
using AnalysisID = ClassID;
//===----------------------------------------------------------------------===//
// Analysis Preservation and Concept Modeling
//===----------------------------------------------------------------------===//
namespace detail {
/// A utility class to represent the analyses that are known to be preserved.
class PreservedAnalyses {
public:
/// Mark all analyses as preserved.
void preserveAll() { preservedIDs.insert(&allAnalysesID); }
/// Returns true if all analyses were marked preserved.
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:
/// An identifier used to represent all potential analyses.
constexpr static AnalysisID allAnalysesID = {};
/// The set of analyses that are known to be preserved.
SmallPtrSet<const void *, 2> preservedIDs;
};
namespace analysis_impl {
/// Trait to check if T provides a static 'isInvalidated' method.
template <typename T, typename... Args>
using has_is_invalidated = decltype(std::declval<T &>().isInvalidated(
std::declval<const PreservedAnalyses &>()));
/// Implementation of 'isInvalidated' if the analysis provides a definition.
template <typename AnalysisT>
std::enable_if_t<is_detected<has_is_invalidated, AnalysisT>::value, bool>
isInvalidated(AnalysisT &analysis, const PreservedAnalyses &pa) {
return analysis.isInvalidated(pa);
}
/// Default implementation of 'isInvalidated'.
template <typename AnalysisT>
std::enable_if_t<!is_detected<has_is_invalidated, AnalysisT>::value, bool>
isInvalidated(AnalysisT &analysis, const PreservedAnalyses &pa) {
return !pa.isPreserved<AnalysisT>();
}
} // end namespace analysis_impl
/// The abstract polymorphic base class representing an analysis.
struct AnalysisConcept {
virtual ~AnalysisConcept() = default;
/// A hook used to query analyses for invalidation. Given a preserved analysis
/// set, returns true if it should truly be invalidated. This allows for more
/// fine-tuned invalidation in cases where an analysis wasn't explicitly
/// marked preserved, but may be preserved(or invalidated) based upon other
/// properties such as analyses sets.
virtual bool isInvalidated(const PreservedAnalyses &pa) = 0;
};
/// A derived analysis model used to hold a specific analysis object.
template <typename AnalysisT> struct AnalysisModel : public AnalysisConcept {
template <typename... Args>
explicit AnalysisModel(Args &&... args)
: analysis(std::forward<Args>(args)...) {}
/// A hook used to query analyses for invalidation.
bool isInvalidated(const PreservedAnalyses &pa) final {
return analysis_impl::isInvalidated(analysis, pa);
}
/// The actual analysis object.
AnalysisT analysis;
};
/// This class represents a cache of analyses for a single operation. All
/// computation, caching, and invalidation of analyses takes place here.
class AnalysisMap {
/// A mapping between an analysis id and an existing analysis instance.
using ConceptMap =
DenseMap<const AnalysisID *, std::unique_ptr<AnalysisConcept>>;
/// Utility to return the name of the given analysis class.
template <typename AnalysisT> static StringRef getAnalysisName() {
StringRef name = llvm::getTypeName<AnalysisT>();
if (!name.consume_front("mlir::"))
name.consume_front("(anonymous namespace)::");
return name;
}
public:
explicit AnalysisMap(Operation *ir) : ir(ir) {}
/// Get an analysis for the current IR unit, computing it if necessary.
template <typename AnalysisT> AnalysisT &getAnalysis(PassInstrumentor *pi) {
auto *id = AnalysisID::getID<AnalysisT>();
typename ConceptMap::iterator it;
bool wasInserted;
std::tie(it, wasInserted) = analyses.try_emplace(id);
// If we don't have a cached analysis for this function, compute it directly
// and add it to the cache.
if (wasInserted) {
if (pi)
pi->runBeforeAnalysis(getAnalysisName<AnalysisT>(), id, ir);
it->second = std::make_unique<AnalysisModel<AnalysisT>>(ir);
if (pi)
pi->runAfterAnalysis(getAnalysisName<AnalysisT>(), id, ir);
}
return static_cast<AnalysisModel<AnalysisT> &>(*it->second).analysis;
}
/// Get a cached analysis instance if one exists, otherwise return null.
template <typename AnalysisT>
Optional<std::reference_wrapper<AnalysisT>> getCachedAnalysis() const {
auto res = analyses.find(AnalysisID::getID<AnalysisT>());
if (res == analyses.end())
return llvm::None;
return {static_cast<AnalysisModel<AnalysisT> &>(*res->second).analysis};
}
/// Returns the operation that this analysis map represents.
Operation *getOperation() const { return ir; }
/// Clear any held analyses.
void clear() { analyses.clear(); }
/// Invalidate any cached analyses based upon the given set of preserved
/// analyses.
void invalidate(const PreservedAnalyses &pa) {
// Remove any analyses that were invalidated.
for (auto it = analyses.begin(), e = analyses.end(); it != e;) {
auto curIt = it++;
if (curIt->second->isInvalidated(pa))
analyses.erase(curIt);
}
}
private:
Operation *ir;
ConceptMap analyses;
};
/// An analysis map that contains a map for the current operation, and a set of
/// maps for any child operations.
struct NestedAnalysisMap {
NestedAnalysisMap(Operation *op) : analyses(op) {}
/// Get the operation for this analysis map.
Operation *getOperation() const { return analyses.getOperation(); }
/// Invalidate any non preserved analyses.
void invalidate(const PreservedAnalyses &pa);
/// The cached analyses for nested operations.
DenseMap<Operation *, std::unique_ptr<NestedAnalysisMap>> childAnalyses;
/// The analyses for the owning module.
detail::AnalysisMap analyses;
};
} // namespace detail
//===----------------------------------------------------------------------===//
// Analysis Management
//===----------------------------------------------------------------------===//
class ModuleAnalysisManager;
/// This class represents an analysis manager for a particular operation
/// instance. It is used to manage and cache analyses on the operation as well
/// as those for child operations, via nested AnalysisManager instances
/// accessible via 'slice'. This class is intended to be passed around by value,
/// and cannot be constructed directly.
class AnalysisManager {
using ParentPointerT =
PointerUnion<const ModuleAnalysisManager *, const AnalysisManager *>;
public:
using PreservedAnalyses = detail::PreservedAnalyses;
// Query for a cached analysis on the given parent operation. The analysis may
// not exist and if it does it may be out-of-date.
template <typename AnalysisT>
Optional<std::reference_wrapper<AnalysisT>>
getCachedParentAnalysis(Operation *parentOp) const {
ParentPointerT curParent = parent;
while (auto *parentAM = curParent.dyn_cast<const AnalysisManager *>()) {
if (parentAM->impl->getOperation() == parentOp)
return parentAM->getCachedAnalysis<AnalysisT>();
curParent = parentAM->parent;
}
return None;
}
// Query for the given analysis for the current operation.
template <typename AnalysisT> AnalysisT &getAnalysis() {
return impl->analyses.getAnalysis<AnalysisT>(getPassInstrumentor());
}
// Query for a cached entry of the given analysis on the current operation.
template <typename AnalysisT>
Optional<std::reference_wrapper<AnalysisT>> getCachedAnalysis() const {
return impl->analyses.getCachedAnalysis<AnalysisT>();
}
/// Query for a analysis of a child operation, constructing it if necessary.
template <typename AnalysisT> AnalysisT &getChildAnalysis(Operation *op) {
return slice(op).template getAnalysis<AnalysisT>();
}
/// Query for a cached analysis of a child operation, or return null.
template <typename AnalysisT>
Optional<std::reference_wrapper<AnalysisT>>
getCachedChildAnalysis(Operation *op) const {
assert(op->getParentOp() == impl->getOperation());
auto it = impl->childAnalyses.find(op);
if (it == impl->childAnalyses.end())
return llvm::None;
return it->second->analyses.getCachedAnalysis<AnalysisT>();
}
/// Get an analysis manager for the given child operation.
AnalysisManager slice(Operation *op);
/// Invalidate any non preserved analyses,
void invalidate(const PreservedAnalyses &pa) { impl->invalidate(pa); }
/// Clear any held analyses.
void clear() {
impl->analyses.clear();
impl->childAnalyses.clear();
}
/// Returns a pass instrumentation object for the current operation. This
/// value may be null.
PassInstrumentor *getPassInstrumentor() const;
private:
AnalysisManager(const AnalysisManager *parent,
detail::NestedAnalysisMap *impl)
: parent(parent), impl(impl) {}
AnalysisManager(const ModuleAnalysisManager *parent,
detail::NestedAnalysisMap *impl)
: parent(parent), impl(impl) {}
/// A reference to the parent analysis manager, or the top-level module
/// analysis manager.
ParentPointerT parent;
/// A reference to the impl analysis map within the parent analysis manager.
detail::NestedAnalysisMap *impl;
/// Allow access to the constructor.
friend class ModuleAnalysisManager;
};
/// An analysis manager class specifically for the top-level module operation.
/// This class contains the memory allocations for all nested analysis managers,
/// and provides an anchor point. This is necessary because AnalysisManager is
/// designed to be a thin wrapper around an existing analysis map instance.
class ModuleAnalysisManager {
public:
ModuleAnalysisManager(ModuleOp module, PassInstrumentor *passInstrumentor)
: analyses(module), passInstrumentor(passInstrumentor) {}
ModuleAnalysisManager(const ModuleAnalysisManager &) = delete;
ModuleAnalysisManager &operator=(const ModuleAnalysisManager &) = delete;
/// Returns a pass instrumentation object for the current module. This value
/// may be null.
PassInstrumentor *getPassInstrumentor() const { return passInstrumentor; }
/// Returns an analysis manager for the current top-level module.
operator AnalysisManager() { return AnalysisManager(this, &analyses); }
private:
/// The analyses for the owning module.
detail::NestedAnalysisMap analyses;
/// An optional instrumentation object.
PassInstrumentor *passInstrumentor;
};
} // end namespace mlir
#endif // MLIR_PASS_ANALYSISMANAGER_H