LLVMのオリジナルのバックエンドを実装してきたが、いろんなソースコードをコンパイルして、いろんなテストプログラムを動かしてきた。
これだけ多くのテストパタンを実行するのなら、テスト環境とリグレッションテストを用意しておくのが良い。LLVMは、テストパタンとリグレッションテストの環境が用意されている。
ここでは、これまで作成したオリジナルバックエンドをテストするための環境について調査する。
llvm-lit
llvm-litは"LLVM Integrated Tester"で、LLVMとClangのテストスイートを実行するためのツールだ。テストセットを記述してLLVMに食わせ、その結果が想定したものかどうかまでチェックしてくれる。
llvm-lit
は以下のようにして使用する。
$ ./bin/llvm-lit -h usage: llvm-lit [-h] [--version] [-j N] [--config-prefix NAME] [-D NAME=VAL] [-q] [-s] [-v] [-vv] [-a] [-o PATH] [--no-progress-bar] [--show-unsupported] [--show-xfail] [--path PATH] [--vg] [--vg-leak] [--vg-arg ARG] [--time-tests] [--no-execute] [--xunit-xml-output XUNIT_OUTPUT_FILE] [--timeout MAXINDIVIDUALTESTTIME] [--max-failures MAXFAILURES] [--max-tests N] [--max-time N] [--shuffle] [-i] [--filter REGEX] [--num-shards M] [--run-shard N] [--debug] [--show-suites] [--show-tests] [--single-process] [test_paths [test_paths ...]]
では、コードジェネレータ(LLVMバックエンド)のテストを見てみる。LLVMのリポジトリ内に、test/CodeGen
内にテストが入っている。
tree -L 1 test/CodeGen
test/CodeGen ├── AArch64 ├── AMDGPU ├── ARC ├── ARM ├── AVR ├── BPF ├── Generic ├── Hexagon ├── Inputs ├── Lanai ├── MIR ├── MSP430 ├── Mips ├── NVPTX ├── Nios2 ├── PowerPC ├── RISCV ├── SPARC ├── SystemZ ├── Thumb ├── Thumb2 ├── WebAssembly ├── WinCFGuard ├── WinEH ├── X86 └── XCore 26 directories, 0 files
ターゲットアーキテクチャ毎に、テストパタンが並んでいる。さらに、RISCVの中身をのぞいてみる。
tree -L 1 test/CodeGen/RISCV
test/CodeGen/RISCV ├── addc-adde-sube-subc.ll ├── align.ll ├── alloca.ll ├── alu32.ll ├── analyze-branch.ll ├── arith-with-overflow.ll ... ├── tail-calls.ll ├── vararg.ll ├── wide-mem.ll └── zext-with-load-is-free.ll 0 directories, 76 files
Clangで処理された中間形式LLVM IRのファイル群が格納されている。
llvm-litでリグレッションテスト流す
リグレッションテストを体験するために、まずは一度RISC-Vターゲットでリグレッションテストを流してみる。llvm-lit
コマンドでディレクトリを指定すれば、自動的にすべてのテストを流してくれる。
./bin/llvm-lit -v ../llvm-myriscvx80/test/CodeGen/RISCV
-- Testing: 94 tests, 8 threads -- PASS: LLVM :: CodeGen/RISCV/addc-adde-sube-subc.ll (1 of 94) PASS: LLVM :: CodeGen/RISCV/alloca.ll (2 of 94) PASS: LLVM :: CodeGen/RISCV/analyze-branch.ll (3 of 94) PASS: LLVM :: CodeGen/RISCV/align.ll (4 of 94) ... PASS: LLVM :: CodeGen/RISCV/sext-zext-trunc.ll (91 of 94) PASS: LLVM :: CodeGen/RISCV/rv64m-exhaustive-w-insts.ll (92 of 94) PASS: LLVM :: CodeGen/RISCV/rv64i-exhaustive-w-insts.ll (93 of 94) PASS: LLVM :: CodeGen/RISCV/atomic-rmw.ll (94 of 94) Testing Time: 11.82s Expected Passes : 94
流し終わった。所要時間はわずか10数秒で、94個のテストを流してすべてPassだ。RISC-Vのバックエンドはまだ開発段階なのでわずかに94本しかテストはないが、X86を見てみると何と3368本ものテストが用意されている。
ls -1 ../llvm-myriscvx80/test/CodeGen/X86 2003-08-03-CallArgLiveRanges.ll 2003-08-23-DeadBlockTest.ll 2003-11-03-GlobalBool.ll ... zext-sext.ll zext-shl.ll zext-trunc.ll zlib-longest-match.ll
コード生成テストの構成を確認してみる
では、テストの内容を確認する。test/CodeGen/RISCV/alu32.ll
を見てみる。
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \ ; RUN: | FileCheck %s -check-prefix=RV32I ; These tests are each targeted at a particular RISC-V ALU instruction. Other ; files in this folder exercise LLVM IR instructions that don't directly match a ; RISC-V instruction ; Register-immediate instructions define i32 @addi(i32 %a) nounwind { ; RV32I-LABEL: addi: ; RV32I: # %bb.0: ; RV32I-NEXT: addi a0, a0, 1 ; RV32I-NEXT: ret %1 = add i32 %a, 1 ret i32 %1 }
RUN:
の行に、テストの内容が書かれている。このテストでは、llc -mtriple=riscv32 -verify-machineinstrs < [テストファイル名] | FileCheck [テストファイル名] -check-prefix=RV32I
が実行される、ということになる。
llc
で生成したターゲットアセンブリコードを、FileCheck
コマンドで結果を比較する、ということになる。実行してみる。
$ ./bin/llvm-lit ../llvm-myriscvx80test/CodeGen/RISCV/alu32.ll -- Testing: 1 tests, 1 threads -- PASS: LLVM :: CodeGen/RISCV/alu32.ll (1 of 1) Testing Time: 0.87s Expected Passes : 1
LLVM IRのコード:
define i32 @addi(i32 %a) nounwind { %1 = add i32 %a, 1 ret i32 %1 }
に対して、以下のコードが生成されることを想定している。これが一致すると、テストがPassとなる。
addi: # %bb.0: addi a0, a0, 1 ret