一般的Pass工程建立方法需要利用LLVM源码目录进行编译,本文介绍了另一种Pass工程建立和编译方法,与cmake相结合进行Pass编译。

1. 环境安装

  • 安装LLVM和clang编译器
1
2
3
sudo apt-get install clang
sudo apt-get install clang++
sudo apt-get install llvm

2. LLVM Pass工程目录

该测试工程LLVM_Test主要包含两个文件夹cmake和src,其具体文件组织形式如下图所示。需要注意的是,利用该工程编译Pass时只需要在src目录下添加相应的Pass源码即可,其他文件不需要修改

具体内容如下:

  • cmake目录中的FindLLVM.cmake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# - Find LLVM 
# This module can be used to find LLVM.
# It requires that the llvm-config executable be available on the system path.
# Once found, llvm-config is used for everything else.
#
# The following variables are set:
#
# LLVM_FOUND - Set to YES if LLVM is found.
# LLVM_VERSION - Set to the decimal version of the LLVM library.
# LLVM_INCLUDE_DIRS - A list of directories where the LLVM headers are located.
# LLVM_LIBRARY_DIRS - A list of directories where the LLVM libraries are located.
# LLVM_LIBRARIES - A list of libraries which should be linked
# LLVM_DYNAMIC_LIBRARY - A single dynamic llvm shared library
# LLVM_DYNAMIC_LIBRARY_FOUND - Whether found the dynamic llvm shared library
# LLVM_OPT - opt program in llvm
#
# Using Following macros to set static library:
# llvm_map_components_to_libraries(OUTPUT_VARIABLE ${llvm components})
#
# tutorial:
# 1. select default LLVM version:
# cmake .. -DLLVM_RECOMMEND_VERSION="3.5"
# 2. set include dir and link dir:
# include_directories(${LLVM_INCLUDE_DIRS})
# link_directories(${LLVM_LIBRARY_DIRS})
# 3.a link static libraries:
# llvm_map_components_to_libraries(LLVM_IRREADER_LIRARY irreader)
# target_link_libraries(target
# ${LLVM_LIBRARIES}
# ${LLVM_IRREADER_LIRARY}
# )
# 3.b link a dynamic library:
# target_link_libraries(target ${LLVM_DYNAMIC_LIBRARY})
#
# 14-10-26:
# LLVM_RECOMMAND_VERSION --> LLVM_RECOMMEND_VERSION
# update tutorial
#
# version: 0.9.1
# add LLVM_FLAGS_NDEBUG means llvm build with NDEBUG
#
# version: 0.9
# remove LLVM_{C/CPP/CXX}_FLAGS which import -DNDEBUG
#
#
if(NOT DEFINED LLVM_RECOMMEND_VERSION)
set(LLVM_RECOMMEND_VERSION "" CACHE STRING "Switch the llvm version")
set_property(CACHE LLVM_RECOMMEND_VERSION PROPERTY STRINGS "" "3.4" "3.5" "3.9")
endif()


if(NOT(DEFINED LLVM_ROOT) )
if(NOT "${LLVM_VERSION}" EQUAL "{LLVM_RECOMMEND_VERSION}")
unset(LLVM_CONFIG_EXE CACHE)
unset(LLVM_DYNAMIC_LIBRARY CACHE)
endif()
# find llvm-config. perfers to the one with version suffix, Ex:llvm-config-3.2
find_program(LLVM_CONFIG_EXE NAMES "llvm-config-${LLVM_RECOMMEND_VERSION}" "llvm-config")
find_program(LLVM_OPT NAMES "opt-${LLVM_RECOMMEND_VERSION}" "opt")

if(NOT LLVM_CONFIG_EXE)
set(LLVM_FOUND False)
message(FATAL_ERROR "Not Found LLVM (LLVM_RECOMMEND_VERSION=${LLVM_RECOMMEND_VERSION})")
else()
set(LLVM_FOUND True)
endif()

# Get the directory of llvm by using llvm-config. also remove whitespaces.
execute_process(COMMAND ${LLVM_CONFIG_EXE} --prefix OUTPUT_VARIABLE LLVM_ROOT
OUTPUT_STRIP_TRAILING_WHITESPACE )

endif()

