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 -S -fno-discard-value-names -emit-llvm test.c -o test.ll
- After generating test.ll, run the following command to test the LLVM Pass.
opt -load-pass-plugin ../Pass/build/libLLVMValueNumberingPass.so -passes=value-numbering test.ll
- If you want to see debug information, use -debug-pass-manager flag.
opt -load-pass-plugin ../Pass/build/libLLVMValueNumberingPass.so -passes=value-numbering test.ll -debug-pass-manager
- The implemented Pass extends from
FunctionPass
class and overridesrun(Function &F, FunctionAnalysisManager &)
function. run(Function &F, FunctionAnalysisManager &)
function gets called for each function in the test code. Name of the function being analyzed is accessible using the following code snippet.
PreservedAnalyses run(Function &F, FunctionAnalysisManager &) {
F.getName();
}
- To print out to the screen, you need to redirect strings to
errs()
, as in:
PreservedAnalyses run(Function &F, FunctionAnalysisManager &) {
errs() << "function name: " << F.getName() << "\n";
}
- We can iterate over basic blocks of the given function as:
PreservedAnalyses run(Function &F, FunctionAnalysisManager &) {
for (auto& basic_block : F)
{
...
}
}
- Next, we can iterate over the instructions in a basic block (BB). Note: instructions are in LLVM IR.
PreservedAnalyses run(Function &F, FunctionAnalysisManager &) {
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)
{
...
}
- You can also access the operands using
getOperand(operand_index)
as in:
for (auto& inst : basic_block)
{
...
errs() << "operand: " << inst.getOperand(0) << "\n";
...
}
- You can check whether instruction is a binary operation (like
a = b + c
) withisBinaryOp()
if (inst.isBinaryOp())
{
...
}
- You can find operator types with
getOpcode()
and predefined opcodes
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
}
- A sample implementation of
run(Function &F, FunctionAnalysisManager &)
:
string func_name = "test";
PreservedAnalyses run(Function &F, FunctionAnalysisManager &) {
errs() << "ValueNumbering: ";
errs() << F.getName() << "\n";
if (F.getName() != func_name)
return PreservedAnalyses::all();
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 PreservedAnalyses::all();
}