高度なビルド構成

はじめに

CMake は、クロスプラットフォームのビルドジェネレータツールです。CMake はプロジェクトをビルドするのではなく、LLVM をビルドするためにビルドツール (GNU make、Visual Studio など) で必要なファイルを生成します。

新しいコントリビューターの方 は、LLVMシステム入門 または CMakeを使用したLLVMのビルド のページから始めてください。このページは、より複雑なビルドを実行するユーザーを対象としています。

以下の例の多くは、特定の CMake ジェネレーターを想定して記述されています。特に明記されていない限り、これらのコマンドはどの CMake ジェネレーターでも動作するはずです。

このドキュメントページに記載されている多くのビルド構成は、CMake キャッシュを使用することで利用できます。CMake キャッシュは、基本的に特定のビルド構成に必要なフラグを設定する構成ファイルです。Clang のキャッシュは、モノレポ内の /clang/cmake/caches にあります。以下の例に示すように、追加の構成フラグとともに、-C フラグを使用して CMake に渡すことができます。

ブートストラップビルド

Clang CMake ビルドシステムは、ブートストラップ (別名: マルチステージ) ビルドをサポートしています。大まかに言えば、マルチステージビルドは、あるステージから次のステージにデータを渡す一連のビルドです。これの最も一般的でシンプルなバージョンは、従来のブートストラップビルドです。

シンプルな 2 ステージブートストラップビルドでは、システムコンパイラを使用して 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 呼び出しは、C および C++ の stage2 ビルド中にのみ '-fno-addrsig' を有効にします。

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

Clang ビルドシステムでは、ビルドをステージとして参照します。stage1 ビルドはホストにインストールされているコンパイラを使用する標準ビルドであり、stage2 ビルドは stage1 コンパイラを使用してビルドされます。この命名規則は、より多くのステージにも当てはまります。一般に、stage*n* ビルドは、stage*n-1* からの出力を使用してビルドされます。

Apple Clang ビルド (より複雑なブートストラップ)

Apple の Clang ビルドは、シンプルなブートストラップシナリオの少し複雑な例です。Apple Clang は、2 段階ビルドを使用してビルドされます。

stage1 コンパイラは、いくつかのオプションが設定されたホストのみのコンパイラです。stage1 コンパイラは使い捨てであるため、最適化とビルド時間のバランスが取れています。stage2 コンパイラは、ユーザーに出荷されることを目的とした完全に最適化されたコンパイラです。

これらのコンパイラを設定するには、多くのオプションが必要です。構成を簡素化するために、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 呼び出しは、stage1 ホストコンパイラを構成し、CLANG_BOOTSTRAP_CMAKE_ARGS を設定して、Apple-stage2.cmake キャッシュスクリプトを stage2 構成ステップに渡します。

stage2-distribution ターゲットをビルドすると、最小限の stage1 コンパイラと必要なツールがビルドされ、次に Apple-stage2.cmake の設定に基づいて stage2 コンパイラが構成およびビルドされます。

キャッシュスクリプトを使用して複雑な設定を行い、特に後のステージビルドにキャッシュスクリプトを含めるようにするこのパターンは、より高度なビルド構成で一般的です。

マルチステージ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_ プレフィックスは、インストルメント化された stage 2 ビルドに変数を渡すように CMake に指示します。また、CLANG_PGO_TRAINING_DEPS オプションを使用すると、外部プロジェクトをビルドする前にビルドする追加のビルドターゲットを指定できます。LLVM テストスイートには、ビルドするために compiler-rt が必要であるため、runtimes ターゲットを依存関係として追加する必要があります。

構成後、stage2-instrumented-generate-profdata ターゲットをビルドすると、stage1 コンパイラが自動的にビルドされ、stage1 コンパイラを使用してインストルメント化されたコンパイラがビルドされ、次にインストルメント化されたコンパイラが perf トレーニングデータに対して実行されます。

$ ninja stage2-instrumented-generate-profdata

これを数時間ほど実行すると、ビルドディレクトリに profdata ファイルが配置されます。Clang を 2 回ビルドするため、これには非常に時間がかかり、ビルドツリーに 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 オプションにフィードできます。

組み込み関数やランタイムライブラリなど、perf トレーニングを実行する前に追加のターゲットをビルドする必要がある場合があります。その目的には、CLANG_PGO_TRAINING_DEPS CMake 変数を使用できます。

set(CLANG_PGO_TRAINING_DEPS builtins runtimes CACHE STRING "")

PGO キャッシュには、他のマルチステージビルドとは少し異なるステージ命名スキームがあります。stage1、stage2-instrumented、stage2 の 3 つのステージを生成します。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 (Binary Optimization and Layout Tool) は、実行時にバイナリをプロファイリングし、その情報を使用して最終バイナリのレイアウトを最適化するツールです。バイナリレベルで実行されるその他の最適化も行います。BOLT を使用して LLVM/Clang をビルドするための CMake キャッシュも利用できます。

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を使用することで、実行時の速度をさらに大幅に向上させることができます。BOLTで最適化されたバイナリを生成するThinLTO付きの3段階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段階の非決定性

コンパイラの古くからの言い伝えでは、非決定性は多頭のヒドラのようなものです。その頭が顔を出すたびに、恐怖と混沌が生じます。

歴史的に、コンパイラが決定論的であることを検証するためのテストの1つは、3段階ビルドでした。3段階ビルドの考え方は、ソースからコンパイラ(stage1)をビルドし、次にそのコンパイラを使用してソースを再ビルド(stage2)し、次にそのコンパイラを使用してstage2ビルドと同一の構成でソースを3回目(stage3)に再ビルドするというものです。この最後に、ビット単位で同一であるべきstage2とstage3のコンパイラが得られます。

LLVMとclangを使用して、これらの3段階ビルドの1つを次のコマンドで実行できます。

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

ビルド後、stage2とstage3のコンパイラを比較できます。