Writing your own LLVM pass

All LLVM passes are subclasses of the pass class, and they implement functionality by overriding the virtual methods inherited from pass. LLVM applies a chain of analyses and transformations on the target program. A pass is an instance of the Pass LLVM class.

Getting ready

Let's see how to write a pass. Let's name the pass function block counter; once done, it will simply display the name of the function and count the basic blocks in that function when run. First, a Makefile needs to be written for the pass. Follow the given steps to write a Makefile:

  1. Open a Makefile in the llvm lib/Transform folder:
    $ vi Makefile
    
  2. Specify the path to the LLVM root folder and the library name, and make this pass a loadable module by specifying it in Makefile, as follows:
    LEVEL = ../../..
    LIBRARYNAME = FuncBlockCount
    LOADABLE_MODULE = 1
    include $(LEVEL)/Makefile.common

This Makefile specifies that all the .cpp files in the current directory are to be compiled and linked together in a shared object.

How to do it…

Do the following steps:

  1. Create a new .cpp file called FuncBlockCount.cpp:
    $ vi FuncBlockCount.cpp
    
  2. In this file, include some header files from LLVM:
    #include "llvm/Pass.h"
    #include "llvm/IR/Function.h"
    #include "llvm/Support/raw_ostream.h"
  3. Include the llvm namespace to enable access to LLVM functions:
    using namespace llvm;
  4. Then start with an anonymous namespace:
    namespace {
  5. Next declare the pass:
    struct FuncBlockCount : public FunctionPass {
  6. Then declare the pass identifier, which will be used by LLVM to identify the pass:
    static char ID;
    FuncBlockCount() : FunctionPass(ID) {}
  7. This step is one of the most important steps in writing a pass—writing a run function. Since this pass inherits FunctionPass and runs on a function, a runOnFunction is defined to be run on a function:
    bool runOnFunction(Function &F) override {
          errs() << "Function " << F.getName() << '
    ';
          return false;
        }
      };
    }

    This function prints the name of the function that is being processed.

  8. The next step is to initialize the pass ID:
    char FuncBlockCount::ID = 0;
  9. Finally, the pass needs to be registered, with a command-line argument and a name:
    static RegisterPass<FuncBlockCount> X("funcblockcount", "Function Block Count", false, false);

    Putting everything together, the entire code looks like this:

    #include "llvm/Pass.h"
    #include "llvm/IR/Function.h"
    #include "llvm/Support/raw_ostream.h"
    using namespace llvm;
    namespace {
    struct FuncBlockCount : public FunctionPass {
      static char ID;
      FuncBlockCount() : FunctionPass(ID) {}
      bool runOnFunction(Function &F) override {
        errs() << "Function " << F.getName() << '
    ';
        return false;
      }
               };
            }
           char FuncBlockCount::ID = 0;
           static RegisterPass<FuncBlockCount> X("funcblockcount", "Function Block Count", false, false);

How it works

A simple gmake command compiles the file, so a new file FuncBlockCount.so is generated at the LLVM root directory. This shared object file can be dynamically loaded to the opt tool to run it on a piece of LLVM IR code. How to load and run it will be demonstrated in the next section.

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.189.186.109