Skip to content

Instantly share code, notes, and snippets.

@colematt
Last active September 18, 2024 11:05
Show Gist options
  • Select an option

  • Save colematt/69a0f4dd554a9e82f55936390e844eca to your computer and use it in GitHub Desktop.

Select an option

Save colematt/69a0f4dd554a9e82f55936390e844eca to your computer and use it in GitHub Desktop.
[Writing an LLVM Pass] #llvm
//===- MyPass.cpp - Example pass ----------------*- C++ -*-===//
#define DEBUG_TYPE "mypass"
#include "llvm/IR/Function.h"
#include "llvm/IR/Module.h"
#include "llvm/Pass.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Debug.h"
STATISTIC(FunctionCount, "Count of functions in the module");
using namespace llvm;
char MyPass::ID = 0;
INITIALIZE_PASS(MyPass, "mypass", "An example pass", false, false)
ModulePass *createMyPassPass() {
return new MyPass();
}
MyPass::MyPass() : ModulePass(ID) {
initializeMyPassPass(*PassRegistry::getPassRegistry());
}
bool MyPass::runOnModule(Module &M) {
for (auto &F : M)
++FunctionCount;
return true;
}
//===- MyPass.h - Example pass ----------------*- C++ -*-===//
#ifndef LLVM_TRANSFORMS_IPO_MYPASS_H
#define LLVM_TRANSFORMS_IPO_MYPASS_H
#include "llvm/Pass.h"
namespace llvm {
class Function;
class Module;
/// MyPass - An example pass
class MyPass : public ModulePass {
/* Other private data members and functions */
public:
static char ID;
explicit MyPass();
// Main run interface method.
bool runOnModule(Module &M) override;
StringRef getPassName() const override { return "MyPass" }
};
} // End llvm namespace
#endif

Writing an LLVM Pass

These steps add a pass to LLVM 3.8. This guide was co-authored with @anh-q

Before Writing

Pick a pass class based on its purpose.

Class Purpose
ModulePass Execute on entire module. Suitable for whole-program analysis and transformation.
FunctionPass Execute on each function. Does not provide inter-procedural view.
ImmutablePass Used to maintain information about current compilation state.
MachineFunctionPass Execute machine code generated for a specific target. Cannot modify anything related to IR.

Classify your pass based on its functionality. Below are some popular categories of an LLVM pass. The category will decide where your pass belong to in the source code hierarchy.

Category Functionality Declaration Location Implementation Location
Analysis Compute information that other passes can used. include/llvm/Analysis lib/Analysis
CodeGen Generate machine code from IR. include/llvm/CodeGen lib/CodeGen
Transform Modify program's IR. Within the Transform category, IPO means inter-procedural. include/llvm/Transforms lib/Transforms
Utility Passes that do not fit any of the above categories.

Writing your pass

  1. Write the declarations in the pass’ header file. Place header file in an appropriate location. See MyPass.h.
  2. Write the pass’ implementation in the pass’ source file. Place in the appropriate location. Override runOnModule() for module passes, runOnFunction() for functions, etc. See MyPass.c

Reminders

  • Use the STATISTIC macro if necessary for keeping pass performance statistics (e.g. number of optimizations performed, pass execution time, etc.). Logging the statistics requires passing the -stats option at the command line invocation.
  • Use the INITIALIZE_PASS macro to prepare the pass for registration. If MyPass uses analysis from other pass such as CallGraph, use INITIALIZE_PASS_BEGIN instead of INITIALIZE_PASS so that pass manager can run MyPass after the required analysis pass. See INITIALIZE_PASS_BEGIN in LLVM documentation for specific example. Also, remember to implement getAnalysisUsage()`.
  • Override doInitialization() or doFinalization() if needed. This does not apply to ImmutablePass.
  • Avoid the following:
    • Using std::endl or include <iostream>, because of unnecessary code to initialize iostream object at startup.
    • Declaring using namespace std, because of namespace conflicts with the LLVM namespace.

Registering Your Pass

  1. Register your pass source files with CMake:
  2. Add a add_llvm_loadable_module entry to the source’s directory’s CMakeLists.txt. This provides a rule to CMake to make your pass’ dynamically loaded library.
  3. Add a add_subdirectory to the source’s parent directory’s CMakeLists.txt. This provides a rule to CMake to add your pass’ directory to the build.
  4. Register your pass with the LLVM pass manager:
    1. Declare ModulePass *createMyPassPass(); in:
      1. include/llvm/Analysis/Passes.h (Analysis pass)
      2. include/llvm/CodeGen/Passes.h (CodeGen pass)
      3. include/llvm/Transforms/IPO.h (IPO pass)
      4. include/llvm/Transforms/MyPass/MyPass.h (Transform passes)
    2. Call initializeMyPassPass(registry); in:
      1. lib/Analysis/Analysis.cpp (Analysis pass)
      2. lib/CodeGen/CodeGen.cpp (CodeGen pass)
      3. lib/Transforms/IPO/IPO.cpp (IPO and Transform passes)
    3. Add a signature for (void) llvm::createMyPassPass(); in include/llvm/LinkAllPasses.h
    4. Add a signature for void initializeMyPassPass(PassRegistry&); to include/llvm/InitializePasses.h
  5. Place pass in LLVM pipeline:
    1. Optimization passes (passes which operate in IR): lib/Transforms/PassManagerBuilder.cpp
    2. CodeGen passes: lib/CodeGen/LLVMTargetMachine.cpp.
  6. Create a front-end invocation flag. See +Add a LLVM Front-end Invocation Option
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment