FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

オリジナルLLVMバックエンド実装をまとめる(29. llvm-litによるテストの調査)

f:id:msyksphinz:20191007020939p:plain:w200

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