美文网首页
LLVM Pass ~ hello world

LLVM Pass ~ hello world

作者: tuyw | 来源:发表于2019-03-31 07:14 被阅读0次

    What is a pass ?

    The LLVM Pass Framework is an important part of the LLVM system, because LLVM passes are where most of the interesting parts of the compiler exist. Passes perform the transformations and optimizations that make up the compiler, they build the analysis results that are used by these transformations, and they are, above all, a structuring technique for compiler code.

    All LLVM passes are subclasses of the Pass class, which implement functionality by overriding virtual methods inherited from Pass. Depending on how your pass works, you should inherit from the ModulePass , CallGraphSCCPass, FunctionPass , or LoopPass, or RegionPass, or BasicBlockPass classes, which gives the system more information about what your pass does, and how it can be combined with other passes. One of the main features of the LLVM Pass Framework is that it schedules passes to run in an efficient way based on the constraints that your pass meets (which are indicated by which class they derive from).

    We start by showing you how to construct a pass, everything from setting up the code, to compiling, loading, and executing it. After the basics are down, more advanced features are discussed.

    Writing hello world

    Here we describe how to write the “hello world” of passes. The “Hello” pass is designed to simply print out the name of non-external functions that exist in the program being compiled. It does not modify the program at all, it just inspects it. The source code and files for this pass are available in the LLVM source tree in the lib/Transforms/Hello directory.

    First, configure and build LLVM. Next, you need to create a new directory somewhere in the LLVM source base. For this example, we’ll assume that you made lib/Transforms/Hello. Finally, you must set up a build script that will compile the source code for the new pass. To do this, copy the following into CMakeLists.txt:

    add_llvm_library( LLVMHello MODULE
      Hello.cpp
    
     DEPENDS
     intrinsics_gen
     PLUGIN_TOOL
     opt
      )
    

    and the following line into lib/Transforms/CMakeLists.txt:

    add_subdirectory(Hello)
    

    (Note that there is already a directory named Hello with a sample “Hello” pass; you may play with it – in which case you don’t need to modify any CMakeLists.txt files – or, if you want to create everything from scratch, use another name.)

    This build script specifies that Hello.cpp file in the current directory is to be compiled and linked into a shared object $(LEVEL)/lib/LLVMHello.so that can be dynamically loaded by the opt tool via its -load option. If your operating system uses a suffix other than .so (such as Windows or Mac OS X), the appropriate extension will be used.

    Now that we have the build scripts set up, we just need to write the code for the pass itself.

    Basic code required

    Now that we have a way to compile our new pass, we just have to write it. Start out with:

    #include "llvm/Pass.h"
    #include "llvm/IR/Function.h"
    #include "llvm/Support/raw_ostream.h"
    

    Which are needed because we are writing a Pass, we are operating on Functions, and we will be doing some printing.

    Next we have:

    using namespace llvm;
    

    … which is required because the functions from the include files live in the llvm namespace.

    Next we have:

    namespace {
    

    … which starts out an anonymous namespace. Anonymous namespaces are to C++ what the “static” keyword is to C (at global scope). It makes the things declared inside of the anonymous namespace visible only to the current file. If you’re not familiar with them, consult a decent C++ book for more information.

    Next, we declare our pass itself:

    struct Hello : public FunctionPass {
    

    This declares a “Hello” class that is a subclass of FunctionPass. The different builtin pass subclasses are described in detail later, but for now, know that FunctionPass operates on a function at a time.

    static char ID;
    Hello() : FunctionPass(ID) {}
    

    This declares pass identifier used by LLVM to identify pass. This allows LLVM to avoid using expensive C++ runtime information.

    bool runOnFunction(Function &F) override {
        errs() << "Hello: ";
        errs().write_escaped(F.getName()) << '\n';
        return false;
      }
    }; // end of struct Hello
    }  // end of anonymous namespace
    

    We declare a runOnFunction method, which overrides an abstract virtual method inherited from FunctionPass. This is where we are supposed to do our thing, so we just print out our message with the name of each function.

    char Hello::ID = 0;
    

    We initialize pass ID here. LLVM uses ID’s address to identify a pass, so initialization value is not important.

    static RegisterPass<Hello> X("hello", "Hello World Pass",
                                 false /* Only looks at CFG */,
                                 false /* Analysis Pass */);
    

    Lastly, we register our class Hello, giving it a command line argument “hello”, and a name “Hello World Pass”. The last two arguments describe its behavior: if a pass walks CFG without modifying it then the third argument is set to true; if a pass is an analysis pass, for example dominator tree pass, then true is supplied as the fourth argument.

    As a whole, the .cpp file looks like:

    #include "llvm/Pass.h"
    #include "llvm/IR/Function.h"
    #include "llvm/Support/raw_ostream.h"
    
    using namespace llvm;
    
    namespace {
    struct Hello : public FunctionPass {
      static char ID;
      Hello() : FunctionPass(ID) {}
    
      bool runOnFunction(Function &F) override {
        errs() << "Hello: ";
        errs().write_escaped(F.getName()) << '\n';
        return false;
      }
    }; // end of struct Hello
    }  // end of anonymous namespace
    
    char Hello::ID = 0;
    static RegisterPass<Hello> X("hello", "Hello World Pass",
                                 false /* Only looks at CFG */,
                                 false /* Analysis Pass */);
    

    Now that it’s all together, compile the file with a simple “gmake” command from the top level of your build directory and you should get a new file “lib/LLVMHello.so”. Note that everything in this file is contained in an anonymous namespace — this reflects the fact that passes are self contained units that do not need external interfaces (although they can have them) to be useful.

    Running a pass with opt

    Now that you have a brand new shiny shared object file, we can use the opt command to run an LLVM program through your pass. Because you registered your pass with RegisterPass, you will be able to use the opt tool to access it, once loaded.

    To test it, follow the example at the end of the Getting Started with the LLVM System to compile “Hello World” to LLVM. We can now run the bitcode file (hello.bc) for the program through our transformation like this (or course, any bitcode file will work):

    $ opt -load lib/LLVMHello.so -hello < hello.bc > /dev/null
    Hello: __main
    Hello: puts
    Hello: main
    

    The -load option specifies that opt should load your pass as a shared object, which makes “-hello” a valid command line argument (which is one reason you need to register your pass). Because the Hello pass does not modify the program in any interesting way, we just throw away the result of opt (sending it to /dev/null).

    To see what happened to the other string you registered, try running opt with the -help option:

    opt -load lib/LLVMHello.so -help
    OVERVIEW: llvm .bc -> .bc modular optimizer and analysis printer
    
    USAGE: opt [subcommand] [options] <input bitcode file>
    
    OPTIONS:
      Optimizations available:
    ...
        -guard-widening           - Widen guards
        -gvn                      - Global Value Numbering
        -gvn-hoist                - Early GVN Hoisting of Expressions
        -hello                    - Hello World Pass
        -indvars                  - Induction Variable Simplification
        -inferattrs               - Infer set function attributes
    ...
    

    The pass name gets added as the information string for your pass, giving some documentation to users of opt. Now that you have a working pass, you would go ahead and make it do the cool transformations you want. Once you get it all working and tested, it may become useful to find out how fast your pass is. The PassManager provides a nice command line option (--time-passes) that allows you to get information about the execution time of your pass along with the other passes you queue up. For example:

    opt -load lib/LLVMHello.so -hello -time-passes < hello.bc > /dev/null
    Hello: __main
    Hello: puts
    Hello: main
    ===-------------------------------------------------------------------------===
                          ... Pass execution timing report ...
    ===-------------------------------------------------------------------------===
      Total Execution Time: 0.0007 seconds (0.0005 wall clock)
    
       ---User Time---   --User+System--   ---Wall Time---  --- Name ---
       0.0004 ( 55.3%)   0.0004 ( 55.3%)   0.0004 ( 75.7%)  Bitcode Writer
       0.0003 ( 44.7%)   0.0003 ( 44.7%)   0.0001 ( 13.6%)  Hello World Pass
       0.0000 (  0.0%)   0.0000 (  0.0%)   0.0001 ( 10.7%)  Module Verifier
       0.0007 (100.0%)   0.0007 (100.0%)   0.0005 (100.0%)  Total
    

    As you can see, our implementation above is pretty fast. The additional passes listed are automatically inserted by the opt tool to verify that the LLVM emitted by your pass is still valid and well formed LLVM, which hasn’t been broken somehow.

    Now that you have seen the basics of the mechanics behind passes, we can talk about some more details of how they work and how to use them.

    更多内容,请参照:
    LLVM Documentation

    相关文章

      网友评论

          本文标题:LLVM Pass ~ hello world

          本文链接:https://www.haomeiwen.com/subject/lxaeyqtx.html