LLVM のディストリビューションの構築

はじめに

このドキュメントは、LLVM およびディストリビューション用の任意の LLVM サブプロジェクトツールの組み合わせを構築してパッケージ化したい人を対象としています。このドキュメントでは、LLVM ビルドシステムの便利な機能、ベストプラクティス、および LLVM のパッケージ化に関する一般的な情報を説明します。

CMake を初めて使用する場合は、CMake による LLVM の構築 または CMake 入門 ドキュメントが役立つかもしれません。このドキュメントで説明する内容の一部は、高度なビルド構成 ドキュメントで説明するビルトの内部処理です。

一般的なディストリビューションのガイドライン

コンパイラのディストリビューションを構築する場合は、一般的にコンパイラのブートストラップビルドを実行することをお勧めします。つまり、ホストツールチェーンを使用して「ステージ 1」のコンパイラを構築し、「ステージ 1」のコンパイラを使用して「ステージ 2」のコンパイラを構築します。これにより、ディストリビューションされるコンパイラが、新しいコンパイラによって提供されるすべてのバグ修正、パフォーマンスの最適化、および一般的な改善から恩恵を受けることができます。

ディストリビューションを構築する方法を決定する際には、評価する必要があるトレードオフがいくつかあります。主な 2 つは次のとおりです。

  1. ディストリビューションのコンパイル時間 対 ビルドされたコンパイラのパフォーマンス

  2. ディストリビューションのバイナリサイズ 対 ビルドされたコンパイラのパフォーマンス

生成されたコンパイラの性能を最大化するためのガイドラインは、LTO、PGO、および静的リンクをすべて使用することです。その結果、ディストリビューション全体が大きくなり、生成するのに時間がかかりますが、コンパイラが最適化する機会が最も大きくなります。

ディストリビューションのサイズを最小化するガイドラインは、LLVM ライブラリと Clang ライブラリを動的にツールにリンクしてコードの重複を削減することです。これにより、生成されるバイナリの最適化機会が減少するため、および動的リンクではプロセス起動時にシンボルを解決する必要があるため(C++ コードでは非常に低速になる場合があります)大幅なパフォーマンス低下が発生します。

警告

非常に重要な注意点が 1 つあります。ディストリビューションは 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_DISTRIBUTION_COMPONENTS で指定されたように特定の LLVM の部分のみをインストールします。

さらに、デフォルトでは install ターゲットは LLVM テストツールをパブリックツールとしてインストールします。これは、LLVM_INSTALL_TOOLCHAIN_ONLYOn に設定することで変更できます。LLVM ツールは LLVM の開発とテストを目的としており、LLVM 開発をサポートするディストリビューションにのみ含める必要があります。

LLVM_DISTRIBUTION_COMPONENTS でビルドする場合、ビルドシステムはリストに指定されたすべてのコンポーネントを構築する distribution ターゲットも生成します。これは、すべての構成されたターゲットを構築する必要なしに、配布された部分のみを構築できるようにする便利なビルドターゲットです。

マルチディストリビューション構成

上で説明した install-distribution ターゲットは、単一のディストリビューションを構築するためのものです。LLVM のビルドシステムはまた複数のディストリビューションを構築することもサポートしており、たとえば 1 つのディストリビューションにツールのみを含め、他のディストリビューションにライブラリを含める(開発を有効にする)ために使用できます。これらは、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 ライブラリをインストールするためのアンブレラターゲット(例: llvm-libraries)も定義します。ターゲットはアンブレラの異なる頒布に表示できます。この場合、ターゲットは表示されている頒布によってエクスポートされます(アンブレラが表示されている頒布ではありません)。ターゲットが 1 つの頒布にのみ表示されるようにし、アンブレラ頒布がターゲット頒布と一致する場合に、LLVM_STRICT_DISTRIBUTIONSOn に設定します。

複数の頒布を構成する例としては、clang/cmake/caches/MultiDistributionExample.cmake を参照することを強くお勧めします。

ライブラリ限定の頒布に関する特別なメモ

LLVM の最も優れた機能の 1 つは、ライブラリを第一に考える設計思想と、LLVM のさまざまな部分を組み合わせてさまざまなツールを作成できる方法です。このような状況でも、BUILD_SHARED_LIBS の使用はサポートされていません。ツール内で使用する共有ライブラリとして LLVM を頒布する場合は、LLVM_BUILD_LLVM_DYLIB を使用する方法が推奨され、LLVM_DYLIB_COMPONENTS を使用して libLLVM に含める LLVM コンポーネントを構成できます。注意: LLVM_BUILD_LLVM_DYLIB は Windows では使用できません。

LLVM の最適化オプション

CMake ビルドシステムでサポートしているビルド最適化は 4 つあります。ブートストラップビルドを実行する場合、第 1 段階コンパイラに対して CMAKE_BUILD_TYPERelease に設定する以外にメリットはありません。より高レベルの最適化を実行するにはコストがかかり、第 1 段階コンパイラは破棄されるためです。ここで説明する今後のオプションはすべて、CMake キャッシュファイルを使用して、または BOOTSTRAP_ でオプションにプレフィックスを付けて第 2 段階コンパイラに設定する必要があります。

最も簡単なのは、CMAKE_BUILD_TYPEオプションを設定することでコンパイラの最適化レベルを設定することです。主な値はReleaseまたはRelWithDebInfoです。既定では、Releaseオプションは-O3最適化レベルを使用し、RelWithDebInfo-O2を使用します。デバッグ情報を作成し-O3を使用する場合、CおよびCXXのCMAKE_<LANG>_FLAGS_RELWITHDEBINFOオプションを上書きできます。DistributionExample.cmakeはこれを行います。

