NoteZ
NoteZ copied to clipboard
LLVM Pass Dev Trick
LLVM Pass Dev Trick
Prologue
本文会介绍一些在开发 LLVM Pass 的一些骚操作. @UESTC-LXY @Naville @lauos
1. 快速编译 LLVM Pass
利用 LLVM Pre-Built Binaries: 可以避免编译 LLVM 的过程.
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fPIC -stdlib=libc++")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS} -std=c++11")
#SET(PREFIX ${CMAKE_INSTALL_PREFIX})
#set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
# ------------- LLVM REQUIRED START -------------
find_package(LLVM REQUIRED CONFIG)
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
include(HandleLLVMOptions)
include(AddLLVM)
include(LLVMConfig)
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
find_package(Clang REQUIRED CONFIG)
include_directories(${CLANG_INCLUDE_DIRS})
add_definitions(${Clang_DEFINITIONS})
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LLVM_COMPILE_FLAGS} -Wall -fno-rtti -std=c++11 -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS")
message(STATUS "LLVM_DEFINITIONS: ${LLVM_DEFINITIONS}")
message(STATUS "LLVM_INCLUDE_DIRS: ${LLVM_INCLUDE_DIRS}")
message(STATUS "CLANG_DEFINITIONS: ${Clang_DEFINITIONS}")
message(STATUS "CLANG_INCLUDE_DIRS: ${CLANG_INCLUDE_DIRS}")
# ------------- LLVM REQUIRED END -------------
if(1)
set (CMAKE_SHARED_LINKER_FLAGS "-undefined dynamic_lookup")
else()
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
)
llvm_map_components_to_libnames(_llvm_libs
CodeGen
Core
Support
${LLVM_LINK_COMPONENTS}
)
llvm_expand_dependencies(llvm_libs ${_llvm_libs})
MESSAGE(STATUS ">>> ${llvm_libs}")
MESSAGE(STATUS ">>> ${LLVM_BINARY_DIR}")
target_link_libraries(CxxTrampoline ${llvm_libs})
endif()
最后 cmake 时指定下 path.
export LLVM_PREBUILD_PATH=/Users/jmpews/project/llvm-7.0/clang+llvm-7.0.0-x86_64-apple-darwin
cmake .. -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_PREFIX_PATH="${LLVM_PREBUILD_PATH}/lib/cmake/llvm;${LLVM_PREBUILD_PATH}/lib/cmake/clang" -DBUILD_SHARED_LIBS=ON -DCMAKE_VERBOSE_MAKEFILE=ON
2. 无感知改造 Xcode | NDK
虽然 NDK 使用的是开源 clang, 但 Xcode 的 clang 则是不开源的(不准确). 这就需要通过一定来加载我们的 Pass.
总体来说两个关键点.
- Inject SHARED pass library
- Hijack the specific function
这里的 Hijack 时机需要特别关注, 同时也要根据不同类型的 Pass, 去 Hijack 不同的函数, 这里这对不同 Pass 介绍下.
2.1. FunctionPass | ModulePass
对于 FunctionPass 和 ModulePass 可以在 void PassManagerBuilder::populateModulePassManager(legacy::PassManagerBase &MPM)
期间进行注入.
// Hijack __ZN4llvm18PassManagerBuilder25populateModulePassManagerERNS_6legacy15PassManagerBaseE
// llvm::PassManagerBuilder::populateModulePassManager(llvm::legacy::PassManagerBase&)
void (*PassManagerBuilder_populateModulePassManager)(PassManagerBuilder *this_, legacy::PassManagerBase *PM);
void fake_PassManagerBuilder_populateModulePassManager(PassManagerBuilder *this_, legacy::PassManagerBase *PM) {
PassManagerBuilder_populateModulePassManager(this_, PM);
dbgs() << "Injiect MachineIR Pass\n";
PM.add(createTrampolineIR());
}
void Hijack_PassManagerBuilder_populateModulePassManager() {
void *_PassManagerBuilder_populateModulePassManager = ZzFindSymbol("__ZN4llvm18PassManagerBuilder25populateModulePassManagerERNS_6legacy15PassManagerBaseE");
ZzReplace(_PassManagerBuilder_populateModulePassManager, (void *)fake_PassManagerBuilder_populateModulePassManager, (void **)&PassManagerBuilder_populateModulePassManager);
}
当然还有另外一种思路通过 PassManagerBuilder::addExtension
在不同的 Pass 处理期间进行添加.
enum ExtensionPointTy {
/// EP_EarlyAsPossible - This extension point allows adding passes before
/// any other transformations, allowing them to see the code as it is coming
/// out of the frontend.
EP_EarlyAsPossible,
/// EP_ModuleOptimizerEarly - This extension point allows adding passes
/// just before the main module-level optimization passes.
EP_ModuleOptimizerEarly,
/// EP_LoopOptimizerEnd - This extension point allows adding loop passes to
/// the end of the loop optimizer.
EP_LoopOptimizerEnd,
/// EP_ScalarOptimizerLate - This extension point allows adding optimization
/// passes after most of the main optimizations, but before the last
/// cleanup-ish optimizations.
EP_ScalarOptimizerLate,
/// EP_OptimizerLast -- This extension point allows adding passes that
/// run after everything else.
EP_OptimizerLast,
/// EP_VectorizerStart - This extension point allows adding optimization
/// passes before the vectorizer and other highly target specific
/// optimization passes are executed.
EP_VectorizerStart,
/// EP_EnabledOnOptLevel0 - This extension point allows adding passes that
/// should not be disabled by O0 optimization level. The passes will be
/// inserted after the inlining pass.
EP_EnabledOnOptLevel0,
/// EP_Peephole - This extension point allows adding passes that perform
/// peephole optimizations similar to the instruction combiner. These passes
/// will be inserted after each instance of the instruction combiner pass.
EP_Peephole,
/// EP_LateLoopOptimizations - This extension point allows adding late loop
/// canonicalization and simplification passes. This is the last point in
/// the loop optimization pipeline before loop deletion. Each pass added
/// here must be an instance of LoopPass.
/// This is the place to add passes that can remove loops, such as target-
/// specific loop idiom recognition.
EP_LateLoopOptimizations,
/// EP_CGSCCOptimizerLate - This extension point allows adding CallGraphSCC
/// passes at the end of the main CallGraphSCC passes and before any
/// function simplification passes run by CGPassManager.
EP_CGSCCOptimizerLate,
};
2.2. MachineFunctionPass
对于 MachineFunctionPass, 需要根据特定的需求选择要 hook 的函数, 在 TargetPassConfig::addMachinePasses()
选择需要 hook 的时机.
例如这里希望能在 LLVM 将虚拟寄存器转化为物理寄存器前, 做一些事情, 则 hook AArch64PassConfig::addPreRegAlloc()
方法.
这里有一个小 Trick, TargetPassConfig::addPass
方法是一个 Protected
方法, 无法通过 this_
直接调用, 所以这里通过函数指针直接绕过编译校验进行调用.
// Hijack __ZN12_GLOBAL__N_117AArch64PassConfig14addPreRegAllocEv
// AArch64PassConfig::addPreRegAlloc()
void (*AArch64PassConfig_addPreRegAlloc)(TargetPassConfig *this_);
void (*TargetPassConfig_addPass)(TargetPassConfig *this_, Pass *P, bool verifyAfter, bool printAfter);
void fake_AArch64PassConfig_addPreRegAlloc(TargetPassConfig *this_) {
TargetPassConfig_addPass(this_, createTrampolineMIR(), true, true);
AArch64PassConfig_addPreRegAlloc(this_);
}
void Hijack_AArch64PassConfig_addPreRegAlloc() {
TargetPassConfig_addPass = (void (*)(TargetPassConfig *this_, Pass *P, bool verifyAfter, bool printAfter))ZzFindSymbol("__ZN4llvm16TargetPassConfig7addPassEPNS_4PassEbb");
void *_AArch64PassConfig_addPreRegAlloc = ZzFindSymbol("__ZN12_GLOBAL__N_117AArch64PassConfig14addPreRegAllocEv");
ZzReplace(_AArch64PassConfig_addPreRegAlloc, (void *)fake_AArch64PassConfig_addPreRegAlloc, (void **)&AArch64PassConfig_addPreRegAlloc);
}
3. LLVM Pass SHARED Library
默认通过 add_llvm_loadable_module
编译插件的结果是 bundle
可以认为是静态库, 但是如果直接通过 add_library( XXX SHARED XXX)
和 target_link_libraries
编译动态库会导致, 与 clang / llc 符号冲突, 部分 option 初始化初始化两次导致失败.
这里通过 set (CMAKE_SHARED_LINKER_FLAGS "-undefined dynamic_lookup")
将于 LLVM 有关的符号设置成动态查找.
Epilogue
emmmmm 再更新