Sunday, February 19, 2012

LLVM pass on Windows: integrating with opt

I've been using LLVM for a few weeks now and my primary interaction with it has been writing custom passes. LLVM has a very nice system for handling user defined passes: you create an independent plug-in and load it dynamically using the opt tool. A full explanation can be found in the LLVM pass documentation.

Unfortunately, plug-ins are dynamically linked in LLVM which excludes this approach on Windows (without relying on Cygwin). For all my searching I was not able to find instructions on integrating a pass directly with the opt tool but I needed to support custom passes on Windows without using Cygwin. Mostly as a note for myself, I am listing here the steps I used to create my own custom pass as part of the opt tool; hopefully they will be useful for others as well.

I've followed as best I could the format used by existing infrastructure within LLVM. This does not mean that I have not missed something critical or what I have provided adheres to LLVM policy and/or standards.

N.B. These examples assume you are:
  •  already familiar with writing a custom pass as described by the LLVM documentation
  •  capable of understanding Makefiles and how to modify them to suit your needs
  •  using llvm-3.0 (that is the version used in these examples)
These examples will build a pass named mypass which will simply print out all function names. This pass will be part of the Custom library (also created here).

The first step is to create a new directory named Custom under llvm/lib/Transforms. This is where all passes for the new library will live. Create a very simple function pass and place it in this new directory - I used the following (MyPass.cpp)
#define DEBUG_TYPE "mypass"
#include "llvm/Function.h"
#include "llvm/Pass.h"
#include "llvm/Transforms/Custom.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

namespace {
  struct MyPass : public FunctionPass {
    static char ID;
    MyPass() : FunctionPass(ID) {
      initializeMyPassPass(*PassRegistry::getPassRegistry());
    }

    /*
     * Just print the function name 
     */
    bool runOnFunction(Function &F) {
      bool Changed = false;
      errs().write_escaped(F.getName()) << "\n";
      return Changed;
    }
  };
}

char MyPass::ID = 0;
INITIALIZE_PASS(MyPass, "mypass", "Print all function names",
                false, false)

FunctionPass *llvm::createMyPassPass() {
  return new MyPass();
}
To load this, and multiple other custom passes, it is necessary to create an initialization routine that iterates over all passes in the library. In this example the only call in that routine is the call to the pass above. Still in the Custom directory, create the following as Custom.cpp:
#include "llvm/InitializePasses.h"
#include "llvm-c/Initialization.h"

using namespace llvm;

/// initializeCustom - Initialize all passes in the Custom library
void llvm::initializeCustom(PassRegistry &Registry) {
  initializeMyPassPass(Registry);
}

/// LLVMInitializeCustom - C binding for initializeCustom.
void LLVMInitializeCustom(LLVMPassRegistryRef R) {
  initializeCustom(*unwrap(R));
}
You will also need to support either build system with a Makefile and CMakeLists.txt.
LEVEL = ../../..
LIBRARYNAME = LLVMCustom
BUILD_ARCHIVE = 1

include $(LEVEL)/Makefile.common

add_llvm_library(LLVMCustom
  MyPass.cpp
  Custom.cpp
  )
That is the entirety of writing a new pass library for LLVM. The remainder of the work is integrating that new code into the larger system.

First, you need to define a way for LLVM to create an instance of your pass. Save the following as Custom.h in llvm/include/llvm/Transforms/
#ifndef LLVM_CUSTOM_H
#define LLVM_CUSTOM_H

namespace llvm {

FunctionPass *createMyPassPass();

}

#endif
Then you need to provide the prototypes for the two initialization routines. Add the following two lines to llvm/include/llvm/InitializePasses.h - make sure you do so inside the llvm namespace.
void initializeCustom(PassRegistry&);
void initializeMyPassPass(PassRegistry&);
Next, you need to set any passes to be linked by adding entries into llvm/include/llvm/LinkAllPasses.h
#include "llvm/Transforms/Custom.h"

// This part must reside in the constructor of struct ForcePassLinking
(void) llvm::createMyPassPass();
Updates are required to all relevant makefiles in the source tree. You will need to adjust the following to account for the additions discussed above:
  • llvm/lib/Transforms/Makefile
  • llvm/lib/Transforms/CMakeLists.txt
  • llvm/tools/opt/Makefile (add link dependency for 'custom')
  • llvm/tools/opt/CMakeLists.txt (add link dependency for 'custom')
  • llvm/tools/bugpoint/Makefile (add link dependency for 'custom')
  • llvm/tools/bugpoint/CMakeLists.txt (add link dependency for 'custom')
Finally, add the code to load the custom library in opt. Add the initialization call in the appropriate place in opt.cpp
initializeCustom(Registry);
The new pass should now be visible (on all platforms) without having to load a library with opt.
    -mergereturn                               - Unify function exit nodes
    -module-debuginfo                          - Decodes module-level debug info
    -mypass                                    - Print all function names
    -no-aa                                     - No Alias Analysis (always returns 'may' alias)
    -no-path-profile                           - No Path Profile Information