Creating an LLVM Function Pass in C/C++
This guide explains how to create a function pass in LLVM to count and name functions in C/C++ code. Below are the steps to set up LLVM from source, implement your function pass, and test it.
You have to keep the following things in the mind to create pass:
- What pass you want to create (its header and Cpp file).
- Register the Pass.
- Test the pass.
Steps to Create an LLVM Function Pass
1. Clone LLVM Source Code
Start by cloning the LLVM repository from GitHub:
git clone https://github.com/llvm/llvm-project.git
cd llvm-project
2. Create a Build Directory and Configure the Build
Create a separate directory for the build to keep the source tree clean:
mkdir build
cd build
cmake -G Ninja ../llvm -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang" -DLLVM_ENABLE_RTTI=ON
-DCMAKE_BUILD_TYPE=Release
: Configures the build for a release version.-DLLVM_ENABLE_PROJECTS="clang"
: Enables Clang project during build.-DLLVM_ENABLE_RTTI=ON
: Enables Run-Time Type Information.
Once configured, you can build LLVM using Ninja:
ninja
3. Create the Pass Header File
Define the structure and interface of your pass in a header file.
Path: llvm/include/llvm/Transforms/Utils/FunctionName.h
#ifndef LLVM_TRANSFORMS_UTILS_FUNCTIONNAME_H
#define LLVM_TRANSFORMS_UTILS_FUNCTIONNAME_H
#include "llvm/IR/PassManager.h"
namespace llvm {
class FunctionNamePass : public PassInfoMixin<FunctionNamePass> {
public:
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
};
} // namespace llvm
#endif // LLVM_TRANSFORMS_UTILS_FUNCTIONNAME_H
4. Implement the Pass
Write the logic for your pass in the implementation file.
Path: llvm/lib/Transforms/Utils/FunctionName.cpp
#include "llvm/Transforms/Utils/FunctionName.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
PreservedAnalyses FunctionNamePass::run(Function &F, FunctionAnalysisManager &AM) {
static int Counter = 1; // Static counter to number the functions
errs() << "Function #" << Counter++ << ": " << F.getName() << "
";
return PreservedAnalyses::all();
}
5. Register the Pass
Update the PassRegistry and PassBuilder to register your new pass.
Update llvm/lib/Passes/PassRegistry.def
Add the following line:
FUNCTION_PASS("functionname", FunctionNamePass())
Update llvm/lib/Passes/PassBuilder.cpp
Include your header file and register the pass:
#include "llvm/Transforms/Utils/FunctionName.h"
6. Add Your Pass to the Build System
Edit the CMakeLists.txt file to include your new files.
Path: llvm/lib/Transforms/Utils/CMakeLists.txt
Add the new source files:
add_llvm_library(LLVMTransformsUtils
...
FunctionName.cpp
)
7. Build LLVM
Rebuild LLVM to include your pass.
cd /path/to/llvm/build
ninja
8. Test the Pass
Run your pass using the opt
tool.
Example C Code:
// test.c
int add(int a, int b) {
return a + b;
}
int main() {
return add(2, 3);
}
Commands to Run:
- Generate LLVM IR:
clang -S -emit-llvm test.c -o test.ll
In test.ll
, you will see the following:
attributes #0 = { noinline nounwind optnone ssp uwtable(sync) "frame-pointer"="non-leaf" }
To run your pass, remove the optnone
attribute. You can do this manually or using sed
:
sed -i '' 's/optnone//g' test.ll
- Apply the Pass:
/path/to/llvm/build/bin/opt -disable-output -passes=functionname test.ll
Expected Output:
Function #1: add
Function #2: main
Notes for Windows Users
Using WSL (Windows Subsystem for Linux)
For Windows, you can use WSL to build and test LLVM. Follow the same steps in the WSL terminal.
Modifying the Generated LLVM IR
If you encounter issues such as optnone
in the attributes, you can remove the optnone
attribute using the following sed command:
sed -i 's/optnone//g' test.ll
After removing optnone
, re-run the opt
command to observe the expected output.