構建 LLVM 發行版本

簡介

本文檔適用於想要構建和打包 LLVM 以及任何 LLVM 子專案工具組合以進行發行的使用者。本文檔涵蓋了 LLVM 建置系統的有用功能,以及有關打包 LLVM 的最佳實務和一般資訊。

如果您是 CMake 的新手,您可能會發現 使用 CMake 建置 LLVMCMake 入門 文件很有用。本文檔中涵蓋的一些內容是 進階建置配置 文件中所述建置的內部運作方式。

一般發行版本指南

在構建編譯器的發行版本時,通常建議執行編譯器的引導建置。這表示使用您的主機工具鏈建置「階段 1」編譯器,然後使用「階段 1」編譯器建置「階段 2」編譯器。這樣做的目的是讓您發行的編譯器受益於新編譯器提供的所有錯誤修復、效能最佳化和一般改進。

在決定如何建置發行版本時,您需要評估一些取捨。其中兩個主要因素是

  1. 發行版本的編譯時間與建置的編譯器的效能

  2. 發行版本的二進制大小與建置的編譯器的效能

最大化產生的編譯器效能的指南是使用 LTO、PGO 和靜態連結所有內容。這將導致整體發行版本更大,並且產生所需的時間更長,但它為編譯器提供了最大的最佳化機會。

最小化發行版本大小的指南是將 LLVM 和 Clang 函式庫動態連結到工具中,以減少程式碼重複。這將導致產生的二進制檔案的效能大幅下降,因為它減少了最佳化機會,並且因為動態連結需要在行程啟動時解析符號,這對於 C++ 程式碼來說可能會非常慢。

警告

一個非常重要的注意事項:建置發佈版本時,永遠不應使用 BUILD_SHARED_LIBS 這個 CMake 選項。該選項的存在僅僅是為了最佳化開發者的工作流程。由於設計和實作上的決策,LLVM 依賴於全域資料,這些資料最終可能會在共用函式庫之間重複,從而導致錯誤。因此,這不是發佈 LLVM 或基於 LLVM 工具的安全方式。

建置具備合理效能的發佈版本的最簡單範例,可以在位於 clang/cmake/caches/DistributionExample.cmake 的 DistributionExample CMake 快取檔案中找到。以下指令將執行並安裝發佈版本:

$ cmake -G Ninja -C <path to clang>/cmake/caches/DistributionExample.cmake <path to LLVM source>
$ ninja stage2-distribution
$ ninja stage2-install-distribution

installinstall-distribution 之間的差異

一個細微但重要的區別是 installinstall-distribution 目標之間的差異。install 目標預計安裝您建置配置為產生的每個 LLVM 部分,除了 LLVM 測試工具。而 install-distribution 目標(建議用於建置發佈版本)僅安裝 LLVM 的特定部分,這些部分在配置時由 LLVM_DISTRIBUTION_COMPONENTS 指定。

此外,預設情況下,install 目標會將 LLVM 測試工具安裝為公開工具。這可以透過將 LLVM_INSTALL_TOOLCHAIN_ONLY 設定為 On 來更改。LLVM 工具旨在用於 LLVM 的開發和測試,並且應該只包含在支援 LLVM 開發的發佈版本中。

使用 LLVM_DISTRIBUTION_COMPONENTS 進行建置時,建置系統還會產生一個 distribution 目標,用於建置清單中指定的所有元件。這是一個方便的建置目標,允許僅建置發佈的部分,而無需建置所有已配置的目標。

多發佈版本配置

上述的 install-distribution 目標用於建置單一發佈版本。LLVM 的建置系統也支援建置多個發佈版本,可用於例如:一個發佈版本僅包含工具,另一個包含函式庫(以啟用開發)。這些是透過將 LLVM_DISTRIBUTIONS 變數設定為包含所有發佈版本名稱的清單(通常以大寫字母開頭,例如「Development」),然後將 LLVM_<distribution>_DISTRIBUTION_COMPONENTS 變數設定為該發佈版本的目標清單來配置的。對於每個發佈版本,建置系統都會產生一個 install-${distribution}-distribution 目標(其中 ${distribution} 是發佈版本的名稱,採用小寫)來安裝該發佈版本。

