Maintaining pass code outside of the LLVM directory

Hi,

I'm developing an LLVM pass and am running into code maintenance issues. Specifically, the "Writing an LLVM Pass" document assumes that the pass directory is placed somewhere in the LLVM source base (e.g. lib/Transforms), but this is a problem for me because:

1) Testing the pass against two different LLVM versions is difficult. For example, testing with the LLVM 2.6 release and the LLVM 2.7 prerelease requires either moving the pass directory back and forth or maintaining two different copies of the same pass, one in each LLVM source base. Neither option is much fun.

2) Mixing custom code with the LLVM source base can lead to dangerous mistakes. For example, I had added some extensive debugging code to the LLVM source, and I wanted to start over with a clean build, so I did "rm -rf" on the LLVM directory. Of course this wiped out my pass code as well! (Luckily I had checked in the code to a personal SVN repo.)

I really need a way to keep my pass code separate from the LLVM source base. I tried to get clever by creating a soft link from my pass directory to lib/Transforms, but this doesn't work because the $PWD is relative to the link's source directory, even when I cd into the link's target directory. This screws up the LEVEL variable in the Makefile and causes build errors. I tried to adjust LEVEL to account for the new location but that still didn't work.

Any suggestions? Thanks,

Trevor

Dear Trevor,

You can always use the LLVM project infrastructure (documented at http://llvm.org/docs/Projects.html). Numerous projects that use LLVM (such as poolalloc and SAFECode) use this infrastructure (or improved versions of it; the document may be a little dated). They have their own distinct source trees that can live outside of LLVM's source tree, but they reuse LLVM's build system. Your source code and Makefiles should require no modification; you'll simply be rearranging the directories and adding a configure script.

If you have any questions or have any problems, please email llvmdev. To start your project, I would recommend using either the poolalloc or SAFECode projects (see http://safecode.cs.illinois.edu for directions on getting SAFECode out of SVN) as a template; the projects/sample project in the LLVM source tree may be outdated.

-- John T.

Trevor Harmon wrote:

You can always use the LLVM project infrastructure (documented at
Creating an LLVM Project — LLVM 18.0.0git documentation).

Thanks for reminding me of that. I had actually started out with a project infrastructure, then switched to a pass implementation alone, not realizing I could combine the two. I've now reverted back to my old project, integrated the pass code into it, and my problem appears to be solved.

If you have any questions or have any problems, please email llvmdev.

Well, there is one thing... I'm wondering how best to actually run the pass after it's been compiled. Currently I'm just invoking "opt" and passing it a "-load" parameter with the name of my pass's dynamic library. I also have to add a couple more "-load" parameters for external libraries needed by the pass, followed by the custom parameters for the pass itself, and of course the "opt" parameters (the name of a bitcode input file, etc.). It gets to be a rather long and complex command line, and not a very friendly way of using the pass.

Is there a better way? I suppose I could put the parameters in a shell script -- simply a custom wrapper for "opt" -- and put it in the project's tools directory. Or maybe I could write some kind of native tool that bypasses "opt" altogether and somehow invokes the pass through LLVM's API. But that might be overkill. Not sure how others typically handle this. Any thoughts? Thanks,

Trevor

Trevor Harmon wrote:

You can always use the LLVM project infrastructure (documented at
Creating an LLVM Project — LLVM 18.0.0git documentation).
    
Thanks for reminding me of that. I had actually started out with a project infrastructure, then switched to a pass implementation alone, not realizing I could combine the two. I've now reverted back to my old project, integrated the pass code into it, and my problem appears to be solved.

If you have any questions or have any problems, please email llvmdev.
    
Well, there is one thing... I'm wondering how best to actually run the pass after it's been compiled. Currently I'm just invoking "opt" and passing it a "-load" parameter with the name of my pass's dynamic library. I also have to add a couple more "-load" parameters for external libraries needed by the pass, followed by the custom parameters for the pass itself, and of course the "opt" parameters (the name of a bitcode input file, etc.). It gets to be a rather long and complex command line, and not a very friendly way of using the pass.

Is there a better way? I suppose I could put the parameters in a shell script -- simply a custom wrapper for "opt" -- and put it in the project's tools directory. Or maybe I could write some kind of native tool that bypasses "opt" altogether and somehow invokes the pass through LLVM's API. But that might be overkill. Not sure how others typically handle this. Any thoughts? Thanks,
  
Either of the above solutions will work nicely. There is also a third option if you're working on large applications: create a custom version of libLTO that runs your pass(es). There are some people trying out this approach; it looks like a promising way to integrate passes seamlessly into the compiler toolchain.

FWIW, SAFECode uses both a special command line tool (based on opt's source code) and a specialized version of libLTO (which is young and needs further testing). SAFECode's functionality is split into several libraries with circular dependences that I haven't cleaned out yet; using opt was quite unwieldy.

One caveat about designing your own command-line tool is that it is possible through subtle design decisions to create a situation where it is difficult or impossible to run your passes through bugpoint in the same fashion as they run inside your tool. I don't think your project will have this problem because you only have a single pass, and your library dependences are probably linear. However, if you have multiple passes in different libraries, and you have single command line options that affect the behavior of multiple passes, then it can become an issue.

The best way, I think, to deal with them should you ever have these issues are:

1) Don't write libraries with circular dependences. Keep the dependences linear if possible.
2) Don't configure default pass behavior using constructor parameters. Instead, use a command-line option in the pass. If a command-line option affects multiple passes, make an immutable pass with the command line option and have your passes query the command line option values from the immutable pass.

I apologize if I'm giving more answers than you need right now, but I've learned some of this stuff the hard way and hope to prevent others from following in my footsteps.
:slight_smile:

-- John T.