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  }