【LLVM】LTO的設計

LLVM具有強大的模塊間優化功能,可以在鏈接時使用。鏈接時優化(LTO)就是指在鏈接時進行模塊間的優化。本文介紹了LTO優化器與鏈接器在接口上的設計。

鏈接器使用libLTO(共享對象)來處理LLVM位代碼文件(bitcode)。鏈接器和LLVM優化器之間的緊密集成有助于進行其他模型不可能實現的優化。

LTO示例

以下示例說明了LTO的集成方法的優點。這個例子需要一個系統鏈接器,它通過本文描述的接口支持LTO。使用clang調用系統鏈接器。

  • 源文件a.c被編譯成LLVM 位代碼格式
  • 源文件main.c被編譯成原生的目標文件
--- a.h ---
extern int foo1(void);
extern void foo2(void);
extern void foo4(void);

--- a.c ---
#include "a.h"

static signed int i = 0;

void foo2(void) {
  i = -1;
}

static int foo3() {
  foo4();
  return 10;
}

int foo1(void) {
  int data = 0;

  if (i < 0)
    data = foo3();

  data = data + 42;
  return data;
}

--- main.c ---
#include <stdio.h>
#include "a.h"

void foo4(void) {
  printf("Hi\n");
}

int main() {
  return foo1();
}

運行一下命令開始編譯:

% clang -flto -c a.c -o a.o        # <-- a.o 是一個bitcode文件
% clang -c main.c -o main.o        # <-- main.o 是原生目標文件
% clang -flto a.o main.o -o main   # <-- 使用 -flto 將兩個.o文件鏈接
  • 在這個例子中,鏈接器會發現函數foo2()在LLVM bitcode文件中被定義為一個外部可見的符號,當鏈接器使用pass對此符號進行處理時會發現函數foo2()從來沒被調用過,此時LLVM優化器會將foo2()移除。
  • 正如foo2()被移除時一樣,優化器同時也能觀察到條件i<0永遠為false,這意味著函數foo3也永遠不會被調用。因此,優化器也會移除foo3()。
  • 同樣的,由于foo3()被移除,鏈接器也會移除foo4()。
    這個例子演示了LTO的優點,而在沒有鏈接的參與下,優化器是沒辦法確定函數foo3()是否應該被移除的。

libLTO與linker鏈接器的多階段通信

鏈接器收集有關符號定義的信息,并在各種鏈接對象中使用。鏈接器通過查看本地.o文件中符號的定義和使用以及使用符號可見性來收集這些信息。鏈接器還使用用戶提供的信息,例如導出的符號列表。

LLVM優化器收集控制流信息,數據流信息,并從優化器的角度更多地了解程序結構。我們的目標是在各個鏈接階段共享這些信息,使鏈接器和優化器之間緊密集成。

第一階段:讀取LLVM bitcode文件

鏈接器首先讀取所有的目標文件并收集符號信息,包括原生的目標文件以及LLVM的bitcode文件。為了在所有.o文件都是原生目標文件的情況下降低鏈接器的開銷,當提供的目標文件不是原生的目標文件時,鏈接器僅調用lto_module_create()。如果lto_module_create()返回指明該文件是LLVM 的bitcode文件,那么鏈接器使用lto_module_get_symbol_name()lto_module_get_symbol_attribute()重新載入該模塊,得到所有的符號信息。這些信息被添加到鏈接器的全局符號表中。

所有的lto*函數都是在共享庫libLTO中實現的。它可以獨立與鏈接器之外進行更新。它采用懶加載方式。

第二階段:符號處理

在此階段,鏈接器使用全局符號表對符號進行處理??赡軙蠓栁炊x的錯誤,讀取歸檔成員,替換弱符號等。鏈接器即使不知道LLVM bitcode文件的具體內容,也可以執行此操作。如果啟用了死代碼刪除,那么鏈接器將收集活動符號列表。

第三階段:優化bitcode文件

在符號解析后,鏈接器告訴LTO共享庫哪些符號在原生的目標文件中是要用到的。在上面的例子中,鏈接器會通過函數lto_codegen_add_must_preserve_symbol()向LTO共享庫提供信息:只有foo1()是原生目標文件將要用到的。接下來鏈接器使用函數lto_codegen_compile()調用LLVM優化器和代碼生成器,通過合并bitcode文件并應用各種優化pass來返回原生目標文件。

第四階段:優化后的符號處理

此階段,鏈接器讀取優化后生成的原生目標文件并更新內部全局符號表響應所有的更改。對于LLVMbitcode文件使用到的外部符號的更改信息,鏈接器也會將其收集起來。在上面的例子中,由于foo3()的移除,鏈接器注意到foo4()不會再被調用,因此也會將foo4()移除。更新全局符號表。

在這之后,鏈接器繼續進行正常的鏈接操作,與bitcode文件的結合并不會影響到后續的鏈接。

libLTO

libLTO作為LLVM工具的一部分,它是一個共享對象,旨在供鏈接器使用。libLTO提供了一個抽象的C接口來使用LLVM過程優化器,并不會暴露LLVM內部的細節實現。
具體libLTO的一些相關函數見這里

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1.ios高性能編程 (1).內層 最小的內層平均值和峰值(2).耗電量 高效的算法和數據結構(3).初始化時...
    歐辰_OSR閱讀 29,547評論 8 265
  • 前言 做iOS開發的朋友們都知道,目前最新的Xcode7,新建項目默認就打開了bitcode設置.而且大部分開發者...
    戴維營教育閱讀 55,485評論 34 253
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,868評論 18 139
  • 先生當年遇到的一群姑娘,至今還留得住大多音容笑貌。有個小芳很像是后來的米國海報大美人兒,可她自己總是裝作不知道,再...
    賤金屬閱讀 183評論 0 0
  • 昨夜 初霜繾綣一懷風情 羞澀地 躲進銀杏葉里 調侃欲滴的金色 秋日的情話 縈繞寂靜時光留下的纏綿悱惻 羞紅了 還在...
    暮罹閱讀 400評論 0 2