16

I'm trying to write a simple "modern" LLVM pass and use it with Clang. I want it to be able to be run with a command like: clang -Xclang -load -Xclang libMyPass.so file.cpp

There are a lot of manuals on how to integrate legacy pass into Clang. However, there is not so much info on the new pass manager. I came across a series of articles called 'Writing LLVM Pass in 2018'. But it only mentions a case when your pass code is placed inside the LLVM code tree. And I need the module to be built out-of-tree.

class MyPass : public llvm::PassInfoMixin<MyPass> {
public:
    llvm::PreservedAnalyses run(
        llvm::Function &F,
        llvm::FunctionAnalysisManager &FAM
    ) {
    // Pass code here
    }
};

extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() {
    return {
        LLVM_PLUGIN_API_VERSION, "MyPass", "v0.1",
        [](llvm::PassBuilder &PB) {
            PB.registerPipelineParsingCallback(
                [](
                    llvm::StringRef Name, llvm::FunctionPassManager &FPM,
                    llvm::ArrayRef <llvm::PassBuilder::PipelineElement>
                ) {
                    if (Name == "my-pass") {
                        FPM.addPass(MyPass());
                        return true;
                    }
                    return false;
                }
            );
        }
    };
}

At the moment, the pass is not being executed. I tried to look at -print-after-all option output and using std::cout to detect whether it has run. I cannot see my pass in logs. Neither I can see my debug output in the console window.

1
  • I have written a simple project together with a makefile to run simple passes through the command line. This is assuming you have the different tools installed. They might be helpful, github.com/JKRT/LLVMPasses . These passes can be run outside of LLVM Commented Feb 6, 2019 at 19:18

3 Answers 3

11

Two years later, there is still little information available on doing this. It took me a while to figure it out for my own pass.

First, to get clang to use your pass library:

clang -O1 -fexperimental-new-pass-manager -fpass-plugin=libMyPass.so file.cpp

Note that the -O1 flag is important (I'll come to that later).

As for your pass, there is no way I can find to force clang to use the "parsing pipeline", so we have to use one of the other optimizer extension points on llvm::PassBuilder (nearly all of these require an optimisation level of one or above, hence -O1). Since your pass's run method takes a Function, we need an extension point callback that takes a FunctionPassManger. We'll use registerVectorizerStartEPCallback.

class MyPass : public llvm::PassInfoMixin<MyPass> {
public:
    llvm::PreservedAnalyses run(
        llvm::Function &F,
        llvm::FunctionAnalysisManager &FAM
    ) {
    // Pass code here
    }
};

extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() {
    return {
        LLVM_PLUGIN_API_VERSION, "MyPass", "v0.1",
        [](llvm::PassBuilder &PB) {
            PB.registerVectorizerStartEPCallback(
                [](
                    llvm::FunctionPassManager &FPM,
                    llvm::OptimizationLevel &O
                ) {
                    FPM.addPass(MyPass());
                  }
            );
        }
    };
}

This code is only correct as of LLVM 11. Later versions have new PassBuilder extension points and have removed or renamed some of the older ones. registerVectorizerStartEPCallback is still there in LLVM 13 though.

I've spent a good while trying to figure out if I could force clang to use a custom pass by constructing a new FunctionPassMangager etc., but there seems to be no way to make clang use it. The optimizer extension points seem to be your only option if you want to stick with out-of-tree passes.

A tip - reading the LLVM header files or grepping through the clang source tree are very helpful when doing anything like this.

Sign up to request clarification or add additional context in comments.

1 Comment

This answer doesn't seem to apply anymore. Downvoting just for the sake of making it stand out.
6

It's possible now.

Sample plugin registration code:

PassPluginLibraryInfo getPassPluginInfo() {
    const auto callback = [](PassBuilder &PB) {
        PB.registerPipelineEarlySimplificationEPCallback([&](ModulePassManager &MPM, auto) {
            MPM.addPass(MyPass());
            return true;
        });
    };

    return {LLVM_PLUGIN_API_VERSION, "name", "0.0.1", callback};
};

extern "C" LLVM_ATTRIBUTE_WEAK PassPluginLibraryInfo llvmGetPassPluginInfo() {
    return getPassPluginInfo();
}

Sample clang invocation code (assuming you built your plugin as a shared library):

clang++ -O0 -g -fpass-plugin=pass.so ...

1 Comment

I upvoted the answer because it's the closest to working that I found, but there is a problem. I had to change the addPass() line: MPM.addPass(createModuleToFunctionPassAdaptor(MyPass()));
6

The answer by Mike Kot is almost correct. I take no credit. However, I really find it frustrating when I cannot find any complete and working answer, and the space for replying is too little for the full code.

Here's what works for me. The main difference from Mike's answer is that I had to use the createModuleToFunctionPassAdaptor(...)

#include "llvm/IR/PassManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"
#include <iostream>
using namespace llvm;

namespace {

struct MyPass : public PassInfoMixin<MyPass> {
  PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {
    outs() << "Running MyPass\n";
    return PreservedAnalyses::all();
  }
};

} // end anonymous namespace

PassPluginLibraryInfo getPassPluginInfo() {
  const auto callback = [](PassBuilder &PB) {
    PB.registerPipelineEarlySimplificationEPCallback(
        [&](ModulePassManager &MPM, auto) {
          MPM.addPass(createModuleToFunctionPassAdaptor(MyPass()));
          return true;
        });
  };

  return {LLVM_PLUGIN_API_VERSION, "name", "0.0.1", callback};
};

extern "C" LLVM_ATTRIBUTE_WEAK PassPluginLibraryInfo llvmGetPassPluginInfo() {
  return getPassPluginInfo();
}

For the rest, the command line works as described. The plugin can also take a relative path, like below:

clang -fpass-plugin=../plugin_dir/pass.so src_file.c -o obj_file

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.