Skip to content

Commit

Permalink
[New PM] Introducing PassInstrumentation framework
Browse files Browse the repository at this point in the history
Pass Execution Instrumentation interface enables customizable instrumentation
of pass execution, as per "RFC: Pass Execution Instrumentation interface"
posted 06/07/2018 on llvm-dev@

The intent is to provide a common machinery to implement all
the pass-execution-debugging features like print-before/after,
opt-bisect, time-passes etc.

Here we get a basic implementation consisting of:
* PassInstrumentationCallbacks class that handles registration of callbacks
  and access to them.

* PassInstrumentation class that handles instrumentation-point interfaces
  that call into PassInstrumentationCallbacks.

* Callbacks accept StringRef which is just a name of the Pass right now.
  There were some ideas to pass an opaque wrapper for the pointer to pass instance,
  however it appears that pointer does not actually identify the instance
  (adaptors and managers might have the same address with the pass they govern).
  Hence it was decided to go simple for now and then later decide on what the proper
  mental model of identifying a "pass in a phase of pipeline" is.

* Callbacks accept llvm::Any serving as a wrapper for const IRUnit*, to remove direct dependencies
  on different IRUnits (e.g. Analyses).

* PassInstrumentationAnalysis analysis is explicitly requested from PassManager through
  usual AnalysisManager::getResult. All pass managers were updated to run that
  to get PassInstrumentation object for instrumentation calls.

* Using tuples/index_sequence getAnalysisResult helper to extract generic AnalysisManager's extra
  args out of a generic PassManager's extra args. This is the only way I was able to explicitly
  run getResult for PassInstrumentationAnalysis out of a generic code like PassManager::run or
  RepeatedPass::run.
  TODO: Upon lengthy discussions we agreed to accept this as an initial implementation
  and then get rid of getAnalysisResult by improving RepeatedPass implementation.

* PassBuilder takes PassInstrumentationCallbacks object to pass it further into
  PassInstrumentationAnalysis. Callbacks registration should be performed directly
  through PassInstrumentationCallbacks.

* new-pm tests updated to account for PassInstrumentationAnalysis being run

* Added PassInstrumentation tests to PassBuilderCallbacks unit tests.
  Other unit tests updated with registration of the now-required PassInstrumentationAnalysis.

Reviewers: chandlerc, philip.pfaffe
Differential Revision: https://reviews.llvm.org/D47858

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@342597 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
Fedor Sergeev committed Sep 19, 2018
1 parent c3779f5 commit 5ffd6db
Show file tree
Hide file tree
Showing 22 changed files with 720 additions and 35 deletions.
33 changes: 31 additions & 2 deletions include/llvm/Analysis/CGSCCPassManager.h
Expand Up @@ -364,6 +364,10 @@ class ModuleToPostOrderCGSCCPassAdaptor
InvalidSCCSet, nullptr, nullptr,
InlinedInternalEdges};

// Request PassInstrumentation from analysis manager, will use it to run
// instrumenting callbacks for the passes later.
PassInstrumentation PI = AM.getResult<PassInstrumentationAnalysis>(M);

