github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/cmd/compile/internal/gc/inl_test.go (about) 1 // Copyright 2017 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 package gc 6 7 import ( 8 "bufio" 9 "internal/testenv" 10 "io" 11 "os/exec" 12 "regexp" 13 "runtime" 14 "strings" 15 "testing" 16 ) 17 18 // TestIntendedInlining tests that specific runtime functions are inlined. 19 // This allows refactoring for code clarity and re-use without fear that 20 // changes to the compiler will cause silent performance regressions. 21 func TestIntendedInlining(t *testing.T) { 22 if testing.Short() && testenv.Builder() == "" { 23 t.Skip("skipping in short mode") 24 } 25 testenv.MustHaveGoRun(t) 26 t.Parallel() 27 28 // want is the list of function names (by package) that should 29 // be inlinable. If they have no callers in thier packages, they 30 // might not actually be inlined anywhere. 31 want := map[string][]string{ 32 "runtime": { 33 // TODO(mvdan): enable these once mid-stack 34 // inlining is available 35 // "adjustctxt", 36 37 "add", 38 "acquirem", 39 "add1", 40 "addb", 41 "adjustpanics", 42 "adjustpointer", 43 "bucketMask", 44 "bucketShift", 45 "chanbuf", 46 "deferArgs", 47 "deferclass", 48 "evacuated", 49 "fastlog2", 50 "fastrand", 51 "float64bits", 52 "funcPC", 53 "getArgInfoFast", 54 "getm", 55 "isDirectIface", 56 "itabHashFunc", 57 "noescape", 58 "pcvalueCacheKey", 59 "readUnaligned32", 60 "readUnaligned64", 61 "releasem", 62 "round", 63 "roundupsize", 64 "stackmapdata", 65 "stringStructOf", 66 "subtract1", 67 "subtractb", 68 "tophash", 69 "totaldefersize", 70 "(*bmap).keys", 71 "(*bmap).overflow", 72 "(*waitq).enqueue", 73 74 // GC-related ones 75 "cgoInRange", 76 "gclinkptr.ptr", 77 "guintptr.ptr", 78 "heapBits.bits", 79 "heapBits.isPointer", 80 "heapBits.morePointers", 81 "heapBits.next", 82 "heapBitsForAddr", 83 "markBits.isMarked", 84 "muintptr.ptr", 85 "puintptr.ptr", 86 "spanOf", 87 "spanOfUnchecked", 88 //"(*gcWork).putFast", // TODO(austin): For debugging #27993 89 "(*gcWork).tryGetFast", 90 "(*guintptr).set", 91 "(*markBits).advance", 92 "(*mspan).allocBitsForIndex", 93 "(*mspan).base", 94 "(*mspan).markBitsForBase", 95 "(*mspan).markBitsForIndex", 96 "(*muintptr).set", 97 "(*puintptr).set", 98 }, 99 "runtime/internal/sys": {}, 100 "runtime/internal/math": { 101 "MulUintptr", 102 }, 103 "bytes": { 104 "(*Buffer).Bytes", 105 "(*Buffer).Cap", 106 "(*Buffer).Len", 107 "(*Buffer).Grow", 108 "(*Buffer).Next", 109 "(*Buffer).Read", 110 "(*Buffer).ReadByte", 111 "(*Buffer).Reset", 112 "(*Buffer).String", 113 "(*Buffer).UnreadByte", 114 "(*Buffer).tryGrowByReslice", 115 }, 116 "compress/flate": { 117 "byLiteral.Len", 118 "byLiteral.Less", 119 "byLiteral.Swap", 120 }, 121 "unicode/utf8": { 122 "FullRune", 123 "FullRuneInString", 124 "RuneLen", 125 "ValidRune", 126 }, 127 "reflect": { 128 "Value.CanAddr", 129 "Value.CanSet", 130 "Value.IsValid", 131 "add", 132 "align", 133 "flag.kind", 134 "flag.ro", 135 136 // TODO: these use panic, need mid-stack 137 // inlining 138 // "Value.CanInterface", 139 // "Value.pointer", 140 // "flag.mustBe", 141 // "flag.mustBeAssignable", 142 // "flag.mustBeExported", 143 }, 144 "regexp": { 145 "(*bitState).push", 146 }, 147 "math/big": { 148 "bigEndianWord", 149 }, 150 } 151 152 if runtime.GOARCH != "386" && runtime.GOARCH != "mips64" && runtime.GOARCH != "mips64le" { 153 // nextFreeFast calls sys.Ctz64, which on 386 is implemented in asm and is not inlinable. 154 // We currently don't have midstack inlining so nextFreeFast is also not inlinable on 386. 155 // On MIPS64x, Ctz64 is not intrinsified and causes nextFreeFast too expensive to inline 156 // (Issue 22239). 157 want["runtime"] = append(want["runtime"], "nextFreeFast") 158 } 159 if runtime.GOARCH != "386" { 160 // As explained above, Ctz64 and Ctz32 are not Go code on 386. 161 // The same applies to Bswap32. 162 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Ctz64") 163 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Ctz32") 164 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Bswap32") 165 } 166 switch runtime.GOARCH { 167 case "amd64", "amd64p32", "arm64", "mips64", "mips64le", "ppc64", "ppc64le", "s390x": 168 // rotl_31 is only defined on 64-bit architectures 169 want["runtime"] = append(want["runtime"], "rotl_31") 170 } 171 172 // Functions that must actually be inlined; they must have actual callers. 173 must := map[string]bool{ 174 "compress/flate.byLiteral.Len": true, 175 "compress/flate.byLiteral.Less": true, 176 "compress/flate.byLiteral.Swap": true, 177 } 178 179 notInlinedReason := make(map[string]string) 180 pkgs := make([]string, 0, len(want)) 181 for pname, fnames := range want { 182 pkgs = append(pkgs, pname) 183 for _, fname := range fnames { 184 fullName := pname + "." + fname 185 if _, ok := notInlinedReason[fullName]; ok { 186 t.Errorf("duplicate func: %s", fullName) 187 } 188 notInlinedReason[fullName] = "unknown reason" 189 } 190 } 191 192 args := append([]string{"build", "-a", "-gcflags=all=-m -m"}, pkgs...) 193 cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), args...)) 194 pr, pw := io.Pipe() 195 cmd.Stdout = pw 196 cmd.Stderr = pw 197 cmdErr := make(chan error, 1) 198 go func() { 199 cmdErr <- cmd.Run() 200 pw.Close() 201 }() 202 scanner := bufio.NewScanner(pr) 203 curPkg := "" 204 canInline := regexp.MustCompile(`: can inline ([^ ]*)`) 205 haveInlined := regexp.MustCompile(`: inlining call to ([^ ]*)`) 206 cannotInline := regexp.MustCompile(`: cannot inline ([^ ]*): (.*)`) 207 for scanner.Scan() { 208 line := scanner.Text() 209 if strings.HasPrefix(line, "# ") { 210 curPkg = line[2:] 211 continue 212 } 213 if m := haveInlined.FindStringSubmatch(line); m != nil { 214 fname := m[1] 215 delete(notInlinedReason, curPkg+"."+fname) 216 continue 217 } 218 if m := canInline.FindStringSubmatch(line); m != nil { 219 fname := m[1] 220 fullname := curPkg + "." + fname 221 // If function must be inlined somewhere, beeing inlinable is not enough 222 if _, ok := must[fullname]; !ok { 223 delete(notInlinedReason, fullname) 224 continue 225 } 226 } 227 if m := cannotInline.FindStringSubmatch(line); m != nil { 228 fname, reason := m[1], m[2] 229 fullName := curPkg + "." + fname 230 if _, ok := notInlinedReason[fullName]; ok { 231 // cmd/compile gave us a reason why 232 notInlinedReason[fullName] = reason 233 } 234 continue 235 } 236 } 237 if err := <-cmdErr; err != nil { 238 t.Fatal(err) 239 } 240 if err := scanner.Err(); err != nil { 241 t.Fatal(err) 242 } 243 for fullName, reason := range notInlinedReason { 244 t.Errorf("%s was not inlined: %s", fullName, reason) 245 } 246 }