每個發佈版本都會建立自己的一組 CMake 匯出,而安裝專案特定發佈版本的 CMake 匯出的目標名為 ${project}-${distribution}-cmake-exports,其中 ${project} 是專案的名稱(採用小寫),而 ${distribution} 是發佈版本的名稱(採用小寫),除非專案是 LLVM,在這種情況下,目標僅命名為 ${distribution}-cmake-exports。這些目標需要明確包含在 LLVM_<distribution>_DISTRIBUTION_COMPONENTS 變數中,才能作為發佈版本的一部分包含在內。

與單一發佈版本設定不同,當建置多個發佈版本時,任何在 LLVM_RUNTIME_DISTRIBUTION_COMPONENTS 中指定的組件都不會自動新增到任何發佈版本中。反之,您必須在某些 LLVM_<distribution>_DISTRIBUTION_COMPONENTS 清單中明確包含目標。

預設情況下,每個目標都可以出現在多個發佈版本中;目標將作為其出現的所有發佈版本的一部分進行安裝,並由其出現的最後一個發佈版本匯出(發佈版本的順序是它們在 LLVM_DISTRIBUTIONS 中出現的順序)。我們還定義了一些傘型目標(例如 llvm-libraries 來安裝所有 LLVM 函式庫);目標可以出現在与其傘型目標不同的發佈版本中,在這種情況下,目標將由其出現的發佈版本匯出(而不是其傘型目標出現的發佈版本)。如果您希望強制目標僅出現在一個發佈版本中,並且傘型目標與目標發佈版本一致,請將 LLVM_STRICT_DISTRIBUTIONS 設定為 On

我們強烈建議您參考 clang/cmake/caches/MultiDistributionExample.cmake 作為設定多個發佈版本的範例。

僅限函式庫發佈版本的特殊注意事項

LLVM 最強大的功能之一是其函式庫優先的設計理念,以及您可以使用 LLVM 不同部分組合各種工具的方式。即使在這種情況下,也不支援使用 BUILD_SHARED_LIBS。如果您想將 LLVM 作為共用函式庫發佈以供工具使用,建議的方法是使用 LLVM_BUILD_LLVM_DYLIB,並且您可以使用 LLVM_DYLIB_COMPONENTS 來設定哪些 LLVM 組件是 libLLVM 的一部分。注意:LLVM_BUILD_LLVM_DYLIB 在 Windows 上不可用。

最佳化 LLVM 的選項

我們的 CMake 建置系統支援四種主要的建置最佳化。執行 bootstrap 建置時,除了將第一階段編譯器的 CMAKE_BUILD_TYPE 設定為 Release 之外,進行其他任何操作都是沒有益處的。這是因為更強化的最佳化執行成本很高,而且第一階段編譯器會被丟棄。所有描述的其他選項都應該在第二階段編譯器上設定,可以使用 CMake 快取檔案,也可以在選項前面加上 BOOTSTRAP_

第一個也是最容易使用的選項是透過設定 CMAKE_BUILD_TYPE 選項來設定編譯器最佳化級別。主要感興趣的值是 ReleaseRelWithDebInfo。預設情況下,Release 選項使用 -O3 最佳化級別,而 RelWithDebInfo 使用 -O2。如果您想產生除錯資訊並使用 -O3,您可以覆寫 C 和 CXX 的 CMAKE_<LANG>_FLAGS_RELWITHDEBINFO 選項。DistributionExample.cmake 就會這樣做。

另一個易於使用的選項是連結時最佳化。您可以將第二階段建置的 LLVM_ENABLE_LTO 選項設定為 ThinFull,以使用 LTO 建置 LLVM。這些選項將顯著增加發佈版本中二進位檔案的連結時間,但會建立更快的二進位檔案。如果您的發佈版本包含靜態程式庫,則不應使用此選項,因為程式庫中的物件將是 LLVM 位元碼,而這是不具可攜性的。

進階建置設定 文件說明了用於產生 LLVM 分析資訊以驅動設定檔導向最佳化的內建工具。樹狀結構中的分析測試非常有限,而且產生設定檔需要相當長的時間,但它可以顯著提升所產生之二進制檔案的效能。