PreservedAnalyses PA = PreservedAnalyses::all();
CG.buildRefSCCs();
for (auto RCI = CG.postorder_ref_scc_begin(),
Expand Down Expand Up @@ -428,8 +432,17 @@ class ModuleToPostOrderCGSCCPassAdaptor

UR.UpdatedRC = nullptr;
UR.UpdatedC = nullptr;

// Check the PassInstrumentation's BeforePass callbacks before
// running the pass, skip its execution completely if asked to
// (callback returns false).
if (!PI.runBeforePass<LazyCallGraph::SCC>(Pass, *C))
continue;

PreservedAnalyses PassPA = Pass.run(*C, CGAM, CG, UR);

PI.runAfterPass<LazyCallGraph::SCC>(Pass, *C);

// Update the SCC and RefSCC if necessary.
C = UR.UpdatedC ? UR.UpdatedC : C;
RC = UR.UpdatedRC ? UR.UpdatedRC : RC;
Expand Down Expand Up @@ -615,12 +628,20 @@ class CGSCCToFunctionPassAdaptor
if (CG.lookupSCC(*N) != CurrentC)
continue;

PreservedAnalyses PassPA = Pass.run(N->getFunction(), FAM);
Function &F = N->getFunction();

PassInstrumentation PI = FAM.getResult<PassInstrumentationAnalysis>(F);
if (!PI.runBeforePass<Function>(Pass, F))
continue;

PreservedAnalyses PassPA = Pass.run(F, FAM);

PI.runAfterPass<Function>(Pass, F);

// We know that the function pass couldn't have invalidated any other
// function's analyses (that's the contract of a function pass), so
// directly handle the function analysis manager's invalidation here.
FAM.invalidate(N->getFunction(), PassPA);
FAM.invalidate(F, PassPA);

// Then intersect the preserved set so that invalidation of module
// analyses will eventually occur when the module pass completes.
Expand Down Expand Up @@ -690,6 +711,8 @@ class DevirtSCCRepeatedPass
PreservedAnalyses run(LazyCallGraph::SCC &InitialC, CGSCCAnalysisManager &AM,
LazyCallGraph &CG, CGSCCUpdateResult &UR) {
PreservedAnalyses PA = PreservedAnalyses::all();
PassInstrumentation PI =
AM.getResult<PassInstrumentationAnalysis>(InitialC, CG);

// The SCC may be refined while we are running passes over it, so set up
// a pointer that we can update.
Expand Down Expand Up @@ -733,8 +756,14 @@ class DevirtSCCRepeatedPass
auto CallCounts = ScanSCC(*C, CallHandles);

for (int Iteration = 0;; ++Iteration) {

if (!PI.runBeforePass<LazyCallGraph::SCC>(Pass, *C))
continue;

PreservedAnalyses PassPA = Pass.run(*C, AM, CG, UR);

PI.runAfterPass<LazyCallGraph::SCC>(Pass, *C);

// If the SCC structure has changed, bail immediately and let the outer
// CGSCC layer handle any iteration to reflect the refined structure.
if (UR.UpdatedC && UR.UpdatedC != C) {
Expand Down
150 changes: 150 additions & 0 deletions include/llvm/IR/PassInstrumentation.h
@@ -0,0 +1,150 @@
//===- llvm/IR/PassInstrumentation.h ----------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
/// \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.
///
/// TODO: currently there is no way for a pass to opt-out of execution control
/// (e.g. become unskippable). PassManager is the only entity that determines
/// how pass instrumentation affects pass execution.
///
//===----------------------------------------------------------------------===//

#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/Support/TypeName.h"
#include <type_traits>

namespace llvm {

class PreservedAnalyses;

/// 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 Pass callbacks accept IRUnits, so they need to take them
// as pointers, wrapped with llvm::Any
using BeforePassFunc = bool(StringRef, Any);
using AfterPassFunc = void(StringRef, Any);
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 registerBeforePassCallback(CallableT C) {
BeforePassCallbacks.emplace_back(std::move(C));
}

template <typename CallableT> void registerAfterPassCallback(CallableT C) {
AfterPassCallbacks.emplace_back(std::move(C));
}

private:
friend class PassInstrumentation;

SmallVector<llvm::unique_function<BeforePassFunc>, 4> BeforePassCallbacks;
SmallVector<llvm::unique_function<AfterPassFunc>, 4> AfterPassCallbacks;
};

/// This class provides instrumentation entry points for the Pass Manager,
/// doing calls to callbacks registered in PassInstrumentationCallbacks.
class PassInstrumentation {
PassInstrumentationCallbacks *Callbacks;

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.
template <typename IRUnitT, typename PassT>
bool runBeforePass(const PassT &Pass, const IRUnitT &IR) const {
if (!Callbacks)
return true;

bool ShouldRun = true;
for (auto &C : Callbacks->BeforePassCallbacks)
ShouldRun &= C(Pass.name(), llvm::Any(&IR));
return ShouldRun;
}

/// AfterPass instrumentation point - takes \p Pass instance that has
/// just been executed and constant reference to IR it operates on.
template <typename IRUnitT, typename PassT>
void runAfterPass(const PassT &Pass, const IRUnitT &IR) const {
if (Callbacks)
for (auto &C : Callbacks->AfterPassCallbacks)
C(Pass.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;
}
};

} // namespace llvm

#endif

0 comments on commit 5ffd6db

Please sign in to comment.