Working with LLVM passes
Dan Liew
Software Reliability Group
11th June 2014
Getting the sources
I spent quite a bit of time preparing source code that you guys can use to easily play with this stuff. You can find it at
https://github.com/delcypher/srg-llvm-pass-tutorial
You need to LLVM/Clang 3.5 (not released yet so just build from trunk). For your reference I built against r210103
Disclaimer!
LLVM passes
Pass
BasicBlockPass
CallGraphSCCPass
FunctionPass
LoopPass
RegionPass
ModulePass
Analysis passes
Transformation passes
Let’s have a quick play with opt
Stripping out dead functions
#include <stdio.h>
void foo()
{
printf("I'm foo\n");
}
int main()
{
printf("I'm main\n");
return 0;
}
Stripping out dead functions
$ clang -emit-llvm -S -O0 simple.c
$ cat simple.ll
...
$ opt simple.ll -S -debug-pass=Structure -internalize -internalize-public-api-list=main -globaldce
Other fun things we can do with opt
#include <stdio.h>
int main()
{
for (int index=0; index < 5; ++index)
printf("I'm main\n");
return 0;
}
Let’s write a slightly more complex program so our graphs aren’t boring!
$ clang -S -emit-llvm -O0 loop.c
Control flow graph
$ opt -view-cfg loop.ll
This looks like the head of a loop. Let’s check the dominator tree to check if we have a back-edge (head dominates the tail)
Dominator Tree
$ opt -view-dom loop.ll
Yep we have a
back-edge :)
Region graph
$ opt -view-regions loop.ll
Regions are a set of basic blocks in the CFG that from outside the region look like they have single entry and single exit edges.
Other graphs that opt can produce
Quick demo of this
See visualisationDemos/*.sh
Writing a simple function pass
The code...
STATISTIC(HelloCounter, "Counts number of functions greeted");
namespace {
// Hello - The first implementation, without getAnalysisUsage.
struct Hello : public FunctionPass {
static char ID; // Pass identification, replacement for typeid
Hello() : FunctionPass(ID) {}
bool runOnFunction(Function &F) override {
++HelloCounter;
errs() << "Hello: ";
errs().write_escaped(F.getName()) << '\n';
return false;
}
};
}
char Hello::ID = 0;
static RegisterPass<Hello> X("hello", "Hello World Pass");
This is a function pass
Inform LLVM’s PassManager that we didn’t change anything
Just a bit of boilerplate code
Declare an unsigned int whose value can be shown using -stats in opt
Demo
See helloPass/run.sh
Quick aside: LLVM’s PassManager
Quick aside: LLVM’s PassManager
Using analyses in your own passes
Call graph cycle detection
...
bool runOnModule(llvm::Module &M) override {
CallGraph &CG = getAnalysis<CallGraphWrapperPass>().getCallGraph();
scc_iterator<CallGraph *> i = scc_begin(&CG), e = scc_end(&CG);
bool hasCycle = false;
// Do cycle detect stuff (see the real source code)
return false;
}
virtual void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
// We are explicitly stating that we require this analysis
AU.addRequired<llvm::CallGraphWrapperPass>();
}
};
Depend on CallGraph analysis
Get the CallGraph analysis results
Demo
See usingAnalyses/run.sh
Using the IRBuilder
I don’t alway inline but when I do, I do it by hand...
IRBuilder
Example: Replace get_global_id
I’m going to make the following transformation.
get_global_id(x) ==
get_local_id(x) + get_group_id(x)*get_local_size(x)
I implemented a pass that replaces all call to get_global_id() to the equivalent form above. Effectively inlining...
Transform
define void @foo(i32* nocapture %A, i32 %x) #0 {
entry:
%call = tail call i32 @get_global_id(i32 %x) #2
%add = add nsw i32 %call, 1
%call1 = tail call i32 @get_global_id(i32 0) #2
%idxprom = sext i32 %call1 to i64
%arrayidx = getelementptr inbounds i32* %A, i64 %idxprom
store i32 %add, i32* %arrayidx, align 4, !tbaa !2
ret void
}
define void @foo(i32* nocapture %A, i32 %x) #0 {
entry:
%rpl.ggi.0. = call i32 @get_group_id(i32 %x)
%rpl.ggi.1. = call i32 @get_local_size(i32 %x)
%rpl.ggi.2. = mul i32 %rpl.ggi.0., %rpl.ggi.1.
%rpl.ggi.3. = call i32 @get_local_id(i32 %x)
%rpl.ggi.result. = add i32 %rpl.ggi.2., %rpl.ggi.3.
%add = add nsw i32 %rpl.ggi.result., 1
%rpl.ggi.0.1 = call i32 @get_group_id(i32 0)
%rpl.ggi.1.2 = call i32 @get_local_size(i32 0)
%rpl.ggi.2.3 = mul i32 %rpl.ggi.0.1, %rpl.ggi.1.2
%rpl.ggi.3.4 = call i32 @get_local_id(i32 0)
%rpl.ggi.result.5 = add i32 %rpl.ggi.2.3, %rpl.ggi.3.4
%idxprom = sext i32 %rpl.ggi.result.5 to i64
%arrayidx = getelementptr inbounds i32* %A, i64 %idxprom
store i32 %add, i32* %arrayidx, align 4, !tbaa !2
ret void
}
OLD
NEW
The IRBuilder bit...
for(std::vector<CallInst*>::iterator CI = foundCalls.begin(), CIE = foundCalls.end(); CI != CIE; ++CI) {
Value* dimension = (*CI)->getArgOperand(0);
IRBuilder<> Builder(*CI);
CallInst* ggiCall = Builder.CreateCall(get_group_idF, dimension, "rpl.ggi.0.");
CallInst* glsCall = Builder.CreateCall(get_local_sizeF, dimension, "rpl.ggi.1.");
Value* mul = Builder.CreateMul(ggiCall, glsCall, "rpl.ggi.2.");
CallInst* gliCall = Builder.CreateCall(get_local_idF, dimension, "rpl.ggi.3.");
Value* result = Builder.CreateAdd(mul, gliCall,"rpl.ggi.result.");
// Replace all uses with
(*CI)->replaceAllUsesWith(result);
(*CI)->eraseFromParent(); // Finally we can remove the CallInst to get_global_id()
}
Demo
See usingIRBuilder/run.sh
Using your passes in clang itself
$ clang -Xclang -load -Xclang YOUR_PASS.so ...
Demo
See helloPass/run_pass_in_clang.sh
More, you say?
Thanks for listening