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
