GCC Test
Table of Contents
1. GCC Test
1.1. Overview
GCC test 使用的是 DejaGnu, 后者是一个基于 `tcl/expect` 的测试框架.
以 riscv-gnu-toolchain 为例, 执行 test 的方法为:
$> RUNTESTFLAGS="riscv.exp" make report SIM=spike
其中 RUNTESTFLAGS 可以用来指定希望测试的具体的 testcase
gcc test 的基本做法是:
testcase 是正常的 c 代码, 但包含了一些 dejagnu 的注解, 这些注解包括:
- 文件需要如何处理, 例如只编译, 或者编译完还需要运行, 或者需要 dump 出中间结果例如 gimple, rtl 或 asm
- 检查结果, 例如编译和运行的结果, 或者 gimple, rtl, asm 是否包含特定的字符串等
例如:
// 编译时需要指定 -mbranch-cost=10 参数 /* { dg-options "-mbranch-cost=10 (HAS_MOVN)" } */ /* { dg-skip-if "code quality test" { *-*-* } { "-O0" } { "" } } */ NOMIPS16 int foo (int x, int y, int z, int k) { return x == k ? x + y : z - x; } // 编译完成后需要保证生成的 as 中包含 movz 或 movn /* { dg-final { scan-assembler "\t(movz|movn)\t" } } */ // 编译完成后需要保证生成的 as 中不包含 bnz 或 beq /* { dg-final { scan-assembler-not "\t(bne|beq)\t" } } */
上面的这个 testcase 要测试的内容是: mips 的 backend 在 branch cost 较大时是否能使用 movz/movn 指令来代替 bne/beq 指令.
与之对应的是另一个 testcase:
/* { dg-options "-mbranch-cost=1 (HAS_MOVN)" } */ /* { dg-skip-if "code quality test" { *-*-* } { "-O0" } { "" } } */ NOMIPS16 int foo (int x, int y, int z, int k) { return x == k ? x + y : z - x; } /* { dg-final { scan-assembler-not "\t(movz|movn)\t" } } */ /* { dg-final { scan-assembler "\t(bnec?|beqc?)\t" } } */
所以 gcc test 主要的测试手段是检测生成的中间结果中是否包含某种关键字, 而不是全部匹配, 因为全部匹配非常容易测试失败.
当测试程序需要运行时 (通过 dg-do run), dejagnu 需要知道如何执行测试程序, 这时需要指定额外的 target_board 参数, 例如 riscv-gnu-toolchain 的 `make report` 实际相当于:
$> make -C build-gcc-newlib-stage2 check-gcc \ "RUNTESTFLAGS=-target_board='riscv-sim/-march=rv64gc/-mabi=lp64d/-mcmodel=medlow'"
target_board 放在 /dejagnu/baseboards 目录下, 包含如何执行测试程序的信息 (例如通过 simulator)
scan-assembler, scan-tree, scan-rtl 都是 gcc 对 dejagnu 的扩展, 定义在 gcc/gcc/testsuite/lib 中.
1.2. scan tree
scan-tree-xxx 通过检测 gimple 的内容是否包含某种关键字来确定某个 gimple 优化是否正确, 例如:
/* { dg-do compile } */ /* { dg-options "-O2 -fdump-tree-optimized" } */ static inline int max (int a, int b) { return a < b ? b : a; } int test_00 (int a) { return max (a, a + 8); } int test_01 (int a) { return max (a, a - 8); } /* { dg-final { scan-tree-dump-not "MAX_EXPR" "optimized" } } */
这个 testcase 是指 `-O2` 时 `max(a,a+8)` 应该直接优化为 `a+8`, 而不是在 gimple 中生成一个 `MAX_EXPR`
1.3. scan rtl
scan-rtl-xxx 通过检测 rtl 的内容是否包含某种关键字来确定某个 rtl 优化是否正确, 例如:
/* { dg-do compile { target { { aarch64*-*-* i?86-*-* x86_64-*-* } && lp64 } } } */ /* { dg-options "-fdump-rtl-ce1 -O2 --param max-rtl-if-conversion-unpredictable-cost=100" } */ typedef long long s64; int foo (s64 a, s64 b, s64 c) { s64 d = a - b; if (d == 0) return a + c; else return b + c + d; } /* This test can be reduced to just return a + c; */ /* { dg-final { scan-rtl-dump "3 true changes made" "ce1" } } */ /* { dg-final { scan-assembler-not "sub\.*\tx\[0-9\]+, x\[0-9\]+, x\[0-9\]+\.*" { target { aarch64*-*-* } } } } */
这个 testcase 是为了测试 pass_rtl_ifcvt, 确定该函数的 `if` 能被优化掉, 变成 `return a+c`
1.4. scan assembler
scan-assembler-xxx 通过检测 as 的内容确定 backend 是否使用了某些汇编指令, 例如前面提到的 `movz`
1.5. 其它
除了使用 `scan-xx` 的 `dg-final`, dejagnu 还提供了一些其它的测试原语, 例如:
- dg-timeout
- dg-error
- dg-warning
- dg-do
- dg-option
- dg-message
直接调用 dejagnu 的方法:
<path_to_dejagnu>/runtest --srcdir=<path_to_gcc_src>/gcc/ --target riscv64-unknown-linux --tool gcc \ --tool_exec <path_to_gcc_build>/bin/riscv64-unknown-linux-gnu-gcc riscv.exp --debug