github.com/yukk001/go1.10.8@v0.0.0-20190813125351-6df2d3982e20/src/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 inlined. 30 want := map[string][]string{ 31 "runtime": { 32 // TODO(mvdan): enable these once mid-stack 33 // inlining is available 34 // "adjustctxt", 35 36 "add", 37 "acquirem", 38 "add1", 39 "addb", 40 "adjustpanics", 41 "adjustpointer", 42 "bucketMask", 43 "bucketShift", 44 "chanbuf", 45 "deferArgs", 46 "deferclass", 47 "evacuated", 48 "fastlog2", 49 "fastrand", 50 "float64bits", 51 "funcPC", 52 "getm", 53 "isDirectIface", 54 "itabHashFunc", 55 "maxSliceCap", 56 "noescape", 57 "readUnaligned32", 58 "readUnaligned64", 59 "releasem", 60 "round", 61 "roundupsize", 62 "selectsize", 63 "stringStructOf", 64 "subtract1", 65 "subtractb", 66 "tophash", 67 "totaldefersize", 68 "(*bmap).keys", 69 "(*bmap).overflow", 70 "(*waitq).enqueue", 71 72 // GC-related ones 73 "cgoInRange", 74 "gclinkptr.ptr", 75 "guintptr.ptr", 76 "heapBits.bits", 77 "heapBits.isPointer", 78 "heapBits.morePointers", 79 "heapBits.next", 80 "heapBitsForAddr", 81 "inheap", 82 "markBits.isMarked", 83 "muintptr.ptr", 84 "puintptr.ptr", 85 "spanOfUnchecked", 86 "(*gcWork).putFast", 87 "(*gcWork).tryGetFast", 88 "(*guintptr).set", 89 "(*markBits).advance", 90 "(*mspan).allocBitsForIndex", 91 "(*mspan).base", 92 "(*mspan).markBitsForBase", 93 "(*mspan).markBitsForIndex", 94 "(*muintptr).set", 95 "(*puintptr).set", 96 }, 97 "runtime/internal/sys": {}, 98 "bytes": { 99 "(*Buffer).Bytes", 100 "(*Buffer).Cap", 101 "(*Buffer).Len", 102 "(*Buffer).Next", 103 "(*Buffer).Read", 104 "(*Buffer).ReadByte", 105 "(*Buffer).Reset", 106 "(*Buffer).String", 107 "(*Buffer).UnreadByte", 108 "(*Buffer).tryGrowByReslice", 109 }, 110 "unicode/utf8": { 111 "FullRune", 112 "FullRuneInString", 113 "RuneLen", 114 "ValidRune", 115 }, 116 "reflect": { 117 "Value.CanAddr", 118 "Value.CanSet", 119 "Value.IsValid", 120 "add", 121 "align", 122 "flag.kind", 123 "flag.ro", 124 125 // TODO: these use panic, need mid-stack 126 // inlining 127 // "Value.CanInterface", 128 // "Value.pointer", 129 // "flag.mustBe", 130 // "flag.mustBeAssignable", 131 // "flag.mustBeExported", 132 }, 133 "regexp": { 134 "(*bitState).push", 135 }, 136 } 137 138 if runtime.GOARCH != "386" && runtime.GOARCH != "mips64" && runtime.GOARCH != "mips64le" { 139 // nextFreeFast calls sys.Ctz64, which on 386 is implemented in asm and is not inlinable. 140 // We currently don't have midstack inlining so nextFreeFast is also not inlinable on 386. 141 // On MIPS64x, Ctz64 is not intrinsified and causes nextFreeFast too expensive to inline 142 // (Issue 22239). 143 want["runtime"] = append(want["runtime"], "nextFreeFast") 144 } 145 if runtime.GOARCH != "386" { 146 // As explained above, Ctz64 and Ctz32 are not Go code on 386. 147 // The same applies to Bswap32. 148 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Ctz64") 149 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Ctz32") 150 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Bswap32") 151 } 152 switch runtime.GOARCH { 153 case "amd64", "amd64p32", "arm64", "mips64", "mips64le", "ppc64", "ppc64le", "s390x": 154 // rotl_31 is only defined on 64-bit architectures 155 want["runtime"] = append(want["runtime"], "rotl_31") 156 } 157 158 notInlinedReason := make(map[string]string) 159 pkgs := make([]string, 0, len(want)) 160 for pname, fnames := range want { 161 pkgs = append(pkgs, pname) 162 for _, fname := range fnames { 163 fullName := pname + "." + fname 164 if _, ok := notInlinedReason[fullName]; ok { 165 t.Errorf("duplicate func: %s", fullName) 166 } 167 notInlinedReason[fullName] = "unknown reason" 168 } 169 } 170 171 args := append([]string{"build", "-a", "-gcflags=all=-m -m"}, pkgs...) 172 cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), args...)) 173 pr, pw := io.Pipe() 174 cmd.Stdout = pw 175 cmd.Stderr = pw 176 cmdErr := make(chan error, 1) 177 go func() { 178 cmdErr <- cmd.Run() 179 pw.Close() 180 }() 181 scanner := bufio.NewScanner(pr) 182 curPkg := "" 183 canInline := regexp.MustCompile(`: can inline ([^ ]*)`) 184 cannotInline := regexp.MustCompile(`: cannot inline ([^ ]*): (.*)`) 185 for scanner.Scan() { 186 line := scanner.Text() 187 if strings.HasPrefix(line, "# ") { 188 curPkg = line[2:] 189 continue 190 } 191 if m := canInline.FindStringSubmatch(line); m != nil { 192 fname := m[1] 193 delete(notInlinedReason, curPkg+"."+fname) 194 continue 195 } 196 if m := cannotInline.FindStringSubmatch(line); m != nil { 197 fname, reason := m[1], m[2] 198 fullName := curPkg + "." + fname 199 if _, ok := notInlinedReason[fullName]; ok { 200 // cmd/compile gave us a reason why 201 notInlinedReason[fullName] = reason 202 } 203 continue 204 } 205 } 206 if err := <-cmdErr; err != nil { 207 t.Fatal(err) 208 } 209 if err := scanner.Err(); err != nil { 210 t.Fatal(err) 211 } 212 for fullName, reason := range notInlinedReason { 213 t.Errorf("%s was not inlined: %s", fullName, reason) 214 } 215 }