参考:uu kk: LLVM pass on Windows: integrating with opt
该方法仍然有效,只不过还需要一些修改,遇到的错误需要解决。
错误1
CMake Error at CMakeLists.txt:658 (message):
Unexpected failure executing llvm-build: llvm-build: fatal error: missing
LLVMBuild.txt file at:
'F:\\svnlocal\\temp\\llvm\\llvm6.0/lib\\Transforms\\Hello\\LLVMBuild.txt'
这样的错误是说明缺少LLVMBuild.txt文件,只要在CMakeLists.txt的同目录下配置一个LLVMBuild.txt文件。
错误2
CMake Error at CMakeLists.txt:658 (message):
Unexpected failure executing llvm-build: Traceback (most recent call last):
File "F:/svnlocal/temp/llvm/llvm6.0/utils/llvm-build/llvm-build", line 6, in
<module>
llvmbuild.main()
File "F:\svnlocal\temp\llvm\llvm6.0\utils\llvm-build\llvmbuild\main.py", lin
e 952, in main
opts.optional_components)
File "F:\svnlocal\temp\llvm\llvm6.0\utils\llvm-build\llvmbuild\main.py", lin
e 332, in write_library_table
tg = c.get_parent_target_group()
File "F:\svnlocal\temp\llvm\llvm6.0\utils\llvm-build\llvmbuild\componentinfo
.py", line 87, in get_parent_target_group
return self.parent_instance.get_parent_target_group()
File "F:\svnlocal\temp\llvm\llvm6.0\utils\llvm-build\llvmbuild\componentinfo
.py", line 87, in get_parent_target_group
return self.parent_instance.get_parent_target_group()
File "F:\svnlocal\temp\llvm\llvm6.0\utils\llvm-build\llvmbuild\componentinfo
.py", line 87, in get_parent_target_group
return self.parent_instance.get_parent_target_group()
[Previous line repeated 992 more times]
File "F:\svnlocal\temp\llvm\llvm6.0\utils\llvm-build\llvmbuild\componentinfo
.py", line 82, in get_parent_target_group
if self.type_name == 'TargetGroup':
RecursionError: maximum recursion depth exceeded in comparison
最初看到这个错误还以为LLVM不支持Python3,于是安装了Python2,结果发现问题依然存在。最后确定下来原因是:LLVMBuild.txt内容的parent配置不正确,正确的配置是:LLVMBuild.txt文件的父目录的父目录,例如llvm/lib/Transforms/Custom(或者llvm/lib/Transforms/Hello)下的LLVMBuild.txt内容中parent应该是Transforms,这里放一个完整的llvm/lib/Transforms/Custom/LLVMBuild.txt内容:
[component_0]
type = Library
name = Custom
parent = Transforms
这一点上,原参考文章里是没有介绍的,需要自己摸索。
错误3
-- Clang version: 6.0.0
-- SampleAnalyzerPlugin ignored -- Loadable modules not supported on this platform.
-- PrintFunctionNames ignored -- Loadable modules not supported on this platform.
-- AnnotateFunctions ignored -- Loadable modules not supported on this platform.
CMake Error at cmake/modules/LLVM-Config.cmake:105 (target_link_libraries):
Target "LLVMCustom" of type UTILITY may not be linked into another target.
One may link only to INTERFACE, STATIC or SHARED libraries, or to
executables with the ENABLE_EXPORTS property set.
Call Stack (most recent call first):
cmake/modules/LLVM-Config.cmake:93 (explicit_llvm_config)
cmake/modules/AddLLVM.cmake:759 (llvm_config)
cmake/modules/AddLLVM.cmake:860 (add_llvm_executable)
tools/bugpoint/CMakeLists.txt:24 (add_llvm_tool)
-- BugpointPasses ignored -- Loadable modules not supported on this platform.
CMake Error at cmake/modules/LLVM-Config.cmake:105 (target_link_libraries):
Target "LLVMCustom" of type UTILITY may not be linked into another target.
One may link only to INTERFACE, STATIC or SHARED libraries, or to
executables with the ENABLE_EXPORTS property set.
Call Stack (most recent call first):
cmake/modules/LLVM-Config.cmake:93 (explicit_llvm_config)
cmake/modules/AddLLVM.cmake:759 (llvm_config)
cmake/modules/AddLLVM.cmake:860 (add_llvm_executable)
tools/opt/CMakeLists.txt:26 (add_llvm_tool)
-- Configuring incomplete, errors occurred!
See also "F:/svnlocal/temp/llvm/llvm6.0/build/CMakeFiles/CMakeOutput.log".
See also "F:/svnlocal/temp/llvm/llvm6.0/build/CMakeFiles/CMakeError.log".
这个错误是因为我偷懒导致,直接复制了Hello文件夹下的CMakeLists.txt,然后只修改了LLVMHello为:LLVMCustom
add_llvm_loadable_module( LLVMCustom
MyPass.cpp
Custom.cpp
DEPENDS
intrinsics_gen
PLUGIN_TOOL
opt
)
实际上应该是add_llvm_library:
add_llvm_library( LLVMCustom
MyPass.cpp
Custom.cpp
)
这一点参考文章是正确的,只是因为直接复制了Hello的CMakeLists.txt而没有完全修改正确而已,add_llvm_loadable_module也要修改掉,修改为add_llvm_library。
错误4
CMake Error at cmake/modules/AddLLVM.cmake:1333 (add_dependencies):
The dependency target "LLVMHello" of target "check-all" does not exist.
Call Stack (most recent call first):
CMakeLists.txt:919 (add_lit_target)
CMake Error at cmake/modules/AddLLVM.cmake:1333 (add_dependencies):
The dependency target "LLVMHello" of target "check-llvm" does not exist.
Call Stack (most recent call first):
cmake/modules/AddLLVM.cmake:1354 (add_lit_target)
test/CMakeLists.txt:157 (add_lit_testsuite)
llvm/test/CMakeLists.txt中删除LLVMHello,如果出现类似的错误,就找到所有有关Hello工程的CMakeLists.txt和LLVMBuild.txt,把关于对Hello的行注释掉或去掉。
错误5
not found unwrap
参考文章的CPP文件代码中少了头文件引用,导致unwrap找不到,正确的Custom.cpp代码如下:
#include "llvm/InitializePasses.h"
#include "llvm-c/Initialization.h"
#include "llvm/InitializePasses.h"
#include "llvm/PassRegistry.h"
using namespace llvm;
/// initializeCustom - Initialize all passes in the Custom library
void llvm::initializeCustom(PassRegistry &Registry) {
initializeMyPassPass(Registry);
}
/// LLVMInitializeCustom - C binding for initializeCustom.
void LLVMInitializeCustom(LLVMPassRegistryRef R) {
initializeCustom(*unwrap(R));
}
梳理一遍完整的步骤
基于llvm6.0介绍
步骤1:创建模块及源码文件
在llvm/lib/Transforms目录下创建一个Custom文件夹,这里存放要自定义的pass,创建一个MyPass.cpp,内容如下:
#define DEBUG_TYPE "mypass"
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/Transforms/Custom.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
namespace {
struct MyPass : public FunctionPass {
bool m_flag;
static char ID;
MyPass() : MyPass(ID) { m_flag = false; }
MyPass(bool flag) : FunctionPass(ID) {
this->m_flag = flag;
initializeMyPassPass(*PassRegistry::getPassRegistry());
}
/*
* Just print the function name
*/
bool runOnFunction(Function &F) {
bool Changed = false;
if (m_flag == false) {
return Changed;
}
errs().write_escaped(F.getName()) << "\n";
return Changed;
}
};
}
char MyPass::ID = 0;
INITIALIZE_PASS(MyPass, "mypass", "Print all function names", false, false)
FunctionPass *llvm::createMyPassPass(bool flag) {
return new MyPass(flag);
}
再创建一个Custom.cpp,内容如下:
#include "llvm/InitializePasses.h"
#include "llvm-c/Initialization.h"
#include "llvm/InitializePasses.h"
#include "llvm/PassRegistry.h"
using namespace llvm;
// initializeCustom - Initialize all passes in the Custom library
void llvm::initializeCustom(PassRegistry &Registry) {
initializeMyPassPass(Registry);
}
// LLVMInitializeCustom - C binding for initializeCustom.
void LLVMInitializeCustom(LLVMPassRegistryRef R) {
initializeCustom(*unwrap(R));
}
步骤2 配置LLVMBuild.txt和CMakeLists.txt
在Custom文件夹下创建LLVMBuild.txt,内容如下:
[component_0]
type = Library
name = Custom
parent = Transforms
简单解释下:
- type:是该模块的编译后的文件形式,这里是Library,也就是编译作为库文件。
- name:是该模块的名称,这里就填Custom,一般跟当前所在的文件夹同名。
- parent:当前文件夹的父目录,一般是Transforms。
在Custom文件夹下创建CMakeLists.txt,内容如下:
add_llvm_library( LLVMCustom
MyPass.cpp
Custom.cpp
)
步骤3 创建模块包含头文件
在llvm/include/llvm/Transforms/目录下创建Custom.h,内容如下:
#ifndef LLVM_CUSTOM_H
#define LLVM_CUSTOM_H
#include "llvm/Pass.h"
namespace llvm {
FunctionPass *createMyPassPass(bool flag);
}
#endif
打开编辑文件llvm/include/llvm/InitializePasses.h,在命名空间llvm中的代码块末尾添加代码:
void initializeCustom(PassRegistry&);
void initializeMyPassPass(PassRegistry&);
打开编辑文件llvm/include/llvm/LinkAllPasses.h,在ForcePassLinking构造函数中添加代码:
#include "llvm/Transforms/Custom.h"
// This part must reside in the constructor of struct ForcePassLinking
(void) llvm::createMyPassPass(true);
步骤4 修改各种LLVMBuild.txt和CMakeLists.txt
- 修改llvm/lib/Transforms/LLVMBuild.txt,common节的subdirectories内容添加:Custom
- 修改llvm/lib/Transforms/CMakeLists.txt,末尾添加:
add_subdirectory(Custom)
- 修改llvm/tools/opt/LLVMBuild.txt,required_libraries内容添加:Custom
- 修改llvm/tools/opt/CMakeLists.txt,在set(LLVM_LINK_COMPONENTS中添加:Custom
- 修改llvm/tools/bugpoint/LLVMBuild.txt,required_libraries内容添加:Custom
- 修改llvm/tools/bugpoint/CMakeLists.txt在set(LLVM_LINK_COMPONENTS中添加:Custom
步骤5 修改opt.cpp
修改llvm/tools/opt/opt.cpp,在main函数中添加代码:
initializeCustom(Registry);
步骤6 CMake GUI重新生成下sln
因为是直接修改的CMakeLists.txt和LLVMBuild.txt,VisualStudio是识别不了的,如果直接编译opt项目最终会出现链接错误,提示找不到Custom中添加的函数。
这个时候再次打开CMakeGUI再次生成下sln工程,由于CMakeGUI有缓存机制,不会对其他文件造成修改,速度是很快的。
目的就是要让llvm/lib/Transforms/CMakeLists.txt中的这句生效:
add_subdirectory(Custom)
然后重新打开VisualStudio,直接定位到opt项目,单独编译即可。
编译运行
VisualStudio2017打开项目工程,找到Tools分组下的opt,单独编译,比全部编译时间会少一些。链接的过程略长,编译的opt.exe在llvm/build/Debug/bin目录下,debug版本的有100多MB!总共耗时30多分钟吧,也够长的了。
命令行下运行
opt.exe -help
输出的内容:
……
-mypass - Print all function names
……
说明自定义的pass已经被编译进opt里啦。
原理解读
上面代码中并没有看到函数initializeMyPassPass的定义,这个是由宏INITIALIZE_PASS自动生成的。
INITIALIZE_PASS(MyPass, "mypass", "Print all function names", false, false)
可以参见INITIALIZE_PASS宏定义:
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis) \
static void *initialize##passName##PassOnce(PassRegistry &Registry) { \
PassInfo *PI = new PassInfo( \
name, arg, &passName::ID, \
PassInfo::NormalCtor_t(callDefaultCtor<passName>), cfg, analysis); \
Registry.registerPass(*PI, true); \
return PI; \
} \
static llvm::once_flag Initialize##passName##PassFlag; \
void llvm::initialize##passName##Pass(PassRegistry &Registry) { \
llvm::call_once(Initialize##passName##PassFlag, \
initialize##passName##PassOnce, std::ref(Registry)); \
}
集成到CLANG
完成上面的步骤,可以把pass集成到opt中,但是如果使用clang的话是没有这些功能的,所以仍需要做些处理。
- llvm/lib/Transforms/IPO/PassManagerBuilder.cpp
#include "llvm/Transforms/Custom.h"
static cl::opt<bool>
MyPass("mypass", cl::init(false), cl::Hidden,
cl::ZeroOrMore, cl::desc("Print all function names"));
在PassManagerBuilder::populateModulePassManager函数中添加:
MPM.add(createMyPassPass(MyPass));
这里设计的是一个开关,当用户使用了-mllvm -mypass参数的时候,MyPass为true,相当于启用了该pass。
- llvm/lib/Transforms/IPO/LLVMBuild.txt添加Custom
- llvm/lib/Target/X86/LLVMBuild.txt添加Custom
最后记住用CMkaeGUI重新生成一下解决方案,然后VisualStudio只编译clang模块即可。
使用clang时开启该pass:
clang.exe -mllvm -mypass example.c -o example.exe