You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

149 lines
5.2 KiB

1 month ago
  1. # Assembly Tests
  2. The Benchmark library provides a number of functions whose primary
  3. purpose in to affect assembly generation, including `DoNotOptimize`
  4. and `ClobberMemory`. In addition there are other functions,
  5. such as `KeepRunning`, for which generating good assembly is paramount.
  6. For these functions it's important to have tests that verify the
  7. correctness and quality of the implementation. This requires testing
  8. the code generated by the compiler.
  9. This document describes how the Benchmark library tests compiler output,
  10. as well as how to properly write new tests.
  11. ## Anatomy of a Test
  12. Writing a test has two steps:
  13. * Write the code you want to generate assembly for.
  14. * Add `// CHECK` lines to match against the verified assembly.
  15. Example:
  16. ```c++
  17. // CHECK-LABEL: test_add:
  18. extern "C" int test_add() {
  19. extern int ExternInt;
  20. return ExternInt + 1;
  21. // CHECK: movl ExternInt(%rip), %eax
  22. // CHECK: addl %eax
  23. // CHECK: ret
  24. }
  25. ```
  26. #### LLVM Filecheck
  27. [LLVM's Filecheck](https://llvm.org/docs/CommandGuide/FileCheck.html)
  28. is used to test the generated assembly against the `// CHECK` lines
  29. specified in the tests source file. Please see the documentation
  30. linked above for information on how to write `CHECK` directives.
  31. #### Tips and Tricks:
  32. * Tests should match the minimal amount of output required to establish
  33. correctness. `CHECK` directives don't have to match on the exact next line
  34. after the previous match, so tests should omit checks for unimportant
  35. bits of assembly. ([`CHECK-NEXT`](https://llvm.org/docs/CommandGuide/FileCheck.html#the-check-next-directive)
  36. can be used to ensure a match occurs exactly after the previous match).
  37. * The tests are compiled with `-O3 -g0`. So we're only testing the
  38. optimized output.
  39. * The assembly output is further cleaned up using `tools/strip_asm.py`.
  40. This removes comments, assembler directives, and unused labels before
  41. the test is run.
  42. * The generated and stripped assembly file for a test is output under
  43. `<build-directory>/test/<test-name>.s`
  44. * Filecheck supports using [`CHECK` prefixes](https://llvm.org/docs/CommandGuide/FileCheck.html#cmdoption-check-prefixes)
  45. to specify lines that should only match in certain situations.
  46. The Benchmark tests use `CHECK-CLANG` and `CHECK-GNU` for lines that
  47. are only expected to match Clang or GCC's output respectively. Normal
  48. `CHECK` lines match against all compilers. (Note: `CHECK-NOT` and
  49. `CHECK-LABEL` are NOT prefixes. They are versions of non-prefixed
  50. `CHECK` lines)
  51. * Use `extern "C"` to disable name mangling for specific functions. This
  52. makes them easier to name in the `CHECK` lines.
  53. ## Problems Writing Portable Tests
  54. Writing tests which check the code generated by a compiler are
  55. inherently non-portable. Different compilers and even different compiler
  56. versions may generate entirely different code. The Benchmark tests
  57. must tolerate this.
  58. LLVM Filecheck provides a number of mechanisms to help write
  59. "more portable" tests; including [matching using regular expressions](https://llvm.org/docs/CommandGuide/FileCheck.html#filecheck-pattern-matching-syntax),
  60. allowing the creation of [named variables](https://llvm.org/docs/CommandGuide/FileCheck.html#filecheck-variables)
  61. for later matching, and [checking non-sequential matches](https://llvm.org/docs/CommandGuide/FileCheck.html#the-check-dag-directive).
  62. #### Capturing Variables
  63. For example, say GCC stores a variable in a register but Clang stores
  64. it in memory. To write a test that tolerates both cases we "capture"
  65. the destination of the store, and then use the captured expression
  66. to write the remainder of the test.
  67. ```c++
  68. // CHECK-LABEL: test_div_no_op_into_shr:
  69. extern "C" void test_div_no_op_into_shr(int value) {
  70. int divisor = 2;
  71. benchmark::DoNotOptimize(divisor); // hide the value from the optimizer
  72. return value / divisor;
  73. // CHECK: movl $2, [[DEST:.*]]
  74. // CHECK: idivl [[DEST]]
  75. // CHECK: ret
  76. }
  77. ```
  78. #### Using Regular Expressions to Match Differing Output
  79. Often tests require testing assembly lines which may subtly differ
  80. between compilers or compiler versions. A common example of this
  81. is matching stack frame addresses. In this case regular expressions
  82. can be used to match the differing bits of output. For example:
  83. <!-- {% raw %} -->
  84. ```c++
  85. int ExternInt;
  86. struct Point { int x, y, z; };
  87. // CHECK-LABEL: test_store_point:
  88. extern "C" void test_store_point() {
  89. Point p{ExternInt, ExternInt, ExternInt};
  90. benchmark::DoNotOptimize(p);
  91. // CHECK: movl ExternInt(%rip), %eax
  92. // CHECK: movl %eax, -{{[0-9]+}}(%rsp)
  93. // CHECK: movl %eax, -{{[0-9]+}}(%rsp)
  94. // CHECK: movl %eax, -{{[0-9]+}}(%rsp)
  95. // CHECK: ret
  96. }
  97. ```
  98. <!-- {% endraw %} -->
  99. ## Current Requirements and Limitations
  100. The tests require Filecheck to be installed along the `PATH` of the
  101. build machine. Otherwise the tests will be disabled.
  102. Additionally, as mentioned in the previous section, codegen tests are
  103. inherently non-portable. Currently the tests are limited to:
  104. * x86_64 targets.
  105. * Compiled with GCC or Clang
  106. Further work could be done, at least on a limited basis, to extend the
  107. tests to other architectures and compilers (using `CHECK` prefixes).
  108. Furthermore, the tests fail for builds which specify additional flags
  109. that modify code generation, including `--coverage` or `-fsanitize=`.