|
| 1 | +// Copyright 2018 The Go Authors. All rights reserved. |
| 2 | +// Use of this source code is governed by a BSD-style |
| 3 | +// license that can be found in the LICENSE file. |
| 4 | + |
| 5 | +The codegen directory contains code generation tests for the gc |
| 6 | +compiler. |
| 7 | + |
| 8 | + |
| 9 | +- Introduction |
| 10 | + |
| 11 | +The test harness compiles Go code inside files in this directory and |
| 12 | +then matches the generated assembly (the output of `go tool compile -S`) |
| 13 | +against a set of regexps specified in comments that follow a special |
| 14 | +syntax (described below). The test driver is implemented as a step of |
| 15 | +the top-level test/run.go suite, called "asmcheck". |
| 16 | + |
| 17 | +The codegen tests run during all.bash, but can also be run in |
| 18 | +isolation by using |
| 19 | + |
| 20 | + $ ../bin/go run run.go -v codegen |
| 21 | + |
| 22 | +in the top-level test directory. |
| 23 | + |
| 24 | +The test harness compiles the tests with the same go toolchain that is |
| 25 | +used to run run.go. After writing tests for a newly added codegen |
| 26 | +transformation, it can be useful to first run the test harness with a |
| 27 | +toolchain from a released Go version (and verify that the new tests |
| 28 | +fail), and then re-runnig the tests using the devel toolchain. |
| 29 | + |
| 30 | + |
| 31 | +- Regexps comments syntax |
| 32 | + |
| 33 | +Instructions to match are specified inside plain comments that start |
| 34 | +with an architecture tag, followed by a colon and a quoted Go-style |
| 35 | +regexp to be matched. For example, the following test: |
| 36 | + |
| 37 | + func Sqrt(x float64) float64 { |
| 38 | + // amd64:"SQRTSD" |
| 39 | + // arm64:"FSQRTD" |
| 40 | + return math.Sqrt(x) |
| 41 | + } |
| 42 | + |
| 43 | +verifies that math.Sqrt calls are intrinsified to a SQRTSD instruction |
| 44 | +on amd64, and to a FSQRTD instruction on arm64. |
| 45 | + |
| 46 | +It is possible to put multiple architectures checks into the same |
| 47 | +line, as: |
| 48 | + |
| 49 | + // amd64:"SQRTSD" arm64:"FSQRTD" |
| 50 | + |
| 51 | +although this form should be avoided when doing so would make the |
| 52 | +regexps line excessively long and difficult to read. |
| 53 | + |
| 54 | +Comments that are on their own line will be matched against the first |
| 55 | +subsequent non-comment line. Inline comments are also supported; the |
| 56 | +regexp will be matched against the code found on the same line: |
| 57 | + |
| 58 | + func Sqrt(x float64) float64 { |
| 59 | + return math.Sqrt(x) // arm:"SQRTD" |
| 60 | + } |
| 61 | + |
| 62 | +It's possible to specify a comma-separated list of regexps to be |
| 63 | +matched. For example, the following test: |
| 64 | + |
| 65 | + func TZ8(n uint8) int { |
| 66 | + // amd64:"BSFQ","ORQ\t\\$256" |
| 67 | + return bits.TrailingZeros8(n) |
| 68 | + } |
| 69 | + |
| 70 | +verifies that the code generated for a bits.TrailingZeros8 call on |
| 71 | +amd64 contains both a "BSFQ" instruction and an "ORQ $256". |
| 72 | + |
| 73 | +Note how the ORQ regex includes a tab char (\t). In the Go assembly |
| 74 | +syntax, operands are separated from opcodes by a tabulation. |
| 75 | + |
| 76 | +Regexps can be quoted using either " or `. Special characters must be |
| 77 | +escaped accordingly. Both of these are accepted, and equivalent: |
| 78 | + |
| 79 | + // amd64:"ADDQ\t\\$3" |
| 80 | + // amd64:`ADDQ\t\$3` |
| 81 | + |
| 82 | +and they'll match this assembly line: |
| 83 | + |
| 84 | + ADDQ $3 |
| 85 | + |
| 86 | +Negative matches can be specified using a - before the quoted regexp. |
| 87 | +For example: |
| 88 | + |
| 89 | + func MoveSmall() { |
| 90 | + x := [...]byte{1, 2, 3, 4, 5, 6, 7} |
| 91 | + copy(x[1:], x[:]) // arm64:-".*memmove" |
| 92 | + } |
| 93 | + |
| 94 | +verifies that NO memmove call is present in the assembly generated for |
| 95 | +the copy() line. |
| 96 | + |
| 97 | + |
| 98 | +- Remarks, and Caveats |
| 99 | + |
| 100 | +-- Write small test functions |
| 101 | + |
| 102 | +As a general guideline, test functions should be small, to avoid |
| 103 | +possible interactions between unrelated lines of code that may be |
| 104 | +introduced, for example, by the compiler's optimization passes. |
| 105 | + |
| 106 | +Any given line of Go code could get assigned more instructions that it |
| 107 | +may appear from reading the source. In particular, matching all MOV |
| 108 | +instructions should be avoided; the compiler may add them for |
| 109 | +unrelated reasons and this may render the test ineffective. |
| 110 | + |
| 111 | +-- Line matching logic |
| 112 | + |
| 113 | +Regexps are always matched from the start of the instructions line. |
| 114 | +This means, for example, that the "MULQ" regexp is equivalent to |
| 115 | +"^MULQ" (^ representing the start of the line), and it will NOT match |
| 116 | +the following assembly line: |
| 117 | + |
| 118 | + IMULQ $99, AX |
| 119 | + |
| 120 | +To force a match at any point of the line, ".*MULQ" should be used. |
| 121 | + |
| 122 | +For the same reason, a negative regexp like -"memmove" is not enough |
| 123 | +to make sure that no memmove call is included in the assembly. A |
| 124 | +memmove call looks like this: |
| 125 | + |
| 126 | + CALL runtime.memmove(SB) |
| 127 | + |
| 128 | +To make sure that the "memmove" symbol does not appear anywhere in the |
| 129 | +assembly, the negative regexp to be used is -".*memmove". |
0 commit comments