Showing posts with label llvm. Show all posts
Showing posts with label llvm. Show all posts

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

Wednesday, January 25, 2012

Return on Investment

About a year ago, a friend and I were discussing the evolution of learning new tools while working under time constraints. He claimed that, regardless of the pressure, it is almost always better to sacrifice the upfront time in learning a new tool than to stay in your comfort zone to construct the illusion of progress. By illusion he simply meant that early stage progress is shadowed by the long-term effect of having to maintain and update a homegrown solution.

He used the following illustration to convey his point:



The effect is that if you choose to develop your own tools you satisfy the immediate gratification of results but the final solution is more likely to end up as a house of cards.

I had forgotten about this conversation until recently. The program I am currently working on had me in line to provide relevant results in a shortened amount of time. I am working with the llvm compiler infrastructure and it's unique assembly language. Part of what I needed to do is understand, at a high level, the contents of an llvm assembly file. My natural reaction to this was to implement some minimal parser in Ruby to get to a working set of results quickly. Things went well until I wanted to make a decent tool for broader use within the team: I was starting to spend more time on developing the Ruby code than making headway on my tasks. Not only did I dig myself a grave in that respect but since I was quick to satisyfy the initial demand for results I set an expectation that the work was less complicated than it truly is (and will continue to be). I was building a house of cards.

I ended up scrapping the Ruby implementation and I'm now happily coming up to speed on how to manage llvm code with, well, llvm tools. This is not without effect from the leads of the program, of course. Live and learn, I suppose.