進階建置配置

簡介

CMake 是一個跨平台的建置產生器工具。CMake 不會建置專案,它會產生建置工具(GNU make、Visual Studio 等)建置 LLVM 所需的檔案。

如果您是**新貢獻者**,請從 LLVM 系統入門使用 CMake 建置 LLVM 頁面開始。本頁面適用於執行更複雜建置的使用者。

以下許多範例都是假設使用特定的 CMake 產生器編寫的。除非另有明確說明,否則這些命令應該可以與任何 CMake 產生器一起使用。

本文件頁面上提到的許多建置配置都可以透過使用 CMake 快取來使用。CMake 快取本質上是一個設定檔,它會為特定的建置配置設定必要的旗標。Clang 的快取位於 monorepo 中的 /clang/cmake/caches 中。它們可以使用 -C 旗標傳遞給 CMake,如下列範例以及其他配置旗標所示。

自舉建置

Clang CMake 建置系統支援自舉(又稱多階段)建置。在高層級上,多階段建置是一系列建置,它們將資料從一個階段傳遞到下一個階段。最常見且簡單的版本是傳統的自舉建置。

在簡單的兩階段自舉建置中,我們使用系統編譯器建置 clang,然後使用剛才建置的 clang 再次建置 clang。在 CMake 中,可以使用單一選項 CLANG_ENABLE_BOOTSTRAP 來配置這種最簡單的自舉建置形式。

$ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release \
    -DCLANG_ENABLE_BOOTSTRAP=On \
    -DLLVM_ENABLE_PROJECTS="clang" \
    <path to source>/llvm
$ ninja stage2

這個命令本身並不是很有用,因為它假設每個階段都有預設配置。下一系列範例使用 CMake 快取腳本來提供更複雜的選項。

根據預設,只有一些 CMake 選項會在階段之間傳遞。這個清單稱為 _BOOTSTRAP_DEFAULT_PASSTHROUGH,在 clang/CMakeLists.txt 中定義。若要強制在階段之間傳遞變數,請使用 -DCLANG_BOOTSTRAP_PASSTHROUGH CMake 選項,每個變數以「;」分隔。例如

$ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release \
    -DCLANG_ENABLE_BOOTSTRAP=On \
    -DCLANG_BOOTSTRAP_PASSTHROUGH="CMAKE_INSTALL_PREFIX;CMAKE_VERBOSE_MAKEFILE" \
    -DLLVM_ENABLE_PROJECTS="clang" \
    <path to source>/llvm
$ ninja stage2

BOOTSTRAP_ 開頭的 CMake 選項只會傳遞到 stage2 建置。這讓您可以使用 Clang 特定的建置旗標。例如,以下 CMake 呼叫只會在 stage2 建置期間為 C 和 C++ 啟用「-fno-addrsig」。

$ cmake [..]  -DBOOTSTRAP_CMAKE_CXX_FLAGS='-fno-addrsig' -DBOOTSTRAP_CMAKE_C_FLAGS='-fno-addrsig' [..]

clang 建置系統將建置稱為階段。 階段 1 建置是使用安裝在主機上的編譯器的標準建置,而階段 2 建置是使用階段 1 編譯器建置的。 這個命名法也適用於更多階段。 一般來說,階段 *n* 建置是使用階段 *n-1* 的輸出建置的。

Apple Clang 建置(更複雜的引導程序)

Apple 的 Clang 建置是簡單引導程序情境的稍微複雜一點的範例。 Apple Clang 是使用 2 階段建置建置的。

階段 1 編譯器是僅限主機的編譯器,並設定了一些選項。 階段 1 編譯器在優化與建置時間之間取得平衡,因為它是用完即丟的。 階段 2 編譯器是要發送給使用者的完全優化編譯器。

設定這些編譯器需要許多選項。 為了簡化設定,Apple Clang 建置設定包含在 CMake 快取檔案中。 您可以使用以下指令建置 Apple Clang 編譯器

$ cmake -G Ninja -C <path to source>/clang/cmake/caches/Apple-stage1.cmake <path to source>/llvm
$ ninja stage2-distribution

