Skip to main content

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:

  1. What pass you want to create (its header and Cpp file).
  2. Register the Pass.
  3. 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:

  1. Generate LLVM IR:
    clang -S -emit-llvm test.c -o test.ll
note

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
  1. 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.


Reference