もう1つの簡単なオプションは、リンク時最適化です。ステージ2ビルドでLLVM_ENABLE_LTOオプションをThinまたはFullに設定して、LTOを使用してLLVMをビルドできます。これらのオプションは、配布におけるバイナリのリンク時間を大幅に短縮しますが、非常に高速なバイナリを作成します。アーカイブに静的アーカイブが含まれている場合、アーカイブ内のオブジェクトはポータブルではないLLVMビットコードになるため、このオプションを使用しないでください。

高度なビルド構成ドキュメントには、プロファイル駆動型最適化を駆動するためのLLVMプロファイリング情報を生成するための組み込みのツールに関する説明があります。ツリー内のプロファイリングテストは非常に限定されており、プロファイルの生成にはかなりの時間がかかりますが、生成されたバイナリの性能が大幅に向上する可能性があります。

PGOプロファイリングに加えて、リンカー順序ファイルの生成をツリー内で部分的にサポートしています。これらのファイルは、最終的なバイナリのレイアウトで関数の推奨順序をリンカーに提供します。これにより、時間的に近い関数同士を物理的にグループ化することでclangの速度が向上します。現在のツールは、dtrace(1)が使用されているDarwinシステムでのみ利用できます。dtraceは非決定論的であることに注意してください。つまり、dtraceを使用した順序ファイル生成も非決定論的です。

サイズを削減するためのオプション

警告

バイナリのサイズを削減するために講じる措置はすべて、生成されたバイナリのランタイムパフォーマンスの低下を招きます。

バイナリのサイズを削減するための最も簡単で最も重要な方法は、CMAKE_BUILD_TYPE変数をMinSizeRelに設定することです。これにより、コンパイラの最適化レベルが-Osに設定され、バイナリのサイズが最適化されます。これにより、サイズへの影響は最小限で、パフォーマンスへの影響はほとんどありません。

バイナリのサイズを削減する最も効果的な方法は、すべてのツールにLLVMを動的にリンクすることです。これにより、LLVMベースのツール間で一般的なコードの重複を減らしてコードサイズが縮小されます。これを行うには、次の2つのCMakeオプションをOnに設定します。LLVM_BUILD_LLVM_DYLIBLLVM_LINK_LLVM_DYLIB

警告

ディストリビューションはBUILD_SHARED_LIBS CMakeオプションを使用してビルドしないでください。(詳細については、上記の警告を参照してください)。

関連するCMakeオプション

このセクションでは、ディストリビューションの構築を支援するために用意された CMake オプションのドキュメントを提供します。これは完全なリストではありません。追加のオプションの多くは CMake での LLVM の構築 ページにドキュメント化されています。既にドキュメント化されている主なオプションには、LLVM_TARGETS_TO_BUILDLLVM_ENABLE_PROJECTSLLVM_ENABLE_RUNTIMESLLVM_BUILD_LLVM_DYLIBLLVM_LINK_LLVM_DYLIB が含まれます。

LLVM_ENABLE_RUNTIMES:STRING

LLVM ランタイムプロジェクト (例: libcxx、compiler-rt、libcxxabi、libunwind…) を含むディストリビューションを構築する場合は、これらのプロジェクトを構築したばかりのコンパイラで構築することが重要です。

LLVM_DISTRIBUTION_COMPONENTS:STRING

この変数は、インストールする LLVM 構築システムコンポーネントのセミコロン区切りのリストに設定できます。ほとんどのライブラリとランタイムだけでなく、すべての LLVM ベースのツールもコンポーネントです。コンポーネント名は、構築システムターゲットの名前と一致します。

LLVM_DISTRIBUTIONS:STRING

この変数は、セミコロン区切りのディストリビューションのリストに設定できます。複数のディストリビューションを設定する方法については、上の 複数のディストリビューションの設定 セクションを参照してください。

LLVM_RUNTIME_DISTRIBUTION_COMPONENTS:STRING

この変数は、ランタイムライブラリコンポーネントのセミコロン区切りのリストに設定できます。これは LLVM_ENABLE_RUNTIMES と組み合わせて、ディストリビューションに含めるランタイムライブラリのコンポーネントを指定するために使用されます。LLVM_DISTRIBUTION_COMPONENTS と同様に、コンポーネント名は構築システムターゲットの名前と一致します。

LLVM_DYLIB_COMPONENTS:STRING

この変数は、LLVM ライブラリコンポーネントのセミコロン区切りの名前に設定できます。LLVM ライブラリコンポーネントは、LLVM プレフィックスが削除されたライブラリ名 (例: Support、Demangle…)、LLVM ターゲット名、または特殊用途コンポーネント名です。特殊用途コンポーネント名は次のとおりです。

  1. all - 利用可能なすべての LLVM コンポーネントライブラリ

  2. Native - Native システムの LLVM ターゲット

  3. AllTargetsAsmParsers - すべてのターゲット ASM パーサライブラリのコンパイル

  4. AllTargetsDescs - すべてのターゲットの説明ライブラリのコンパイル

  5. AllTargetsDisassemblers - すべての対象ディスアセンブラライブラリのコンパイル

  6. AllTargetsInfos - すべてのターゲット情報ライブラリのコンパイル

LLVM_INSTALL_TOOLCHAIN_ONLY:BOOL

このオプションはデフォルトで Off に設定されています。On に設定すると、デフォルトの install ターゲットから多くの LLVM 開発およびテストツールやコンポーネントライブラリが削除されます。多くの LLVM ツールは開発とテストでの使用のみを目的としているので、ディストリビューションに開発ツールを含めることは推奨されません。