LLVMテストインフラストラクチャガイド¶
概要¶
このドキュメントは、LLVMテストインフラストラクチャのリファレンスマニュアルです。LLVMテストインフラストラクチャの構造、使用するのに必要なツール、テストの追加と実行方法について説明しています。
要件¶
LLVMテストインフラストラクチャを使用するには、LLVMのビルドに必要なすべてのソフトウェアに加え、Python 3.8以降が必要です。
LLVMテストインフラストラクチャの構成¶
LLVMテストインフラストラクチャには、単体テスト、回帰テスト、および完全なプログラムという3つの主要なカテゴリのテストが含まれています。単体テストと回帰テストは、LLVMリポジトリ内のllvm/unittestsおよびllvm/testの下にそれぞれ含まれており、常にパスすることが期待されています。これらは、すべてのコミットの前に実行する必要があります。
完全なプログラムのテストは、「LLVMテストスイート」(または「test-suite」)と呼ばれ、test-suite GitHubのリポジトリにあります。歴史的な理由から、これらのテストは「ナイトリーテスト」とも呼ばれていますが、「test-suite」よりも曖昧さが少なく、ナイトリーよりもはるかに頻繁に実行されるにもかかわらず、今でも使用されています。
単体テスト¶
単体テストは、Google TestとGoogle Mockを使用して記述され、llvm/unittestsディレクトリにあります。一般的に、単体テストはサポートライブラリやその他の汎用データ構造を対象としており、IRに対する変換や分析のテストには回帰テストに依存することを好みます。
回帰テスト¶
回帰テストは、LLVMの特定の機能をテストするか、LLVMの特定のバグをトリガーする小さなコードです。使用される言語は、テスト対象のLLVMの部分によって異なります。これらのテストは、Litテストツール(LLVMの一部)によって駆動され、llvm/testディレクトリにあります。
通常、LLVMでバグが見つかった場合、問題を再現するのに十分なコードを含む回帰テストを記述し、このディレクトリの下のどこかに配置する必要があります。たとえば、実際のアプリケーションまたはベンチマークから抽出された小さなLLVM IRの一部にすることができます。
テスト分析¶
分析とは、IRの一部に関するプロパティを推論し、それを変換しないパスです。一般的に、回帰テストと同じインフラストラクチャを使用して、分析結果を消費し、FileCheckに適したテキスト形式で標準出力に出力する別の「プリンター」パスを作成することでテストされます。llvm/test/Analysis/BranchProbabilityInfo/loop.llはそのようなテストの例です。
test-suite¶
テストスイートには、完全なプログラムが含まれており、コンパイルしてスタンドアロンプログラムにリンクできるコードの一部です。これらのプログラムは、一般的にCやC++などの高水準言語で記述されています。
これらのプログラムは、ユーザーが指定したコンパイラとフラグのセットを使用してコンパイルされ、実行されてプログラム出力とタイミング情報を取得します。これらのプログラムの出力が参照出力と比較され、プログラムが正しくコンパイルされていることが確認されます。
プログラムのコンパイルと実行に加えて、完全なプログラムテストは、生成されたプログラムの効率と、LLVMがコードをコンパイル、最適化、および生成する速度の両方において、LLVMのパフォーマンスをベンチマークする方法として機能します。
test-suiteは、test-suite GitHubのリポジトリにあります。
test-suiteガイドで詳細を参照してください。
デバッグ情報テスト¶
テストスイートには、デバッグ情報の品質をチェックするテストが含まれています。テストは、Cベースの言語またはLLVMアセンブリ言語で記述されています。
これらのテストは、デバッガでコンパイルおよび実行されます。デバッガの出力がチェックされ、デバッグ情報の検証が行われます。詳細については、テストスイートのREADME.txtを参照してください。このテストスイートは、cross-project-tests/debuginfo-testsディレクトリにあります。
クイックスタート¶
テストは、2つの別々のリポジトリにあります。単体テストと回帰テストは、メインの「llvm」/ディレクトリの下のllvm/unittestsおよびllvm/testディレクトリにあります(そのため、メインのLLVMツリーでこれらのテストを無料で取得できます)。LLVMをビルドした後にmake check-allを使用して、単体テストと回帰テストを実行します。
test-suiteモジュールには、完全なCおよびC++プログラムを含む、より包括的なテストが含まれています。詳細については、test-suiteガイドを参照してください。
単体テストと回帰テスト¶
すべてのLLVM単体テストを実行するには、check-llvm-unitターゲットを使用します。
% make check-llvm-unit
すべてのLLVM回帰テストを実行するには、check-llvmターゲットを使用します。
% make check-llvm
妥当なテストパフォーマンスを得るには、リリースモードでLLVMとサブプロジェクトをビルドします。
% cmake -DCMAKE_BUILD_TYPE="Release" -DLLVM_ENABLE_ASSERTIONS=On
Clangをチェックアウトしてビルドした場合、次を使用してLLVMとClangのテストを同時に実行できます。
% make check-all
Valgrind(デフォルトでMemcheck)を使用してテストを実行するには、LIT_ARGS make変数を使用して、必要なオプションをlitに渡します。たとえば、次を使用できます。
% make check LIT_ARGS="-v --vg --vg-leak"
Valgrindを使用したテストと、リークチェックが有効になっているテストを有効にします。
個々のテストまたはテストのサブセットを実行するには、LLVMの一部としてビルドされるllvm-litスクリプトを使用できます。たとえば、Integer/BitPacked.llテストを単独で実行するには、次を実行します。
% llvm-lit ~/llvm/test/Integer/BitPacked.ll
または、すべてのARM CodeGenテストを実行するには
% llvm-lit ~/llvm/test/CodeGen/ARM
回帰テストは、**ユーザー以外**の場所にインストールされている場合のみ、Pythonのpsutilモジュールを使用します。Linuxでは、sudoを使用して、または仮想環境内でインストールします。Windowsでは、すべてのユーザーに対してPythonをインストールし、管理者権限を持つコマンドプロンプトでpip install psutilを実行します。
litツールの使用方法の詳細については、llvm-lit --helpまたはlitマニュアルページを参照してください。
デバッグ情報テスト¶
デバッグ情報テストを実行するには、cmakeコマンドラインでLLVM_ENABLE_PROJECTS定義にcross-project-testsプロジェクトを追加するだけです。
回帰テストの構造¶
LLVM回帰テストはlitによって駆動され、llvm/testディレクトリにあります。
このディレクトリには、LLVMのさまざまな機能をテストし、回帰が発生しないことを確認するための多数の小さなテストが含まれています。このディレクトリは、いくつかのサブディレクトリに分割されており、それぞれがLLVMの特定の領域に焦点を当てています。
新しい回帰テストの作成¶
回帰テストの構造は非常に単純ですが、設定する必要がある情報が必要です。この情報はcmakeを介して収集され、ビルドディレクトリのtest/lit.site.cfg.pyファイルに書き込まれます。llvm/testのMakefileがこの作業を行います。
回帰テストを機能させるには、テストの各ディレクトリにlit.local.cfgファイルが必要です。litはこのファイルを探して、テストの実行方法を決定します。このファイルは単なるPythonコードであるため非常に柔軟性がありますが、LLVM回帰テストでは標準化されています。テストのディレクトリを追加する場合は、別のディレクトリからlit.local.cfgをコピーして実行を開始してください。標準のlit.local.cfgは、単にテストで検索するファイルを指定します。ディレクトリのみを含むディレクトリには、lit.local.cfgファイルは必要ありません。Litドキュメントで詳細を参照してください。
各テストファイルには、「RUN:」で始まる行を含める必要があります。これにより、lit はテストの実行方法を認識します。RUN行がない場合、lit はテスト実行中にエラーを報告します。
RUN行は、テストプログラムのコメント内で、キーワードRUN、コロン、そして最後に実行するコマンド(パイプライン)を使用して指定します。これらの行は、lit がテストケースを実行するために実行する「スクリプト」を形成します。RUN行の構文は、I/Oリダイレクトや変数置換を含むパイプラインに対するシェルの構文と似ています。しかし、これらの行はシェルスクリプトのように見えるかもしれませんが、実際にはそうではありません。RUN行はlitによって解釈されます。したがって、構文はシェルとはいくつかの点で異なります。必要に応じて、複数のRUN行を指定できます。
lit は各RUN行に対して置換を行い、LLVMツールの名前を、各ツール用にビルドされた実行ファイルのフルパス($(LLVM_OBJ_ROOT)/bin内)に置き換えます。これにより、lit はテスト中にユーザーのパスにあるLLVMツールを誤って呼び出すことがなくなります。
各RUN行は、その最後の文字が\でない限り、他の行とは独立して実行されます。この継続文字により、RUN行は次の行と連結されます。このようにして、行の長さを長くすることなく、長いコマンドのパイプラインを構築できます。 \で終わる行は、\で終わらないRUN行が見つかるまで連結されます。この連結されたRUN行のセットが1つの実行を構成します。lit は変数を置換し、パイプラインが実行されるように設定します。パイプライン内のプロセスが失敗した場合、行全体(およびテストケース)も失敗します。
以下は、.llファイルにおける有効なRUN行の例です。
; RUN: llvm-as < %s | llvm-dis > %t1
; RUN: llvm-dis < %s.bc-13 > %t2
; RUN: diff %t1 %t2
Unixシェルと同様に、RUN行ではパイプラインとI/Oリダイレクトを使用できます。
RUN行を作成する際には、注意すべき引用符の規則がいくつかあります。一般的に、何も引用符で囲む必要はありません。lit は引用符文字を取り除かないため、呼び出されたプログラムに渡されます。これを回避するには、中括弧を使用してlitに、囲まれたすべてのものを1つの値として扱うように指示します。
一般的に、RUN行はできる限りシンプルにするように心がけ、テキスト出力(その後で検査できるもの)を生成するツールを実行する場合のみに使用してください。テストがパスしたかどうかを判断するための出力の検査に推奨される方法は、FileCheckツールを使用することです。[RUN行でのgrepの使用は推奨されていません。grepを使用するパッチを送信またはコミットしないでください。]
テストごとに個別のファイルを作成するのではなく、関連するテストを1つのファイルにまとめます。既に機能を網羅しているファイルがないか確認し、新しいファイルを作成するのではなく、そこにコードを追加することを検討してください。
回帰テストにおけるアサーションの生成¶
一部の回帰テストケースは、手動で記述/更新するには非常に大規模で複雑です。その場合、人的作業を減らすために、llvm/utils/にあるスクリプトを使用できます。
たとえば、llcベースのテストでアサーションを生成するには、1つ以上のRUN行を追加した後、以下を実行します。
% llvm/utils/update_llc_test_checks.py --llc-binary build/bin/llc test.ll
これにより、FileCheckアサーションが生成され、アサーションが自動生成されたことを示すNOTE:行が先頭に挿入されます。
既存のテストケースのアサーションを更新する場合は、-uオプションを渡します。これにより、まずNOTE:行が存在し、スクリプト名と一致するかどうかが確認されます。
テストが手書きのアサーションに完全に依存しており、アサーションを自動生成すべきでない場合があります。その場合は、最初の行にNOTE: Do not autogenerateというテキストを追加すると、スクリプトはそのテストをスキップします。生成されたアサーションがテストで機能しない理由を説明しておくと、将来の開発者が状況を理解するのに役立ちます。
これらは最も一般的なスクリプトであり、アサーション生成における目的/用途を示しています。
update_analyze_test_checks.py
opt -passes='print<cost-model>'
update_cc_test_checks.py
C/C++, or clang/clang++ (IR checks)
update_llc_test_checks.py
llc (assembly checks)
update_mca_test_checks.py
llvm-mca
update_mir_test_checks.py
llc (MIR checks)
update_test_checks.py
opt
テストのプリコミットワークフロー¶
テストがクラッシュ、アサート、無限ループを起こさない場合、まずベースラインチェック行と共にテストをコミットします。つまり、テストはコンパイルエラーまたは最適化の欠如を示します。テストで何かが変更されることが予想される場合は、「TODO」または「FIXME」コメントを追加します。
コンパイラへのコード変更を含む後続のパッチでは、テストに対するチェック行の差分が表示されるため、パッチの影響を確認しやすくなります。問題が解決した場合は、前の手順で追加したTODO/FIXMEコメントを削除します。
コミット権限がある場合、ベースラインテスト(機能変更なしのパッチまたはNFCパッチ)はプリコミットレビューなしでmainブランチにプッシュできます。
回帰テストのベストプラクティス¶
可能な限り、自動生成されたチェック行(上記で説明したスクリプトで生成)を使用します。
特定のテストでテスト対象/期待事項に関するコメントを含めます。バグトラッカーに関連する問題がある場合は、それらのバグレポートへの参照を追加します(例:「詳細についてはPR999を参照してください」)。
必要がない限り、未定義の動作とポイズン/undef値を避けてください。たとえば、
br i1 undefのようなパターンは、将来の最適化の結果として壊れる可能性が高いため、使用しないでください。不要な命令、メタデータ、属性などを削除して、テストを最小限に抑えます。
llvm-reduceのようなツールは、この自動化に役立ちます。PhaseOrderingテスト以外では、最小限のパスのセットのみを実行します。たとえば、
opt -S -passes=instcombineをopt -S -O3よりも優先します。名前のない命令/ブロック(
%0や1:など)は避けてください。将来テストを変更した場合に番号を付け直す必要がある可能性があります。これらはopt -S -passes=instnamerを実行することで削除できます。値(変数、ブロック、関数を含む)には意味のある名前を付け、最適化パイプラインによって生成された複雑な名前(
%foo.0.0.0.0.0.0など)を保持することを避けてください。
追加ファイル¶
テストでRUN:行を含むファイル以外にも追加ファイルが必要で、そのファイルが小さい場合は、同じファイルに指定し、split-fileを使用してそれらを抽出することを検討してください。たとえば、
; RUN: split-file %s %t
; RUN: llvm-link -S %t/a.ll %t/b.ll | FileCheck %s
; CHECK: ...
;--- a.ll
...
;--- b.ll
...
各パートは、正規表現^(.|//)--- <part>によって区切られています。
[[#@LINE+1]]のように相対行番号をテストする場合は、--leading-linesを指定して先頭に空行を追加し、行番号を保持します。
追加ファイルが大きい場合は、慣例的にInputsサブディレクトリに配置します。その後、追加ファイルは%S/Inputs/foo.barとして参照できます。
たとえば、test/Linker/ident.llを考えてみましょう。ディレクトリ構造は次のとおりです。
test/
Linker/
ident.ll
Inputs/
ident.a.ll
ident.b.ll
便宜上、内容は次のとおりです。
;;;;; ident.ll:
; RUN: llvm-link %S/Inputs/ident.a.ll %S/Inputs/ident.b.ll -S | FileCheck %s
; Verify that multiple input llvm.ident metadata are linked together.
; CHECK-DAG: !llvm.ident = !{!0, !1, !2}
; CHECK-DAG: "Compiler V1"
; CHECK-DAG: "Compiler V2"
; CHECK-DAG: "Compiler V3"
;;;;; Inputs/ident.a.ll:
!llvm.ident = !{!0, !1}
!0 = metadata !{metadata !"Compiler V1"}
!1 = metadata !{metadata !"Compiler V2"}
;;;;; Inputs/ident.b.ll:
!llvm.ident = !{!0}
!0 = metadata !{metadata !"Compiler V3"}
対称性の理由から、ident.llは、RUN:行を保持すること以外、テストに実際には参加しないダミーファイルです。
注記
既存のテストの中には、Inputs/ディレクトリに単に追加ファイルを配置する代わりに、追加ファイル内でRUN: trueを使用しているものがあります。このパターンは推奨されていません。
詳細なテスト¶
一般的に、IRとアセンブリのテストファイルは、不要な詳細を削除してクリーンアップすることが推奨されます。ただし、クリーンアップがそれほど実用的ではない、詳細なIRまたはアセンブリファイルが必要なテスト(例:Clangからの大量のデバッグ情報出力)の場合、split-fileのgenと呼ばれるパート内に生成命令を含めることができます。次に、テストファイルでllvm/utils/update_test_body.pyを実行して、必要なコンテンツを生成します。
; RUN: rm -rf %t && split-file %s %t && cd %t
; RUN: opt -S a.ll ... | FileCheck %s
; CHECK: hello
;--- a.cc
int va;
;--- gen
clang --target=x86_64-linux -S -emit-llvm -g a.cc -o -
;--- a.ll
# content generated by the script 'gen'
PATH=/path/to/clang_build/bin:$PATH llvm/utils/update_test_body.py path/to/test.ll
スクリプトは、split-fileを使用して追加ファイルを準備し、genを呼び出し、その後genの後の部分をその標準出力で書き換えます。
便宜上、テストで単一のアセンブリファイルが必要な場合は、genとその必要なファイルを.ifdefと.endifで囲むこともできます。そうすれば、RUN行でsplit-fileをスキップできます。
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o a.o
# RUN: ... | FileCheck %s
# CHECK: hello
.ifdef GEN
#--- a.cc
int va;
#--- gen
clang --target=x86_64-linux -S -g a.cc -o -
.endif
# content generated by the script 'gen'
注記
別のマシンで再生成が必要になった場合の差異を避けるために、明示的なターゲットトリプルを指定することを検討してください。
gen は、PWD が /proc/self/cwd に設定されて呼び出されます。Clangコマンドは、デフォルト値がPWDであるため、-fdebug-compilation-dir= を必要としません。
.endif の前にプレフィックスを確認する必要があります。.endif の後の部分は置き換えられるためです。
テストボディに複数のファイルが含まれる場合、---区切り記号を出力し、RUN行でsplit-fileを使用できます。
# RUN: rm -rf %t && split-file %s %t && cd %t
...
#--- a.cc
int va;
#--- b.cc
int vb;
#--- gen
clang --target=x86_64-linux -S -O1 -g a.cc -o -
echo '#--- b.s'
clang --target=x86_64-linux -S -O1 -g b.cc -o -
#--- a.s
脆弱なテスト¶
テスト対象のツールが入力ファイルへのフルパスを出力する場合、誤って失敗する脆弱なテストを作成するのは容易です。たとえば、opt はデフォルトでModuleIDを出力します。
$ cat example.ll
define i32 @main() nounwind {
ret i32 0
}
$ opt -S /path/to/example.ll
; ModuleID = '/path/to/example.ll'
define i32 @main() nounwind {
ret i32 0
}
ModuleID は、CHECK行と予期せず一致することがあります。例:
; RUN: opt -S %s | FileCheck
define i32 @main() nounwind {
; CHECK-NOT: load
ret i32 0
}
このテストは、downloadディレクトリに配置されていると失敗します。
テストを堅牢にするには、常にRUN行でopt ... < %sを使用してください。opt は、入力が標準入力から来た場合、ModuleIDを出力しません。
プラットフォーム固有のテスト¶
生成されたコード、特定の出力、またはバックエンド機能に関連するなど、特定のプラットフォームに関する知識を必要とするテストを追加する場合は、異なるアーキテクチャで実行されるビルドボット(すべてのバックエンドをコンパイルしないボットも含む)が失敗しないように、機能を分離する必要があります。
最初の問題は、ターゲット固有の出力(構造体のサイズ、パス、アーキテクチャ名など)を確認することです。例:
Windowsパスを含むテストは、Linuxでは失敗し、その逆も同様です。
テキストのどこかに
x86_64を確認するテストは、他の場所では失敗します。デバッグ情報が型と構造体のサイズを計算するテスト。
また、テストが任意のバックエンドでコード化されている動作に依存する場合は、独自のディレクトリに配置する必要があります。たとえば、ARMのコードジェネレータテストはtest/CodeGen/ARMなどに配置されます。これらのディレクトリには、特定のバックエンドがコンパイルされて使用可能な場合にのみ、そのディレクトリ内のすべてのテストが実行されることを保証する特別なlit設定ファイルが含まれています。
たとえば、test/CodeGen/ARMでは、lit.local.cfgは次のようになります。
config.suffixes = ['.ll', '.c', '.cpp', '.test']
if not 'ARM' in config.root.targets:
config.unsupported = True
その他のプラットフォーム固有のテストとしては、特定のサブアーキテクチャの特定の機能に依存するものがあります(たとえば、AVX2をサポートするIntelチップのみ)。
たとえば、test/CodeGen/X86/psubus.llは、3つのサブアーキテクチャのバリアントをテストします。
; RUN: llc -mcpu=core2 < %s | FileCheck %s -check-prefix=SSE2
; RUN: llc -mcpu=corei7-avx < %s | FileCheck %s -check-prefix=AVX1
; RUN: llc -mcpu=core-avx2 < %s | FileCheck %s -check-prefix=AVX2
そして、チェックは異なります。
; SSE2: @test1
; SSE2: psubusw LCPI0_0(%rip), %xmm0
; AVX1: @test1
; AVX1: vpsubusw LCPI0_0(%rip), %xmm0, %xmm0
; AVX2: @test1
; AVX2: vpsubusw LCPI0_0(%rip), %xmm0, %xmm0
したがって、プラットフォーム固有であるか、またはサブアーキテクチャの特別な機能に依存する動作をテストする場合は、特定のトリプルを追加し、特定のFileCheckを使用してテストし、他のすべてのアーキテクチャを除外する特定のディレクトリに配置する必要があります。
テスト実行の制限¶
デバッグビルド時や特定のプラットフォームでのみ実行できるテストがあります。REQUIRESとUNSUPPORTEDを使用して、テストが有効になるタイミングを制御します。
一部のテストは失敗することが想定されています。たとえば、テストで検出される既知のバグがある場合があります。XFAILを使用して、テストを予期される失敗としてマークします。XFAILテストは、実行が失敗した場合に成功し、実行が成功した場合に失敗します。
; This test will be only enabled in the build with asserts.
; REQUIRES: asserts
; This test is disabled when running on Linux.
; UNSUPPORTED: system-linux
; This test is expected to fail when targeting PowerPC.
; XFAIL: target=powerpc{{.*}}
REQUIRES、UNSUPPORTED、XFAILはすべて、コンマ区切りのブール式のリストを受け入れます。各式内の値は次のとおりです。
lit.cfgなどの設定ファイルによってconfig.available_featuresに追加された機能。機能の文字列比較は大文字と小文字が区別されます。さらに、ブール式には{{ }}で囲まれた任意のPython正規表現を含めることができ、その場合、ブール式は、任意の機能が正規表現と一致する場合に満たされます。正規表現は識別子の内部に表示されるため、たとえばhe{{l+}}oはhelo、hello、hellloなどに一致します。target=という文字列で始まるデフォルトのターゲットトリプル(例:target=x86_64-pc-windows-msvc)。通常、正規表現を使用してトリプルの部分を照合します(例:target={{.*}}-windows{{.*}}は任意のWindowsターゲットトリプルに一致します)。
REQUIRESは、すべての式がtrueの場合にテストを有効にします。UNSUPPORTEDは、いずれかの式がtrueの場合にテストを無効にします。XFAILは、いずれかの式がtrueの場合にテストが失敗することを期待します。テストがどこでも失敗することが予想される場合は、XFAIL: *を使用します。同様に、UNSUPPORTED: target={{.*}}を使用して、テストをどこでも無効にします。
; This test is disabled when running on Windows,
; and is disabled when targeting Linux, except for Android Linux.
; UNSUPPORTED: system-windows, target={{.*linux.*}} && !target={{.*android.*}}
; This test is expected to fail when targeting PowerPC or running on Darwin.
; XFAIL: target=powerpc{{.*}}, system-darwin
制約の記述に関するヒント¶
``REQUIRES``と``UNSUPPORTED``
これらは論理的な逆です。原則として、UNSUPPORTEDは絶対に必要ではありません(論理否定をREQUIRESで使用してまったく同じ効果を得ることができます)。しかし、これにより、これらの句を読みやすく理解しやすくなります。一般的に、人々はREQUIRESを使用して、テストが正しく動作するために依存するものを記述し、UNSUPPORTEDを使用して、テストが動作しないことが予想されるケースを除外します。
``UNSUPPORTED``と``XFAIL``
これら両方は、テストが動作しないことが予想されることを示しています。ただし、効果は異なります。UNSUPPORTEDにより、テストがスキップされます。これにより実行時間が節約されますが、テストが実際に動作し始めるかどうかを知ることはできません。逆に、XFAILは実際にテストを実行しますが、失敗出力を期待します。実行時間が追加でかかりますが、テストが正しく動作し始めた場合に警告します(XPASSテスト結果)。どちらが各ケースでより適切かを判断する必要があります。
``target=…``の使用
ターゲットトリプルの確認はトリッキーです。誤って指定するのは簡単です。たとえば、target=mips{{.*}}は、mipsだけでなく、mipsel、mips64、mips64elにも一致します。target={{.*}}-linux-gnuはx86_64-unknown-linux-gnuに一致しますが、armv8l-unknown-linux-gnueabihfには一致しますません。トリプルのコンポーネントを区切るためにハイフンを使用することをお勧めします(target=mips-{{.*}})し、予期しないサフィックスを許可するために末尾のワイルドカードを使用するのが一般的です。
また、短いものにするために巧妙なことをするのではなく、トリプルのコンポーネント全体を使用する正規表現を作成する方が一般的です。たとえば、式でfreebsdとnetbsdの両方に一致させるには、target={{.*(free|net)bsd.*}}と記述できます。ただし、これにより、grep freebsdがこのテストを見つけることが妨げられます。次の方法を使用する方が優れています。target={{.+-freebsd.*}} || target={{.+-netbsd.*}}
置換¶
LLVMツールの名前の置き換えに加えて、RUN行では次の置換が実行されます。
%%単一の
%で置き換えられます。これにより、他の置換をエスケープできます。%sテストケースのソースへのファイルパス。これは、コマンドラインでLLVMツールへの入力として渡すのに適しています。
例:
/home/user/llvm/test/MC/ELF/foo_test.s%Sテストケースのソースへのディレクトリパス。
例:
/home/user/llvm/test/MC/ELF%tこのテストケースに使用できる一時ファイル名のファイルパス。ファイル名は他のテストケースと競合しません。複数のテンポラリが必要な場合は、それに追加できます。これは、リダイレクトされた出力の宛先として役立ちます。
例:
/home/user/llvm.build/test/MC/ELF/Output/foo_test.s.tmp%T%tのディレクトリ。非推奨。誤用されやすく、テスト間で競合状態を引き起こす可能性があるため、使用しないでください。一時ディレクトリが必要な場合は、代わりに
rm -rf %t && mkdir %tを使用してください。例:
/home/user/llvm.build/test/MC/ELF/Output
%{pathsep}
パスセパレータに展開されます。つまり、
:(またはWindowsでは;)。
%{fs-src-root}ソースディレクトリのファイルシステムパスのルートコンポーネントに展開されます。つまり、Unixシステムでは
/、WindowsではC:\(または別のドライブ)。%{fs-tmp-root}テストの一時ディレクトリのファイルシステムパスのルートコンポーネントに展開されます。つまり、Unixシステムでは
/、WindowsではC:\(または別のドライブ)。%{fs-sep}ファイルシステムセパレータに展開されます。つまり、
/またはWindowsでは\。
%/s, %/S, %/t, %/T
上記の対応する置換のように動作しますが、
\文字を/に置き換えます。これは、パスセパレータを正規化するために役立ちます。例:
%s: C:\Desktop Files/foo_test.s.tmp例:
%/s: C:/Desktop Files/foo_test.s.tmp
%{s:real}, %{S:real}, %{t:real}, %{T:real} %{/s:real}, %{/S:real}, %{/t:real}, %{/T:real}
対応する置換と同様に動作しますが、
/も含め、シンボリックリンクとドライブをすべて展開した実パスを使用します。例:
%s: S:\foo_test.s.tmp例:
%{/s:real}: C:/SDrive/foo_test.s.tmp
%:s, %:S, %:t, %:T
上記の対応する置換と同様に動作しますが、Windowsパスの先頭のコロンを削除します。これは、Windowsで絶対パスを連結して有効なパスを作成するために役立ちます。
例:
%s: C:\Desktop Files\foo_test.s.tmp例:
%:s: C\Desktop Files\foo_test.s.tmp
%errc_<ERRCODE>
ホストプラットフォームに基づいて異なるスペルを許可するために、一部のエラーメッセージが置換される場合があります。
現在サポートされているエラーコードは、ENOENT、EISDIR、EINVAL、EACCESです。
例:
Linux %errc_ENOENT: No such file or directory例:
Windows %errc_ENOENT: no such file or directory
%if feature %{<if branch>%} %else %{<else branch>%}
条件付き置換:
featureが利用可能な場合、<if branch>に展開され、利用できない場合は<else branch>に展開されます。%else %{<else branch>%}はオプションであり、存在しない場合は%else %{%}として扱われます。
%(line), %(line+<number>), %(line-<number>)
この置換が使用されている行数に、オプションの整数オフセットを付けたもの。これらは、
RUN:、DEFINE:、およびREDEFINE:ディレクティブに直接表示されている場合のみ展開されます。他の場所で定義された置換内での出現は、決して展開されません。たとえば、テストファイルの行番号を参照する複数のRUN行を持つテストで使用できます。
LLVM固有の置換
%shlibextホストプラットフォームの共有ライブラリファイルのサフィックス。先頭にピリオドが含まれます。
例:
.so(Linux)、.dylib(macOS)、.dll(Windows)%exeextホストプラットフォームの実行可能ファイルのサフィックス。先頭にピリオドが含まれます。
例:
.exe(Windows)、Linuxでは空。
Clang固有の置換
%clangClangドライバを呼び出します。
%clang_cppC++用のClangドライバを呼び出します。
%clang_clCL互換のClangドライバを呼び出します。
%clangxxG++互換のClangドライバを呼び出します。
%clang_cc1Clangフロントエンドを呼び出します。
%itanium_abi_triple,%ms_abi_tripleこれらの置換を使用して、目的のABIに調整された現在のターゲットトリプルを取得できます。たとえば、テストスイートが
i686-pc-win32ターゲットで実行されている場合、%itanium_abi_tripleはi686-pc-mingw32に展開されます。これにより、テストを特定のABIで実行できますが、特定のトリプルに制約されることはありません。
FileCheck固有の置換
%ProtectFileCheckOutput呼び出しのテキスト出力でテスト結果に影響がある場合にのみ、
FileCheck呼び出しの前に配置する必要があります。通常、これは簡単に判断できます。FileCheck呼び出しのstdoutまたはstderrのリダイレクトまたはパイプ処理を探してください。
テスト固有の置換
追加の置換は次のように定義できます。
Lit設定ファイル(例:
lit.cfgまたはlit.local.cfg)は、テストディレクトリのすべてのテストの置換を定義できます。これは、置換リストconfig.substitutionsを拡張することで行います。リストの各アイテムは、パターンとその置換で構成されるタプルであり、litはプレーンテキストとして適用します(pythonのre.subがエスケープシーケンスと見なすシーケンスが含まれている場合でも)。単一のテストファイル内に置換を定義するために、litは以下で詳しく説明する
DEFINE:およびREDEFINE:ディレクティブをサポートしています。これらが他のテストファイルに影響を与えないように、これらのディレクティブは、lit設定ファイルによって生成される置換リストのコピーを変更します。
たとえば、次のディレクティブをテストファイルに挿入して、空の初期値を持つ%{cflags}および%{fcflags}置換を定義し、新しく定義された%{check}置換のパラメータとして機能させることができます。
; DEFINE: %{cflags} =
; DEFINE: %{fcflags} =
; DEFINE: %{check} = \
; DEFINE: %clang_cc1 -verify -fopenmp -fopenmp-version=51 %{cflags} \
; DEFINE: -emit-llvm -o - %s | \
; DEFINE: FileCheck %{fcflags} %s
あるいは、上記のようにlit設定ファイルで置換を定義して、他のテストファイルと共有することもできます。いずれの場合も、テストファイルはRUN:行で%{check}を使用する前に、次のディレクティブを使用してパラメータ置換を必要に応じて再定義できます。
; REDEFINE: %{cflags} = -triple x86_64-apple-darwin10.6.0 -fopenmp-simd
; REDEFINE: %{fcflags} = -check-prefix=SIMD
; RUN: %{check}
; REDEFINE: %{cflags} = -triple x86_64-unknown-linux-gnu -fopenmp-simd
; REDEFINE: %{fcflags} = -check-prefix=SIMD
; RUN: %{check}
; REDEFINE: %{cflags} = -triple x86_64-apple-darwin10.6.0
; REDEFINE: %{fcflags} = -check-prefix=NO-SIMD
; RUN: %{check}
; REDEFINE: %{cflags} = -triple x86_64-unknown-linux-gnu
; REDEFINE: %{fcflags} = -check-prefix=NO-SIMD
; RUN: %{check}
初期値を提供することに加えて、上記の例のパラメータ置換の最初のDEFINE:ディレクティブは、2番目の目的を果たします。%{check}とそのパラメータが期待通りに展開されるように、置換順序を確立します。テストファイルに必要な定義順序を覚える簡単な方法があります。参照する可能性のある置換の前に、置換を定義します。
一般的に、置換展開は次のように動作します。
各
RUN:行に到達すると、litはそのRUN:行内のすべての置換を、置換リストの現在の値を使用して展開します。%(line)、%(line+<number>)、および%(line-<number>)を除き、DEFINE:およびREDEFINE:ディレクティブでは、置換展開は行われません。RUN:行の置換を展開する場合、litはデフォルトで置換リストを1回だけ通過します。この場合、置換は、その値に表示される置換よりも前に置換リストに挿入されている必要があります。後者が展開されるようにするためです。(より柔軟性を高めるために、lit設定ファイルでrecursiveExpansionLimitを設定することで、置換リストへの複数回の通過を有効にできます。)lit設定ファイルはどこにでも挿入できますが、
DEFINE:およびREDEFINE:ディレクティブの挿入動作は以下で指定されており、上記の例で示したユースケースのために特別に設計されています。直接的または他の置換を介して、それ自体に関して置換を定義することは避ける必要があります。通常、完全に展開できない無限再帰的定義が生成されます。
REDEFINE:を使用する場合でも、以前の値に関して置換を定義するわけではありません。
DEFINE:およびREDEFINE:ディレクティブ間の関係は、多くのプログラミング言語における変数宣言と変数代入の関係に似ています。
DEFINE: %{name} = valueこのディレクティブは、パターンが
%{name}である新しい置換に指定された値を割り当てます。または、%{name}を含むパターンを持つ置換が既に存在する場合はエラーを報告します。これは、混乱を招く展開(例:lit設定ファイルがパターン%{name}\[0\]を持つ置換を定義する可能性がある)を引き起こす可能性があるためです。新しい置換は置換リストの先頭に挿入されるため、最初に展開されます。したがって、その値には、同じテストファイル内またはlit設定ファイル内のいずれかで以前に定義された置換を含めることができ、両方が展開されます。REDEFINE: %{name} = valueこのディレクティブは、パターンが
%{name}である既存の置換に指定された値を割り当てます。または、そのパターンを持つ置換が存在しない場合、または%{name}を含むパターンを持つ置換が複数存在する場合はエラーを報告します。置換の置換リスト内の現在の位置は変更されないため、他の既存の置換に対する展開順序は保持されます。
DEFINE:およびREDEFINE:ディレクティブの両方に次のプロパティが適用されます。
置換名:ディレクティブでは、
%{name}の直前または直後の空白はオプションで、破棄されます。%{name}は%{で始まる必要があり、}で終わる必要があり、残りの部分は文字またはアンダースコアで始まり、英数字、ハイフン、アンダースコア、コロンのみを含める必要があります。この構文にはいくつかの利点があります。pythonの
re.subパターンで特別なシーケンスが%{name}に含まれることはありません。それ以外の場合、lit設定ファイルで%{name}を置換パターンとして指定しようとすると、混乱を招く展開が発生する可能性があります。ブレースを使用すると、別の置換のパターンが
%{name}の一部と一致したり、その逆になったりする可能性が回避され、混乱を招く展開を防ぎます。ただし、lit設定ファイルとlit自体によって定義された置換のパターンはこの形式に制限されないため、オーバーラップは理論的には依然として可能です。
置換値: 値には、`
=`の後の最初の空白文字以外から最後の空白文字以外までのすべてのテキストが含まれます。`=`の後に空白文字以外の文字がない場合、値は空文字列になります。Pythonの`re.sub`置換文字列に現れる可能性のあるエスケープシーケンスは、値ではプレーンテキストとして扱われます。行継続: `
:`の後の行の最後の空白文字以外の文字が`\`の場合、次のディレクティブは同じディレクティブキーワード(例:`DEFINE:`)を使用する必要があり、追加のディレクティブがない場合はエラーになります。そのディレクティブは継続として機能します。つまり、いずれかのディレクティブの`:`の後のテキストを解析するための上記のルールに従う前に、litはそのテキストを連結して単一のディレクティブを形成し、`\`を単一のスペースに置き換え、そのスペースに隣接するその他の空白文字を削除します。継続は同じ方法で継続できます。`:`の後に空白文字しかない継続はエラーです。
recursiveExpansionLimit
前のセクションで説明したように、`RUN:`行で置換を展開する場合、litはデフォルトで置換リストを1回だけ通過します。したがって、置換が適切な順序で定義されていない場合、一部は`RUN:`行で展開されないままになります。たとえば、次のディレクティブは`%{outer}`内の`%{inner}`を参照していますが、`%{outer}`の後まで`%{inner}`を定義しません。
; By default, this definition order does not enable full expansion.
; DEFINE: %{outer} = %{inner}
; DEFINE: %{inner} = expanded
; RUN: echo '%{outer}'
DEFINE:`は置換を置換リストの先頭に挿入するため、`%{inner}`は最初に展開されますが、元の`RUN:`行に`%{inner}`が含まれていないため、効果はありません。次に、`%{outer}`が展開され、`echo`コマンドの出力は次のようになります。
%{inner}
もちろん、この単純なケースを修正する1つの方法は、`%{outer}`と`%{inner}`の定義を逆にすることです。ただし、テストに相互参照できる複雑な置換セットがある場合、十分な置換順序が存在しない可能性があります。
このようなユースケースに対処するために、lit設定ファイルは`config.recursiveExpansionLimit`をサポートしています。これは、非負の整数に設定して、置換リストを通過する最大回数を指定できます。したがって、上記の例では、制限を2に設定すると、litは2回目のパスを実行して`RUN:`行の`%{inner}`を展開し、`echo`コマンドからの出力は次のようになります。
expanded
パフォーマンスを向上させるために、litは`RUN:`行が変更されなくなったことに気付くと、パスの作成を停止します。したがって、上記の例では、制限を2より高く設定しても無害です。
デバッグを容易にするために、制限に達した後、litはさらに1回パスを行い、`RUN:`行が再び変更された場合にエラーを報告します。したがって、上記の例では、制限を1に設定すると、litは正しくない出力を生成する代わりにエラーを報告します。
オプション¶
LLVM litの設定では、ユーザーオプションを使用していくつかの項目をカスタマイズできます。
llc`, `opt`, …それぞれのLLVMツールの名前をカスタムコマンドラインで置き換えます。これにより、これらのツールのカスタムパスとデフォルトの引数を指定できます。例
% llvm-lit “-Dllc=llc -verify-machineinstrs”
run_long_tests長時間実行されるテストの実行を有効にします。
llvm_site_configデフォルトの設定ファイルではなく、指定されたlit設定ファイルをロードします。
その他の機能¶
RUN行の記述を容易にするために、いくつかのヘルパープログラムがあります。これらのヘルパーはテスト実行時にPATHに含まれているため、名前を使用して呼び出すことができます。例
notこのプログラムは、その引数を実行し、それから結果コードを反転させます。ゼロの結果コードは1になります。ゼロ以外の結果コードは0になります。
出力をより役立つものにするために、litはテストケースの行をスキャンして、`PR[0-9]+`と一致するパターンを含む行を探します。これは、テストケースに関連するPR(Problem Report)番号を指定するための構文です。「PR」の後の数値は、LLVM Bugzillaの番号を指定します。PR番号が指定されている場合、合格/不合格のレポートで使用されます。これは、テストが失敗したときにコンテキストをすばやく取得するのに役立ちます。
最後に、「END.」を含む行は、行の特別な解釈を終了させます。これは一般的に最後のRUN:行の直後に行われます。これには2つの副作用があります。
テストプログラムの一部である行、つまりテストケースへの指示ではない行の特別な解釈を防ぎます。
ファイルの残りの部分を解釈することを回避することで、非常に大きなテストケースの速度を向上させます。
