LLVM筆記(3) - PASS


1. pass的概念
  在LLVM中優化以pass形式實現, 每一個pass代表一種優化. pass分為兩類, 一類是分析(analysis)pass, 負責收集信息共其它pass使用, 輔助調試或使程序可視化; 另一類是變換(transform)pass, 改變程序的dataflow / controlflow. LLVM中實現了幾十種優化pass, 其中許多pass運行不止一次. analysis pass存放在lib/Analysis下, transform pass存放在lib/Transforms下. 本文簡要介紹其概念, 后文將逐個詳細分析.

2. 跟蹤與調試pass
  LLVM提供了完善的日志系統來跟蹤各個pass的運行.

 1 [13:38:49] hansy@hansy:~$ cat test.c
 2 int sigma(int cnt)
 3 {
 4   int sum = 0, i = 0;
 5   for (i = 0; i < cnt; i++)
 6     sum += i;
 7   return sum;
 8 }
 9 
10 [13:38:55] hansy@hansy:~$ clang test.c -O2 -mllvm -debug -S 2>test.ll
11 [13:39:02] hansy@hansy:~$ clang test.c -O2 -mllvm -debug-only=early-cse -S 2>test.ll
12 [13:39:05] hansy@hansy:~$ clang test.c -O2 -mllvm -print-before-all -S 2>test.ll
13 [13:39:16] hansy@hansy:~$ clang test.c -O2 -mllvm -print-after-all -S 2>test.ll
14 [13:39:25] hansy@hansy:~$ clang test.c -O2 -mllvm -print-after-all -mllvm -filter-print-funcs=sigma -S 2>test.ll 


  -mllvm指定將后一個選項傳遞給llvm. 因為默認執行的clang其實是一個driver, 在運行中調用對應的程序執行前端分析(clang), 中端優化指令選擇(llvm)以及匯編鏈接(assembler & linker). -debug選項即將llvm中DEBUG宏輸出到stderr. 如果我們只想需要某個特定pass的打印可以使用-debug-only選項(后接pass的DEBUG_TYPE).
  通過-debug我們可以快速定位程序運行的路徑, 但如果我們想要知道某個pass運行完后的結果可以使用后面兩條命令. -print-before-all會在每個pass執行之前(根據pass的scope(function / loop / basicblock))打印IR, 類似的-print-after-all會在每個pass執行完畢后打印IR. 如果源文件中存在多個函數, 打印較多, 我們還可以使用-filter-print-funcs指定打印的函數名(對-debug選項無效).

3. DEBUG in LLVM
  LLVM代碼中使用LLVM_DEBUG()宏(defined in include/llvm/Support/Debug.h)控制打印信息, 該宏調用DEBUG_WITH_TYPE()宏, 后者根據NDEBUG是否定義決定是否執行括號內的內容.

 1 #ifndef NDEBUG
 2 #define DEBUG_WITH_TYPE(TYPE, X)                                        \
 3   do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType(TYPE)) { X; } \
 4   } while (false)
 5 #else
 6 #define DEBUG_WITH_TYPE(TYPE, X) do { } while (false)
 7 #endif
 8 
 9 #define LLVM_DEBUG(X) DEBUG_WITH_TYPE(DEBUG_TYPE, X)