macro(_llvm_config _var_name)
execute_process(COMMAND ${LLVM_CONFIG_EXE} ${ARGN}
OUTPUT_VARIABLE ${_var_name}
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endmacro()

set(LLVM_INSTALL_PREFIX ${LLVM_ROOT})
add_definitions(-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS)

_llvm_config(LLVM_VERSION --version)
STRING(REGEX REPLACE "^([0-9]+)\\.[0-9]+(svn)?\\.?[0-9]*" "\\1" LLVM_VERSION_MAJOR "${LLVM_VERSION}")
STRING(REGEX REPLACE "^[0-9]+\\.([0-9]+)(svn)?\\.?[0-9]*" "\\1" LLVM_VERSION_MINOR "${LLVM_VERSION}")
_llvm_config(LLVM_LD_FLAGS --ldflags)
_llvm_config(LLVM_LIBRARY_DIRS --libdir)
_llvm_config(LLVM_INCLUDE_DIRS --includedir)
string(REGEX MATCH "-l.*" LLVM_LIBRARIES ${LLVM_LD_FLAGS})
_llvm_config(LLVM_C_FLAGS --cflags)
if(LLVM_C_FLAGS MATCHES "-DNDEBUG")
add_definitions(-DLLVM_FLAGS_NDEBUG)
endif()

find_library(LLVM_DYNAMIC_LIBRARY
NAMES "LLVM" "LLVM-${LLVM_VERSION}"
PATHS ${LLVM_LIBRARY_DIRS}
)

if(NOT LLVM_DYNAMIC_LIBRARY)
set(LLVM_DYNAMIC_LIBRARY_FOUND False)
else()
set(LLVM_DYNAMIC_LIBRARY_FOUND True)
endif()

macro(llvm_map_components_to_libraries _var_name)
_llvm_config(${_var_name} --libs "${ARGN}")
endmacro()

message(STATUS "Found LLVM Version ${LLVM_VERSION} ")
  • 主目录的CMakeLists.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cmake_minimum_required(VERSION 2.8)
project(Test)

set(CMAKE_MODULE_PATH
${CMAKE_MODULE_PATH}
${CMAKE_SOURCE_DIR}/cmake
)

if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
endif()

find_package(LLVM)

add_subdirectory(src)
  • src目录中的CMakeLists.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
aux_source_directory(. DIR_SRCS)
include_directories(
${LLVM_PROF_INCLUDE_DIRS}
${PROJECT_BINARY_DIR}
${LLVM_INCLUDE_DIRS}
../include
)
link_directories(
${LLVM_LIBRARY_DIRS}
)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall --std=c++11 -fno-rtti")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNO_DEBUG")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")

add_library(Test SHARED
${DIR_SRCS}
)

target_link_libraries(Test
${LLVM_DYNAMIC_LIBRARY}
)
  • src中的FunctionTest.cpp文件是LLVM Pass的一个示例,在该目录中可以编写相应的Pass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <llvm/Pass.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/Constants.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/IR/InstIterator.h>
#include <llvm/IR/Operator.h>
#include <llvm/Analysis/AliasAnalysis.h>
using namespace llvm;
namespace{
class MyTest:public FunctionPass
{
public:
static char ID;
MyTest():FunctionPass(ID){}
bool runOnFunction(Function &F) override;
};
}
char MyTest::ID = 0;
static RegisterPass<MyTest> X("MyTest","My Test",false,false);
bool MyTest::runOnFunction(Function &F) {

Function *tmp = &F;
errs()<<tmp->getName()<<"\n";
// 遍历函数中的所有基本块
for (Function::iterator bb = tmp->begin(); bb != tmp->end(); ++bb) {
// 遍历基本块中的每条指令
for (BasicBlock::iterator inst = bb->begin(); inst != bb->end(); ++inst) {
if (inst->isBinaryOp()) {
errs()<<*inst<<"\n";
}
}
}

return false;
}

3. 编译过程

1
2
3
4
5
cd LLVM_Test/
mkdir build
cd build/
cmake ..
make

编译出来的.so文件在LLVM_Test/build/src目录下。具体过程如下图所示:

4. Pass调用

利用该工程编译之后,我们就可以调用相应的Pass了,具体调用过程如下:

1
opt -load LLVM_Test/build/src/libTest.so -MyTest test.ll