├── .travis.yml ├── Makefile ├── README.md ├── clangtool.cpp └── test.c /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | # This is the system compiler, not our LLVM infrastructure 3 | compiler: 4 | - clang 5 | 6 | # Use containers 7 | sudo: false 8 | 9 | 10 | addons: 11 | apt: 12 | sources: 13 | - ubuntu-toolchain-r-test 14 | - llvm-toolchain-precise-3.6 15 | packages: 16 | - llvm-3.6-dev 17 | - llvm-3.6 18 | - clang-3.6 19 | 20 | before_script: 21 | - export CC=clang-3.6 22 | - export CXX=clang++-3.6 23 | - export LLVM_CONFIG=llvm-config-3.6 24 | 25 | # -e == environment vars override makevars 26 | script: 27 | - make -e test 28 | 29 | notifications: 30 | email: false 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: clangtool.so 2 | 3 | CXX=clang++ 4 | CC=clang 5 | LLVM_CONFIG=llvm-config 6 | 7 | # Flags for building shared libraries. 8 | SYSTEM=$(shell uname) 9 | ifeq ($(SYSTEM), Darwin) 10 | CXXFLAGS= -fno-common 11 | LDFLAGS= -dynamiclib -Wl,-flat_namespace -Wl,-undefined,suppress 12 | endif 13 | ifeq ($(SYSTEM), Linux) 14 | CXXFLAGS= -fPIC 15 | LDFLAGS= -shared 16 | endif 17 | 18 | clangtool.so : %.so: %.cpp 19 | $(CXX) $(CXXFLAGS) $< -o $@ `$(LLVM_CONFIG) --cxxflags` $(LDFLAGS) 20 | 21 | CLANGTOOL_FLAGS=-Xclang -load -Xclang ./clangtool.so 22 | 23 | .PHONY: test testO0 testO2 24 | test: testO0 testO2 25 | 26 | testO0: test.c clangtool.so 27 | $(CC) $(CLANGTOOL_FLAGS) -O0 -o test test.c 28 | 29 | testO2: test.c clangtool.so 30 | $(CC) $(CLANGTOOL_FLAGS) -O2 -o test test.c 31 | 32 | clean: 33 | rm -f clangtool.so test 34 | 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Using LLVM plugins with clang 2 | 3 | [![build status](https://travis-ci.org/rdadolf/clangtool.svg?branch=master)](https://travis-ci.org/rdadolf/clangtool) 4 | 5 | The [LLVM docs](http://llvm.org/docs/WritingAnLLVMPass.html) suggest using `opt` to load and run custom passes. This works, but for certain tasks, like building programs with complicated Makefiles or unusual environments, it is less than ideal. Instead, LLVM provides a hook to allow plugin modules to be run automatically when loaded by clang. This lets you add a couple of flags and use clang (and your pass) as a drop-in replacement for your normal C compiler. These files demonstrate how that works. 6 | 7 | ### Using clangtool: 8 | 9 | Two steps: 10 | 11 | 1. Build your custom LLVM pass the normal way (as a shared library with the appropriate llvm flags). 12 | 13 | 2. Run `clang` on your input code with the following extra options: 14 | 15 | clang -Xclang -load -Xclang .so ... 16 | 17 | That's it. Your pass will be run automatically. You can specify these with `CFLAGS` in a Makefile, override `CC` or `CXX`, or write a script to wrap clang and use that. 18 | 19 | ### How it works: 20 | 21 | The key is a static class called `RegisterStandardPasses`, which is defined in the `PassManagerBuilder.h` header. The constuctor for this class calls the `addGlobalExtension` function, which in turn adds your custom pass to the list of extensions that are loaded by default. 22 | -------------------------------------------------------------------------------- /clangtool.cpp: -------------------------------------------------------------------------------- 1 | #include "llvm/Pass.h" 2 | #include "llvm/IR/BasicBlock.h" 3 | #include "llvm/PassRegistry.h" 4 | #include "llvm/PassManager.h" 5 | #include "llvm/Transforms/IPO/PassManagerBuilder.h" 6 | // For output 7 | #include "llvm/Support/raw_ostream.h" 8 | 9 | using namespace llvm; 10 | 11 | // Example pass: just dumps the insns for every block. 12 | namespace { 13 | struct ClangTool : public BasicBlockPass { 14 | static char ID; 15 | ClangTool() : BasicBlockPass(ID) {} 16 | virtual bool runOnBasicBlock(BasicBlock &BB) { 17 | BasicBlock::iterator i; 18 | errs() << "Basic Block\n"; 19 | for( i=BB.begin(); i!=BB.end(); i++ ) { 20 | errs() << " " << i->getOpcodeName() << "\n"; 21 | } 22 | return false; 23 | } 24 | }; 25 | } 26 | 27 | // Pass info 28 | char ClangTool::ID = 0; // LLVM ignores the actual value 29 | static RegisterPass X("clangtool", "Example pass", false, false); 30 | 31 | // Pass loading stuff 32 | // To use, run: clang -Xclang -load -Xclang .so ... 33 | 34 | // This function is of type PassManagerBuilder::ExtensionFn 35 | static void loadPass(const PassManagerBuilder &Builder, PassManagerBase &PM) { 36 | PM.add(new ClangTool()); 37 | } 38 | // These constructors add our pass to a list of global extensions. 39 | static RegisterStandardPasses clangtoolLoader_Ox(PassManagerBuilder::EP_OptimizerLast, loadPass); 40 | static RegisterStandardPasses clangtoolLoader_O0(PassManagerBuilder::EP_EnabledOnOptLevel0, loadPass); 41 | 42 | // Note: The location EP_OptimizerLast places this pass at the end of the list 43 | // of *optimizations*. That means on -O0, it does not get run. 44 | // 45 | // In general, adding your pass twice will cause it to run twice, but in this 46 | // particular case, the two are disjoint (EP_EnabledOnOptLevel0 only runs if 47 | // you're in -O0, and EP_OptimizerLast only runs if you're not). You can check 48 | // include/llvm/Transforms/IPO/PassManagerBuilder.h header and 49 | // lib/Transforms/IPO/PassManagerBuilder.cpp file for the exact behavior. 50 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int fib(int x) { 4 | if(x<2) 5 | return 1; 6 | else 7 | return fib(x-1) + fib(x-2); 8 | } 9 | 10 | int main(int argc, char **argv) 11 | { 12 | int i; 13 | printf("fib(5): %d\n", fib(5)); 14 | return 0; 15 | } 16 | --------------------------------------------------------------------------------