這個 CMake 呼叫會設定階段 1 主機編譯器,並設定 CLANG_BOOTSTRAP_CMAKE_ARGS 以將 Apple-stage2.cmake 快取腳本傳遞給階段 2 設定步驟。

當您建置 stage2-distribution 目標時,它會建置最小的階段 1 編譯器和所需的工具,然後根據 Apple-stage2.cmake 中的設定設定和建置階段 2 編譯器。

這種使用快取腳本設定複雜設定的模式,特別是讓後續階段建置包含快取腳本,在我們更進階的建置設定中很常見。

多階段 PGO

設定檔引導式優化 (PGO) 是一種優化 clang 產生程式碼的絕佳方法。 我們多階段的 PGO 建置是產生可用於優化 clang 的 PGO 設定檔的工作流程。

在較高的層級上,PGO 的運作方式是您建置一個檢測編譯器,然後針對範例原始程式檔執行檢測編譯器。 雖然檢測編譯器會執行,但它會輸出一堆包含效能計數器 (.profraw 檔案) 的檔案。 產生所有 profraw 檔案後,您可以使用 llvm-profdata 將檔案合併成單一 profdata 檔案,您可以將其饋送到 LLVM_PROFDATA_FILE 選項。

我們的 PGO.cmake 快取會自動執行整個過程。 您可以使用以下指令將其用於 CMake 的設定

$ cmake -G Ninja -C <path to source>/clang/cmake/caches/PGO.cmake \
    <path to source>/llvm

快取檔案還接受幾個額外的選項來修改建置,尤其是 PGO_INSTRUMENT_LTO 選項。 將此選項設定為 Thin 或 Full 將分別啟用 ThinLTO 或完整 LTO,透過啟用程序間優化,進一步增強 PGO 建置的效能增益。 例如,若要針對也啟用 ThinTLO 的 PGO 建置執行 CMake 設定,請使用以下指令

$ cmake -G Ninja -C <path to source>/clang/cmake/caches/PGO.cmake \
    -DPGO_INSTRUMENT_LTO=Thin \
    <path to source>/llvm

根據預設,clang 將透過編譯簡單的 hello world 程式來產生設定檔資料。 您也可以告訴 clang 使用外部專案來產生可能更適合您的使用案例的設定檔資料。 您指定的專案必須是 lit 測試套件(使用 CLANG_PGO_TRAINING_DATA 選項)或 CMake 專案(使用 CLANG_PERF_TRAINING_DATA_SOURCE_DIR 選項)。

例如,如果您想使用 LLVM 測試套件 來產生設定檔資料,您可以使用以下指令

$ cmake -G Ninja -C <path to source>/clang/cmake/caches/PGO.cmake \
     -DBOOTSTRAP_CLANG_PGO_TRAINING_DATA_SOURCE_DIR=<path to llvm-test-suite> \
     -DBOOTSTRAP_CLANG_PGO_TRAINING_DEPS=runtimes

BOOTSTRAP_ 前綴字元會告知 CMake 將變數傳遞給檢測階段二建置。 而 CLANG_PGO_TRAINING_DEPS 選項可讓您指定在建置外部專案之前要建置的其他建置目標。 LLVM 測試套件需要建置 compiler-rt,因此我們需要將 runtimes 目標新增為相依性。

配置完成後,建置 stage2-instrumented-generate-profdata 目標將會自動建置 stage1 編譯器,使用 stage1 編譯器建置已插裝的編譯器,然後針對效能訓練資料執行已插裝的編譯器。

$ ninja stage2-instrumented-generate-profdata

如果您讓它執行幾個小時左右,它會在您的建置目錄中放置一個 profdata 檔案。這需要很長時間,因為它會建置 clang 兩次,而且您必須在您的建置樹中擁有 compiler-rt。

只要原始碼檔案使用 LIT 風格的 RUN 行標記,此程序就會使用 perf-training 目錄下的任何原始碼檔案作為訓練資料。

完成後,您可以使用 find . -name clang.profdata 找到它,但它應該位於類似以下的路徑中:

<build dir>/tools/clang/stage2-instrumented-bins/utils/perf-training/clang.profdata

在建置最佳化編譯器時,您可以將該檔案提供給 LLVM_PROFDATA_FILE 選項。

