Getting started for LLVM pass writing
- Clone this repo, see the file details below:
- Pass - root directory for build and source code
- Transforms is top level directory for project.
- CMakeLists.txt file is CMakeLists for the Project.
- ValueNumbering is top level directory for the Pass
- ValueNumbering.cpp contains code for the Pass
- CMakeLists.txt is CMakeLists for this Pass
- ValueNumbering is top level directory for the Pass
- build will build Pass in this directory
- test contains Test code
- For Mac OSX users, uncomment the following line in Pass/Transforms/ValueNumbering/CMakeLists.txt
SET(CMAKE_MODULE_LINKER_FLAGS "-undefined dynamic_lookup")
- Move to Pass/build/ directory using cd command on your local system. Next, execute the following command. If it executes successfully, proceed to next step.
cmake -DCMAKE_BUILD_TYPE=Release ../Transforms/ValueNumbering
- Next execute make and it will generate *.so files under build directory.
make -j4
- Move to test/ directory and generate Test.ll file for Test.c using following command.
clang -Xclang -disable-O0-optnone Test.c -O0 -S -emit-llvm -o Test.ll
- Next generate Test.bc file for Test.ll using following command.
opt Test.ll -mem2reg -S -o Test.bc
- After generating test.bc, execute following command it execute the LLVM Pass.
opt -load ../Pass/build/libLLVMValueNumberingPass.so -ValueNumbering < Test.bc > /dev/null
- The implemented Pass extends from
FunctionPass
class and overridesrunOnFunction(Function &F)
function. runOnFunction(Function &F)
function gets called the number of times as many number of functions are present in test code. Name of the function is available using following code snippet.
bool runOnFunction(Function &F) override {
F.getName();
}
- We can iterate over basic blocks of the given function as:
bool runOnFunction(Function &F) override {
for (auto& basic_block : F)
{
...
}
}
- Next, we can iterate over the instructions in a basic block (BB). Note: instructions are in LLVM IR.
bool runOnFunction(Function &F) override {
for (auto& basic_block : F)
{
for (auto& inst : basic_block)
{
...
}
}
}
- Once we get an instruction, then we can cast it as
User
and iterate over operands of that instruction.
auto* ptr = dyn_cast<User>(&inst);
for (auto it = ptr->op_begin(); it != ptr->op_end(); ++it)
{
...
}
- Use Following API to check whether instruction is a binary operation (Assignment)
if (inst.isBinaryOp())
{
...
}
- Use Following APIs to compare and find operator types
if (inst.isBinaryOp())
{
inst.getOpcodeName(); //prints OpCode by name such as add, mul etc.
if(inst.getOpcode() == Instruction::Add)
{
errs() << "This is Addition"<<"\n";
}
if(inst.getOpcode() == Instruction::Mul)
{
errs() << "This is Multiplication"<<"\n";
}
// See Other classes Instruction::Sub, Instruction::UDiv, Instruction::SDiv
}
- Implementation of
runOnFunction(Function &F)
looks as following in whole.
string func_name = "test";
bool runOnFunction(Function &F) override {
errs() << "ValueNumbering: ";
errs() << F.getName() << "\n";
if (F.getName() != func_name) return false;
for (auto& basic_block : F)
{
for (auto& inst : basic_block)
{
errs() << inst << "\n";
auto* ptr = dyn_cast<User>(&inst);
for (auto it = ptr->op_begin(); it != ptr->op_end(); ++it)
{
errs() << "\t" << *(*it) << "\n";
}
}
}
return false;
}