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 的注解, 这些注解包括:

  1. 文件需要如何处理, 例如只编译, 或者编译完还需要运行, 或者需要 dump 出中间结果例如 gimple, rtl 或 asm
  2. 检查结果, 例如编译和运行的结果, 或者 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

Author: [email protected]
Date: 2022-11-24 Thu 10:07
Last updated: 2023-09-11 Mon 17:37

知识共享许可协议