github.com/epfl-dcsl/gotee@v0.0.0-20200909122901-014b35f5e5e9/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  }