在執行效能訓練之前,可能需要建置其他目標,例如內建函式和執行階段程式庫。您可以使用 CLANG_PGO_TRAINING_DEPS CMake 變數來達到此目的。

set(CLANG_PGO_TRAINING_DEPS builtins runtimes CACHE STRING "")

與其他多階段建置相比,PGO 快取的階段命名方案略有不同。它會產生三個階段:stage1、stage2-instrumented 和 stage2。這兩個 stage2 建置都是使用 stage1 編譯器建置的。

PGO 快取會產生以下額外目標:

stage2-instrumented

建置 stage1 編譯器、執行階段和必要的工具(llvm-config、llvm-profdata),然後使用該編譯器來建置已插裝的 stage2 編譯器。

stage2-instrumented-generate-profdata

依賴於 stage2-instrumented,並將使用已插裝的編譯器根據 clang/utils/perf-training 中的訓練檔案產生 profdata。

stage2

依賴於 stage2-instrumented-generate-profdata,並將使用 stage1 編譯器和 stage2 profdata 來建置 PGO 最佳化的編譯器。

stage2-check-llvm

依賴於 stage2 並使用 stage2 編譯器執行 check-llvm。

stage2-check-clang

依賴於 stage2 並使用 stage2 編譯器執行 check-clang。

stage2-check-all

依賴於 stage2 並使用 stage2 編譯器執行 check-all。

stage2-test-suite

依賴於 stage2 並使用 stage2 編譯器執行測試套件(需要樹內測試套件)。

BOLT

BOLT(二進位最佳化和佈局工具)是一種在連結後透過在執行階段分析二進位檔案來最佳化二進位檔案的工具,然後使用該資訊來最佳化最終二進位檔案的佈局,以及在二進位層級執行的其他最佳化。還有一些 CMake 快取可用於使用 BOLT 建置 LLVM/Clang。

若要配置一個建置 LLVM/Clang 並使用 BOLT 最佳化它的單階段建置,請使用以下 CMake 配置:

$ cmake <path to source>/llvm -C <path to source>/clang/cmake/caches/BOLT.cmake

然後,透過執行以下 ninja 命令來建置 BOLT 最佳化的二進位檔案:

$ ninja clang-bolt

如果您在建置過程中看到錯誤,請嘗試透過將 CMAKE_C_COMPILER 和 CMAKE_CXX_COMPILER 旗標設定為適當的值,使用最新版本的 Clang/LLVM 進行建置。

也可以在 PGO 和 (Thin)LTO 之上使用 BOLT,以進一步顯著提升執行階段速度。若要配置使用 ThinLTO 並使用 BOLT 最佳化結果二進位檔案的三階段 PGO 建置,請使用以下 CMake 配置命令:

$ cmake -G Ninja <path to source>/llvm \
    -C <path to source>/clang/cmake/caches/BOLT-PGO.cmake \
    -DBOOTSTRAP_LLVM_ENABLE_LLD=ON \
    -DBOOTSTRAP_BOOTSTRAP_LLVM_ENABLE_LLD=ON \
    -DPGO_INSTRUMENT_LTO=Thin

然後,若要建置最終最佳化的二進位檔案,請建置 stage2-clang-bolt 目標。

$ ninja stage2-clang-bolt

3 階段非確定性

在編譯器的古老傳說中,非確定性就像多頭蛇。每當它的頭冒出來時,就會發生恐怖和混亂。

過去驗證編譯器是否具備確定性 (deterministic) 的測試方法之一是三階段建置。三階段建置的概念是,您先使用原始碼建置一個編譯器(階段 1),然後使用該編譯器重新建置原始碼(階段 2),接著使用該編譯器以與階段 2 建置相同的配置再次重新建置原始碼(階段 3)。在此過程結束時,您將擁有兩個應該完全相同的階段 2 和階段 3 編譯器。

您可以使用以下指令,透過 LLVM 和 clang 執行其中一種三階段建置。

$ cmake -G Ninja -C <path to source>/clang/cmake/caches/3-stage.cmake <path to source>/llvm
$ ninja stage3

建置完成後,您可以比較階段 2 和階段 3 的編譯器。