286 lines
11 KiB
C++
286 lines
11 KiB
C++
//===- llvm/IR/PassInstrumentation.h ----------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
/// \file
|
|
///
|
|
/// This file defines the Pass Instrumentation classes that provide
|
|
/// instrumentation points into the pass execution by PassManager.
|
|
///
|
|
/// There are two main classes:
|
|
/// - PassInstrumentation provides a set of instrumentation points for
|
|
/// pass managers to call on.
|
|
///
|
|
/// - PassInstrumentationCallbacks registers callbacks and provides access
|
|
/// to them for PassInstrumentation.
|
|
///
|
|
/// PassInstrumentation object is being used as a result of
|
|
/// PassInstrumentationAnalysis (so it is intended to be easily copyable).
|
|
///
|
|
/// Intended scheme of use for Pass Instrumentation is as follows:
|
|
/// - register instrumentation callbacks in PassInstrumentationCallbacks
|
|
/// instance. PassBuilder provides helper for that.
|
|
///
|
|
/// - register PassInstrumentationAnalysis with all the PassManagers.
|
|
/// PassBuilder handles that automatically when registering analyses.
|
|
///
|
|
/// - Pass Manager requests PassInstrumentationAnalysis from analysis manager
|
|
/// and gets PassInstrumentation as its result.
|
|
///
|
|
/// - Pass Manager invokes PassInstrumentation entry points appropriately,
|
|
/// passing StringRef identification ("name") of the pass currently being
|
|
/// executed and IRUnit it works on. There can be different schemes of
|
|
/// providing names in future, currently it is just a name() of the pass.
|
|
///
|
|
/// - PassInstrumentation wraps address of IRUnit into llvm::Any and passes
|
|
/// control to all the registered callbacks. Note that we specifically wrap
|
|
/// 'const IRUnitT*' so as to avoid any accidental changes to IR in
|
|
/// instrumenting callbacks.
|
|
///
|
|
/// - Some instrumentation points (BeforePass) allow to control execution
|
|
/// of a pass. For those callbacks returning false means pass will not be
|
|
/// executed.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_IR_PASSINSTRUMENTATION_H
|
|
#define LLVM_IR_PASSINSTRUMENTATION_H
|
|
|
|
#include "llvm/ADT/Any.h"
|
|
#include "llvm/ADT/FunctionExtras.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include <type_traits>
|
|
|
|
namespace llvm {
|
|
|
|
class PreservedAnalyses;
|
|
class StringRef;
|
|
|
|
/// This class manages callbacks registration, as well as provides a way for
|
|
/// PassInstrumentation to pass control to the registered callbacks.
|
|
class PassInstrumentationCallbacks {
|
|
public:
|
|
// Before/After callbacks accept IRUnits whenever appropriate, so they need
|
|
// to take them as constant pointers, wrapped with llvm::Any.
|
|
// For the case when IRUnit has been invalidated there is a different
|
|
// callback to use - AfterPassInvalidated.
|
|
// We call all BeforePassFuncs to determine if a pass should run or not.
|
|
// BeforeNonSkippedPassFuncs are called only if the pass should run.
|
|
// TODO: currently AfterPassInvalidated does not accept IRUnit, since passing
|
|
// already invalidated IRUnit is unsafe. There are ways to handle invalidated
|
|
// IRUnits in a safe way, and we might pursue that as soon as there is a
|
|
// useful instrumentation that needs it.
|
|
using BeforePassFunc = bool(StringRef, Any);
|
|
using BeforeSkippedPassFunc = void(StringRef, Any);
|
|
using BeforeNonSkippedPassFunc = void(StringRef, Any);
|
|
using AfterPassFunc = void(StringRef, Any, const PreservedAnalyses &);
|
|
using AfterPassInvalidatedFunc = void(StringRef, const PreservedAnalyses &);
|
|
using BeforeAnalysisFunc = void(StringRef, Any);
|
|
using AfterAnalysisFunc = void(StringRef, Any);
|
|
|
|
public:
|
|
PassInstrumentationCallbacks() {}
|
|
|
|
/// Copying PassInstrumentationCallbacks is not intended.
|
|
PassInstrumentationCallbacks(const PassInstrumentationCallbacks &) = delete;
|
|
void operator=(const PassInstrumentationCallbacks &) = delete;
|
|
|
|
template <typename CallableT>
|
|
void registerShouldRunOptionalPassCallback(CallableT C) {
|
|
ShouldRunOptionalPassCallbacks.emplace_back(std::move(C));
|
|
}
|
|
|
|
template <typename CallableT>
|
|
void registerBeforeSkippedPassCallback(CallableT C) {
|
|
BeforeSkippedPassCallbacks.emplace_back(std::move(C));
|
|
}
|
|
|
|
template <typename CallableT>
|
|
void registerBeforeNonSkippedPassCallback(CallableT C) {
|
|
BeforeNonSkippedPassCallbacks.emplace_back(std::move(C));
|
|
}
|
|
|
|
template <typename CallableT> void registerAfterPassCallback(CallableT C) {
|
|
AfterPassCallbacks.emplace_back(std::move(C));
|
|
}
|
|
|
|
template <typename CallableT>
|
|
void registerAfterPassInvalidatedCallback(CallableT C) {
|
|
AfterPassInvalidatedCallbacks.emplace_back(std::move(C));
|
|
}
|
|
|
|
template <typename CallableT>
|
|
void registerBeforeAnalysisCallback(CallableT C) {
|
|
BeforeAnalysisCallbacks.emplace_back(std::move(C));
|
|
}
|
|
|
|
template <typename CallableT>
|
|
void registerAfterAnalysisCallback(CallableT C) {
|
|
AfterAnalysisCallbacks.emplace_back(std::move(C));
|
|
}
|
|
|
|
/// Add a class name to pass name mapping for use by pass instrumentation.
|
|
void addClassToPassName(StringRef ClassName, StringRef PassName);
|
|
/// Get the pass name for a given pass class name.
|
|
StringRef getPassNameForClassName(StringRef ClassName);
|
|
|
|
private:
|
|
friend class PassInstrumentation;
|
|
|
|
/// These are only run on passes that are not required. They return false when
|
|
/// an optional pass should be skipped.
|
|
SmallVector<llvm::unique_function<BeforePassFunc>, 4>
|
|
ShouldRunOptionalPassCallbacks;
|
|
/// These are run on passes that are skipped.
|
|
SmallVector<llvm::unique_function<BeforeSkippedPassFunc>, 4>
|
|
BeforeSkippedPassCallbacks;
|
|
/// These are run on passes that are about to be run.
|
|
SmallVector<llvm::unique_function<BeforeNonSkippedPassFunc>, 4>
|
|
BeforeNonSkippedPassCallbacks;
|
|
/// These are run on passes that have just run.
|
|
SmallVector<llvm::unique_function<AfterPassFunc>, 4> AfterPassCallbacks;
|
|
/// These are run passes that have just run on invalidated IR.
|
|
SmallVector<llvm::unique_function<AfterPassInvalidatedFunc>, 4>
|
|
AfterPassInvalidatedCallbacks;
|
|
/// These are run on analyses that are about to be run.
|
|
SmallVector<llvm::unique_function<BeforeAnalysisFunc>, 4>
|
|
BeforeAnalysisCallbacks;
|
|
/// These are run on analyses that have been run.
|
|
SmallVector<llvm::unique_function<AfterAnalysisFunc>, 4>
|
|
AfterAnalysisCallbacks;
|
|
|
|
StringMap<std::string> ClassToPassName;
|
|
};
|
|
|
|
/// This class provides instrumentation entry points for the Pass Manager,
|
|
/// doing calls to callbacks registered in PassInstrumentationCallbacks.
|
|
class PassInstrumentation {
|
|
PassInstrumentationCallbacks *Callbacks;
|
|
|
|
// Template argument PassT of PassInstrumentation::runBeforePass could be two
|
|
// kinds: (1) a regular pass inherited from PassInfoMixin (happen when
|
|
// creating a adaptor pass for a regular pass); (2) a type-erased PassConcept
|
|
// created from (1). Here we want to make case (1) skippable unconditionally
|
|
// since they are regular passes. We call PassConcept::isRequired to decide
|
|
// for case (2).
|
|
template <typename PassT>
|
|
using has_required_t = decltype(std::declval<PassT &>().isRequired());
|
|
|
|
template <typename PassT>
|
|
static std::enable_if_t<is_detected<has_required_t, PassT>::value, bool>
|
|
isRequired(const PassT &Pass) {
|
|
return Pass.isRequired();
|
|
}
|
|
template <typename PassT>
|
|
static std::enable_if_t<!is_detected<has_required_t, PassT>::value, bool>
|
|
isRequired(const PassT &Pass) {
|
|
return false;
|
|
}
|
|
|
|
public:
|
|
/// Callbacks object is not owned by PassInstrumentation, its life-time
|
|
/// should at least match the life-time of corresponding
|
|
/// PassInstrumentationAnalysis (which usually is till the end of current
|
|
/// compilation).
|
|
PassInstrumentation(PassInstrumentationCallbacks *CB = nullptr)
|
|
: Callbacks(CB) {}
|
|
|
|
/// BeforePass instrumentation point - takes \p Pass instance to be executed
|
|
/// and constant reference to IR it operates on. \Returns true if pass is
|
|
/// allowed to be executed. These are only run on optional pass since required
|
|
/// passes must always be run. This allows these callbacks to print info when
|
|
/// they want to skip a pass.
|
|
template <typename IRUnitT, typename PassT>
|
|
bool runBeforePass(const PassT &Pass, const IRUnitT &IR) const {
|
|
if (!Callbacks)
|
|
return true;
|
|
|
|
bool ShouldRun = true;
|
|
if (!isRequired(Pass)) {
|
|
for (auto &C : Callbacks->ShouldRunOptionalPassCallbacks)
|
|
ShouldRun &= C(Pass.name(), llvm::Any(&IR));
|
|
}
|
|
|
|
if (ShouldRun) {
|
|
for (auto &C : Callbacks->BeforeNonSkippedPassCallbacks)
|
|
C(Pass.name(), llvm::Any(&IR));
|
|
} else {
|
|
for (auto &C : Callbacks->BeforeSkippedPassCallbacks)
|
|
C(Pass.name(), llvm::Any(&IR));
|
|
}
|
|
|
|
return ShouldRun;
|
|
}
|
|
|
|
/// AfterPass instrumentation point - takes \p Pass instance that has
|
|
/// just been executed and constant reference to \p IR it operates on.
|
|
/// \p IR is guaranteed to be valid at this point.
|
|
template <typename IRUnitT, typename PassT>
|
|
void runAfterPass(const PassT &Pass, const IRUnitT &IR,
|
|
const PreservedAnalyses &PA) const {
|
|
if (Callbacks)
|
|
for (auto &C : Callbacks->AfterPassCallbacks)
|
|
C(Pass.name(), llvm::Any(&IR), PA);
|
|
}
|
|
|
|
/// AfterPassInvalidated instrumentation point - takes \p Pass instance
|
|
/// that has just been executed. For use when IR has been invalidated
|
|
/// by \p Pass execution.
|
|
template <typename IRUnitT, typename PassT>
|
|
void runAfterPassInvalidated(const PassT &Pass,
|
|
const PreservedAnalyses &PA) const {
|
|
if (Callbacks)
|
|
for (auto &C : Callbacks->AfterPassInvalidatedCallbacks)
|
|
C(Pass.name(), PA);
|
|
}
|
|
|
|
/// BeforeAnalysis instrumentation point - takes \p Analysis instance
|
|
/// to be executed and constant reference to IR it operates on.
|
|
template <typename IRUnitT, typename PassT>
|
|
void runBeforeAnalysis(const PassT &Analysis, const IRUnitT &IR) const {
|
|
if (Callbacks)
|
|
for (auto &C : Callbacks->BeforeAnalysisCallbacks)
|
|
C(Analysis.name(), llvm::Any(&IR));
|
|
}
|
|
|
|
/// AfterAnalysis instrumentation point - takes \p Analysis instance
|
|
/// that has just been executed and constant reference to IR it operated on.
|
|
template <typename IRUnitT, typename PassT>
|
|
void runAfterAnalysis(const PassT &Analysis, const IRUnitT &IR) const {
|
|
if (Callbacks)
|
|
for (auto &C : Callbacks->AfterAnalysisCallbacks)
|
|
C(Analysis.name(), llvm::Any(&IR));
|
|
}
|
|
|
|
/// Handle invalidation from the pass manager when PassInstrumentation
|
|
/// is used as the result of PassInstrumentationAnalysis.
|
|
///
|
|
/// On attempt to invalidate just return false. There is nothing to become
|
|
/// invalid here.
|
|
template <typename IRUnitT, typename... ExtraArgsT>
|
|
bool invalidate(IRUnitT &, const class llvm::PreservedAnalyses &,
|
|
ExtraArgsT...) {
|
|
return false;
|
|
}
|
|
|
|
template <typename CallableT>
|
|
void pushBeforeNonSkippedPassCallback(CallableT C) {
|
|
if (Callbacks)
|
|
Callbacks->BeforeNonSkippedPassCallbacks.emplace_back(std::move(C));
|
|
}
|
|
void popBeforeNonSkippedPassCallback() {
|
|
if (Callbacks)
|
|
Callbacks->BeforeNonSkippedPassCallbacks.pop_back();
|
|
}
|
|
};
|
|
|
|
bool isSpecialPass(StringRef PassID, const std::vector<StringRef> &Specials);
|
|
|
|
} // namespace llvm
|
|
|
|
#endif
|