10 
11 #ifndef NDEBUG
12 static cl::opt<bool, true>
13 Debug("debug", cl::desc("Enable debug output"), cl::Hidden,
14       cl::location(DebugFlag));
15 static cl::opt<DebugOnlyOpt, true, cl::parser<std::string> >
16 DebugOnly("debug-only", cl::desc("Enable a specific type of debug output (comma separated list of types)"),
17           cl::Hidden, cl::ZeroOrMore, cl::value_desc("debug string"),
18           cl::location(DebugOnlyOptLoc), cl::ValueRequired);
19 #endif
20 
21 bool isCurrentDebugType(const char *DebugType) {
22   if (CurrentDebugType->empty())
23     return true;
24   for (auto &d : *CurrentDebugType) {
25     if (d == DebugType)
26       return true;
27   }
28   return false;
29 }
30 
31 raw_ostream &llvm::dbgs() {
32   static struct dbgstream {
33     circular_raw_ostream strm;
34 
35     dbgstream() :
36         strm(errs(), "*** Debug Log Output ***\n",
37              (!EnableDebugBuffering || !DebugFlag) ? 0 : DebugBufferSize) {
38       if (EnableDebugBuffering && DebugFlag && DebugBufferSize != 0)
39         sys::AddSignalHandler(&debug_user_sig_handler, nullptr);
40     }
41   } thestrm;
42 
43   return thestrm.strm;
44 }


  DebugType的設置見lib/Support/Debug.cpp文件, 其中DebugFlag(defined in lib/Support/Debug.cpp)是llvm選項-debug設置的. isCurrentDebugType()在不設置DebugType時返回true, 如果使用-debug-only選項那么容器CurrentDebugType非空即根據比較字符串結果決定打印. LLVM_DEBUG()宏固定使用DEBUG_TYPE作為傳入的字符串, 所以不同pass下需要定義不同的DEBUG_TYPE宏, 同理如要打印某個pass就要找到對應的DEBUG_TYPE而不是直接寫pass名. 注意輸出時指定dbgs()為ostream, 它會將輸出重定向到stderr(), 避免與正常的輸出沖突.

  最后說下NDEBUG宏, 該宏在Debug編譯時不起效, 因此Debug編譯時DEBUG()宏正常輸出. 而Release編譯時NDEBUG起效, 若也想輸入調試信息需要在編譯時增加-DLLVM_ENABLE_ASSERTIONS=On選項. 具體代碼見cmake/modules/HandleLLVMOptions.cmake文件. 如下所示:

 1 if( LLVM_ENABLE_ASSERTIONS )
 2   # MSVC doesn't like _DEBUG on release builds. See PR 4379.
 3   if( NOT MSVC )
 4     add_definitions( -D_DEBUG )
 5   endif()
 6   # On non-Debug builds cmake automatically defines NDEBUG, so we
 7   # explicitly undefine it:
 8   if( NOT uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG" )
 9     add_definitions( -UNDEBUG )
10     # Also remove /D NDEBUG to avoid MSVC warnings about conflicting defines.
11     foreach (flags_var_to_scrub
12         CMAKE_CXX_FLAGS_RELEASE
13         CMAKE_CXX_FLAGS_RELWITHDEBINFO
14         CMAKE_CXX_FLAGS_MINSIZEREL
15         CMAKE_C_FLAGS_RELEASE
16         CMAKE_C_FLAGS_RELWITHDEBINFO
17         CMAKE_C_FLAGS_MINSIZEREL)
18       string (REGEX REPLACE "(^| )[/-]D *NDEBUG($| )" " "
19         "${flags_var_to_scrub}" "${${flags_var_to_scrub}}")
20     endforeach()
21   endif()
22 endif()


4. 編寫一個簡單pass
  LLVM文檔介紹了如何編寫一個hello world的pass, 具體見docs/WritingAnLLVMPass, 此文不再詳述.

5. pass的分類
  pass分為ImmutablePass, ModulePass, CallGraphSCCPass, FunctionPass, LoopPass, RegionPass, BasicBlockPass與MachineFunctionPass.
  ImmutablePass指的是不運行, 不改變狀態也永不更新的pass, 一般情況下用於顯示編譯器的配置信息.
  ModulePass是將整個程序視作一個單元處理的pass, 它是最通用的pass類型. ModulePass可以調用函數級別的pass. ModulePass需要實現runOnModule(Module &).
  CallGraphSCCPass用於被那些需要從底向上(bottom-up)遍歷call graph的pass調用. 關於更多SCC內容在后文分析. CallGraphSCCPass需要實現doInitialization(CallGraph &), runOnSCC(CallGraphSCC &)和doFinalization(CallGraph &).
  FunctionPass是以單個函數為作用域的pass, 每個函數間是相互獨立的, 相互之間無法影響. FunctionPass需要實現doInitialization(Module &), runOnFunction(Function &)和doFinalization(Module &).
  LoopPass是以單個loop為作用域的pass, 每個loop間相互獨立. LoopPass以嵌套方式處理循環, 外層循環最后處理. LoopPass需要實現doInitialization(Loop *, LPPassManager &), runOnLoop(Loop *, LPPassManager &)和doFinalization().
  RegionPass類似與LoopPass. RegionPass需要實現doInitialization(Region *, RGPassManager &), runOnRegion(Region *, RGPassManager &)和doFinalization().
  BasicBlockPass類似FunctionPass, 但是以BasicBlock為scope. BasicBlockPass需要實現同樣的接口, 但Initlialization與Finalization可以以Module或Function為單位.
  MachineFunctionPass類似FunctionPass, 區別在於前者屬於LLVM code generator(后端), 生成架構相關代碼, 后者屬於LLVM optimizer(中端), 生成通用的IR. 因此MachineFunctionPass無法通過通用pass接口注冊(因此也無法使用opt調用優化), 而是通過TargetMachine::addPassesToEmitFile及其類似接口注冊.

6. 注冊pass
  在編寫完一個pass后並不會立即生效, 還需要將他注冊到LLVM框架中. 根據pass作用域不同分為兩處注冊. 對於架構無關的中端優化pass, LLVM提供了一套pass pipleine. 在lib/Passes/PassRegistry.def中包含了LLVM中端優化pass. 對於架構相關的優化pass, 需要在目標后端目錄下注冊. 以X86為例, 在lib/Target/X86/X86TargetMachine.cpp中存放相關代碼. 注意對特定架構的做的中端優化也放在該文件下, 但是在使用-emit-llvm生成IR時並不會調用這些優化.

7. 略

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM