Preskočite na sadržaj

Implementing an analysis and transformation pass in the LLVM compiler infrastructure

The LLVM documentation contains the Getting Started/Tutorials section, which offers two tutorials, one on implementing a language frontend and the other on building a just-in-time (JIT) compiler. While the latter tutorial covers optimization, it does so in a specific context that is not required here.

We will follow The LLVM Compiler Framework and Infrastructure Tutorial presented by Chris Lattner and Vikram Adve at the LCPC'04 Mini Workshop on Compiler Research Infrastructures in Septembed 2004. Although the presentation is somewhat a bit older, the information in it remains quite relevant and is a good complement to the documentation available on LLVM.

Writing an LLVM pass

The LLVM documentation contains the guidance for writing a pass in the two following ways:

Since the legacy pass manager will eventually be removed from LLVM and given that the new pass manager is much easier to use requires less boilerplate code, we will follow ths last guide.

The HelloWorldPass described in the Basic code required section is already a part of the LLVM source code and it got compiled when we initially set up the development environment. We will be modifying it from now on, but first let's use llvm-stress to create the file containing LLVM IR that will be used to test the pass:

$ ./bin/llvm-stress -o example-stress.ll

The pass implemented in the HelloWorldPass class is registered as helloworld in the llvm/lib/Passes/PassRegistry.def file:

FUNCTION_PASS("helloworld", HelloWorldPass())

so it can be called by adding the parameter -passes with the value helloworld to opt:

$ ./bin/opt example-stress.ll -S -passes helloworld -o example-stress-opt.ll
autogen_SD0

One can observe the function name in the output. The resulting LLVM IR file can be compared wuth th source file with llvm-diff and there will be no difference since the pass only performs analysis (specifically, it finds functions and prints their names) without performing any transformation.

Assignment

Modify the optimization pass so that it also prints the the number of operands for each function and the function type (signature); the API documentation of the Function class is a good place to look for a way to obtain this information.

Assignment

Modify the optimization pass so that it also prints the number of times each function was called. Amend the example-stress.ll file with the function manualgen_SD0 calling the function autogen_SD0:

define void @manualgen_SD0(i8* %0, i32* %1, i64* %2, i32 %3, i64 %4, i8 %5) {
  call void (i8*, i32*, i64*, i32, i64, i8) @autogen_SD0(i8* %0, i32* %1, i64* %2, i32 %3, i64 %4, i8 %5)
  ret void
}

and use this file to verify the correct number is printed

Assignment

Modify the optimization pass so that it also prints the number of usages (reads and writes) for each of the function arguments.

Assignment

Modify the optimization pass so that it also prints the number of instructions and then intersects the instruction list (note that the instructions reside inside of the basic blocks). For each of the instructions, print the type and the operands, if any.

Finally, before starting a specific project implemented using the LLVM libraries, you should read the LLVM Programmer's Manual.

Author: Vedran Miletić