除了 PGO 分析之外,我們在樹狀結構中也為產生連結器順序檔案提供了有限的支援。這些檔案為連結器提供了最終二進制檔案配置中函式的建議排序。透過實際將在時間上彼此接近呼叫的函式分組,這可以顯著加快 clang 的速度。目前的工具僅適用於具有 dtrace(1) 的 Darwin 系統。值得注意的是,dtrace 是非決定性的,因此使用 dtrace 產生的順序檔案也是非決定性的。

減少大小的選項

警告

任何減少二進制檔案大小的步驟都會犧牲所產生之二進制檔案的執行階段效能。

減少二進制檔案大小最簡單且最不重要的方法是將 *CMAKE_BUILD_TYPE* 變數設定為 MinSizeRel,這會將編譯器最佳化等級設定為 -Os,以最佳化二進制檔案大小。這對大小的效益最小,對效能的影響也最小。

減少二進制檔案大小最有效的方法是將 LLVM 動態連結到所有工具中。這可以透過減少基於 LLVM 的工具之間常見程式碼的重複來減少程式碼大小。這可以透過將以下兩個 CMake 選項設定為 On 來完成:*LLVM_BUILD_LLVM_DYLIB* 和 *LLVM_LINK_LLVM_DYLIB*。

警告

發行版不應使用 *BUILD_SHARED_LIBS* CMake 選項建置。(如需詳細說明,請參閱上述警告)。

相關的 CMake 選項

本節提供旨在協助建構發行版的 CMake 選項文件。這不是一份詳盡的清單,使用 CMake 建置 LLVM 頁面中記載了許多其他選項。一些已經記載的重要選項包括:*LLVM_TARGETS_TO_BUILD*、*LLVM_ENABLE_PROJECTS*、*LLVM_ENABLE_RUNTIMES*、*LLVM_BUILD_LLVM_DYLIB* 和 *LLVM_LINK_LLVM_DYLIB*。

LLVM_ENABLE_RUNTIMES:字串

建置包含 LLVM 執行階段專案(即 libcxx、compiler-rt、libcxxabi、libunwind…)的發行版時,使用剛建置的編譯器建置這些專案非常重要。

LLVM_DISTRIBUTION_COMPONENTS:字串

此變數可以設定為要安裝的 LLVM 建置系統元件的分號分隔清單。所有基於 LLVM 的工具都是元件,大多數函式庫和執行階段也是如此。元件名稱與建置系統目標的名稱相符。

LLVM_DISTRIBUTIONS:字串

此變數可以設定為發行版的分號分隔清單。如需此變數和其他用於設定多個發行版的 CMake 變數的詳細資訊,請參閱上面的 多發行版設定 一節。

LLVM_RUNTIME_DISTRIBUTION_COMPONENTS:字串

此變數可以設定為執行階段函式庫元件的分號分隔清單。這與 *LLVM_ENABLE_RUNTIMES* 結合使用,以指定要包含在發行版中的執行階段函式庫元件。如同 *LLVM_DISTRIBUTION_COMPONENTS*,元件名稱與建置系統目標的名稱相符。

LLVM_DYLIB_COMPONENTS:字串

此變數可以設定為 LLVM 函式庫元件的分號分隔名稱。LLVM 函式庫元件是移除 LLVM 前綴的函式庫名稱(即 Support、Demangle…)、LLVM 目標名稱或特殊用途的元件名稱。特殊用途的元件名稱為

  1. all - 所有可用的 LLVM 組件庫

  2. Native - 原生系統的 LLVM 目標

  3. AllTargetsAsmParsers - 所有包含的目標 ASM 解析器庫

  4. AllTargetsDescs - 所有包含的目標描述庫

  5. AllTargetsDisassemblers - 所有包含的目標反組譯器庫

  6. AllTargetsInfos - 所有包含的目標資訊庫

LLVM_INSTALL_TOOLCHAIN_ONLY:BOOL

此選項預設為 Off:當設為 On 時,它會從預設的 install 目標中移除許多 LLVM 開發和測試工具以及組件庫。不建議在發行版中包含開發工具,因為許多 LLVM 工具僅供開發和測試使用。