]> Cypherpunks.ru repositories - gostls13.git/blobdiff - test/nosplit.go
cmd/compile/internal/inline: score call sites exposed by inlines
[gostls13.git] / test / nosplit.go
index a58a64587251f91f53f039a517eefed4b310c434..e171d1da6618ec649274f36d5fed8d76ebb4a38f 100644 (file)
@@ -1,6 +1,7 @@
-// +build !nacl
 // run
 
+//go:build !nacl && !js && !aix && !wasip1 && !gcflags_noopt && gc
+
 // Copyright 2014 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
@@ -21,6 +22,8 @@ import (
        "strings"
 )
 
+const debug = false
+
 var tests = `
 # These are test cases for the linker analysis that detects chains of
 # nosplit functions that would cause a stack overflow.
@@ -33,8 +36,8 @@ var tests = `
 # The body is assembly code, with some shorthands.
 # The shorthand 'call x' stands for CALL x(SB).
 # The shorthand 'callind' stands for 'CALL R0', where R0 is a register.
-# Each test case must define a function named main, and it must be first.
-# That is, a line beginning "main " indicates the start of a new test case.
+# Each test case must define a function named start, and it must be first.
+# That is, a line beginning "start " indicates the start of a new test case.
 # Within a stanza, ; can be used instead of \n to separate lines.
 #
 # After the function definition, the test case ends with an optional
@@ -46,41 +49,54 @@ var tests = `
 # that are only 32-bit aligned.
 
 # Ordinary function should work
-main 0
+start 0
 
 # Large frame marked nosplit is always wrong.
-main 10000 nosplit
+# Frame is so large it overflows cmd/link's int16.
+start 100000 nosplit
 REJECT
 
 # Calling a large frame is okay.
-main 0 call big
+start 0 call big
 big 10000
 
 # But not if the frame is nosplit.
-main 0 call big
+start 0 call big
 big 10000 nosplit
 REJECT
 
 # Recursion is okay.
-main 0 call main
+start 0 call start
 
 # Recursive nosplit runs out of space.
-main 0 nosplit call main
+start 0 nosplit call start
+REJECT
+
+# Non-trivial recursion runs out of space.
+start 0 call f1
+f1 0 nosplit call f2
+f2 0 nosplit call f1
+REJECT
+# Same but cycle starts below nosplit entry.
+start 0 call f1
+f1 0 nosplit call f2
+f2 0 nosplit call f3
+f3 0 nosplit call f2
 REJECT
 
 # Chains of ordinary functions okay.
-main 0 call f1
+start 0 call f1
 f1 80 call f2
 f2 80
 
 # Chains of nosplit must fit in the stack limit, 128 bytes.
-main 0 call f1
+start 0 call f1
 f1 80 nosplit call f2
 f2 80 nosplit
 REJECT
 
 # Larger chains.
-main 0 call f1
+start 0 call f1
 f1 16 call f2
 f2 16 call f3
 f3 16 call f4
@@ -91,7 +107,7 @@ f7 16 call f8
 f8 16 call end
 end 1000
 
-main 0 call f1
+start 0 call f1
 f1 16 nosplit call f2
 f2 16 nosplit call f3
 f3 16 nosplit call f4
@@ -103,85 +119,94 @@ f8 16 nosplit call end
 end 1000
 REJECT
 
+# Two paths both go over the stack limit.
+start 0 call f1
+f1 80 nosplit call f2 call f3
+f2 40 nosplit call f4
+f3 96 nosplit
+f4 40 nosplit
+REJECT
+
 # Test cases near the 128-byte limit.
 
 # Ordinary stack split frame is always okay.
-main 112
-main 116
-main 120
-main 124
-main 128
-main 132
-main 136
+start 112
+start 116
+start 120
+start 124
+start 128
+start 132
+start 136
 
 # A nosplit leaf can use the whole 128-CallSize bytes available on entry.
-# (CallSize is 32 on ppc64)
-main 96 nosplit
-main 100 nosplit; REJECT ppc64 ppc64le
-main 104 nosplit; REJECT ppc64 ppc64le
-main 108 nosplit; REJECT ppc64 ppc64le
-main 112 nosplit; REJECT ppc64 ppc64le
-main 116 nosplit; REJECT ppc64 ppc64le
-main 120 nosplit; REJECT ppc64 ppc64le
-main 124 nosplit; REJECT ppc64 ppc64le
-main 128 nosplit; REJECT
-main 132 nosplit; REJECT
-main 136 nosplit; REJECT
+# (CallSize is 32 on ppc64, 8 on amd64 for frame pointer.)
+start 96 nosplit
+start 100 nosplit; REJECT ppc64 ppc64le
+start 104 nosplit; REJECT ppc64 ppc64le arm64
+start 108 nosplit; REJECT ppc64 ppc64le
+start 112 nosplit; REJECT ppc64 ppc64le arm64
+start 116 nosplit; REJECT ppc64 ppc64le
+start 120 nosplit; REJECT ppc64 ppc64le amd64 arm64
+start 124 nosplit; REJECT ppc64 ppc64le amd64
+start 128 nosplit; REJECT
+start 132 nosplit; REJECT
+start 136 nosplit; REJECT
 
 # Calling a nosplit function from a nosplit function requires
 # having room for the saved caller PC and the called frame.
 # Because ARM doesn't save LR in the leaf, it gets an extra 4 bytes.
 # Because arm64 doesn't save LR in the leaf, it gets an extra 8 bytes.
-# ppc64 doesn't save LR in the leaf, but CallSize is 32, so it gets 24 fewer bytes than amd64.
-main 96 nosplit call f; f 0 nosplit
-main 100 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le
-main 104 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le
-main 108 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le
-main 112 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le
-main 116 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le
-main 120 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64
-main 124 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 386
-main 128 nosplit call f; f 0 nosplit; REJECT
-main 132 nosplit call f; f 0 nosplit; REJECT
-main 136 nosplit call f; f 0 nosplit; REJECT
+# ppc64 doesn't save LR in the leaf, but CallSize is 32, so it gets 24 bytes.
+# Because AMD64 uses frame pointer, it has 8 fewer bytes.
+start 96 nosplit call f; f 0 nosplit
+start 100 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le
+start 104 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le arm64
+start 108 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le
+start 112 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 arm64
+start 116 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64
+start 120 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 arm64
+start 124 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 386
+start 128 nosplit call f; f 0 nosplit; REJECT
+start 132 nosplit call f; f 0 nosplit; REJECT
+start 136 nosplit call f; f 0 nosplit; REJECT
 
 # Calling a splitting function from a nosplit function requires
 # having room for the saved caller PC of the call but also the
 # saved caller PC for the call to morestack.
-# RISC architectures differ in the same way as before.
-main 96 nosplit call f; f 0 call f
-main 100 nosplit call f; f 0 call f; REJECT ppc64 ppc64le
-main 104 nosplit call f; f 0 call f; REJECT ppc64 ppc64le
-main 108 nosplit call f; f 0 call f; REJECT ppc64 ppc64le
-main 112 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64
-main 116 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64
-main 120 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 386
-main 124 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 386
-main 128 nosplit call f; f 0 call f; REJECT
-main 132 nosplit call f; f 0 call f; REJECT
-main 136 nosplit call f; f 0 call f; REJECT
+# Architectures differ in the same way as before.
+start 96 nosplit call f; f 0 call f
+start 100 nosplit call f; f 0 call f; REJECT ppc64 ppc64le
+start 104 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 arm64
+start 108 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64
+start 112 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 arm64
+start 116 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64
+start 120 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 386 arm64
+start 124 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 386
+start 128 nosplit call f; f 0 call f; REJECT
+start 132 nosplit call f; f 0 call f; REJECT
+start 136 nosplit call f; f 0 call f; REJECT
 
 # Indirect calls are assumed to be splitting functions.
-main 96 nosplit callind
-main 100 nosplit callind; REJECT ppc64 ppc64le
-main 104 nosplit callind; REJECT ppc64 ppc64le
-main 108 nosplit callind; REJECT ppc64 ppc64le
-main 112 nosplit callind; REJECT ppc64 ppc64le amd64
-main 116 nosplit callind; REJECT ppc64 ppc64le amd64
-main 120 nosplit callind; REJECT ppc64 ppc64le amd64 386
-main 124 nosplit callind; REJECT ppc64 ppc64le amd64 386
-main 128 nosplit callind; REJECT
-main 132 nosplit callind; REJECT
-main 136 nosplit callind; REJECT
+start 96 nosplit callind
+start 100 nosplit callind; REJECT ppc64 ppc64le
+start 104 nosplit callind; REJECT ppc64 ppc64le amd64 arm64
+start 108 nosplit callind; REJECT ppc64 ppc64le amd64
+start 112 nosplit callind; REJECT ppc64 ppc64le amd64 arm64
+start 116 nosplit callind; REJECT ppc64 ppc64le amd64
+start 120 nosplit callind; REJECT ppc64 ppc64le amd64 386 arm64
+start 124 nosplit callind; REJECT ppc64 ppc64le amd64 386
+start 128 nosplit callind; REJECT
+start 132 nosplit callind; REJECT
+start 136 nosplit callind; REJECT
 
 # Issue 7623
-main 0 call f; f 112
-main 0 call f; f 116
-main 0 call f; f 120
-main 0 call f; f 124
-main 0 call f; f 128
-main 0 call f; f 132
-main 0 call f; f 136
+start 0 call f; f 112
+start 0 call f; f 116
+start 0 call f; f 120
+start 0 call f; f 124
+start 0 call f; f 128
+start 0 call f; f 132
+start 0 call f; f 136
 `
 
 var (
@@ -198,17 +223,6 @@ func main() {
                goarch = runtime.GOARCH
        }
 
-       version, err := exec.Command("go", "tool", "compile", "-V").Output()
-       if err != nil {
-               bug()
-               fmt.Printf("running go tool compile -V: %v\n", err)
-               return
-       }
-       if strings.Contains(string(version), "framepointer") {
-               // Skip this test if GOEXPERIMENT=framepointer
-               return
-       }
-
        dir, err := ioutil.TempDir("", "go-test-nosplit")
        if err != nil {
                bug()
@@ -216,6 +230,11 @@ func main() {
                return
        }
        defer os.RemoveAll(dir)
+       os.Setenv("GOPATH", filepath.Join(dir, "_gopath"))
+
+       if err := ioutil.WriteFile(filepath.Join(dir, "go.mod"), []byte("module go-test-nosplit\n"), 0666); err != nil {
+               log.Panic(err)
+       }
 
        tests = strings.Replace(tests, "\t", " ", -1)
        tests = commentRE.ReplaceAllString(tests, "")
@@ -225,7 +244,7 @@ func main() {
 TestCases:
        for len(tests) > 0 {
                var stanza string
-               i := strings.Index(tests, "\nmain ")
+               i := strings.Index(tests, "\nstart ")
                if i < 0 {
                        stanza, tests = tests, ""
                } else {
@@ -261,20 +280,28 @@ TestCases:
                var buf bytes.Buffer
                ptrSize := 4
                switch goarch {
+               case "mips", "mipsle":
+                       fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
                case "mips64", "mips64le":
                        ptrSize = 8
-                       fmt.Fprintf(&buf, "#define CALL JAL\n#define REGISTER (R0)\n")
+                       fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
+               case "loong64":
+                       ptrSize = 8
+                       fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
                case "ppc64", "ppc64le":
                        ptrSize = 8
-                       fmt.Fprintf(&buf, "#define CALL BL\n#define REGISTER (CTR)\n")
+                       fmt.Fprintf(&buf, "#define REGISTER (CTR)\n")
                case "arm":
-                       fmt.Fprintf(&buf, "#define CALL BL\n#define REGISTER (R0)\n")
+                       fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
                case "arm64":
                        ptrSize = 8
-                       fmt.Fprintf(&buf, "#define CALL BL\n#define REGISTER (R0)\n")
+                       fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
                case "amd64":
                        ptrSize = 8
                        fmt.Fprintf(&buf, "#define REGISTER AX\n")
+               case "riscv64":
+                       ptrSize = 8
+                       fmt.Fprintf(&buf, "#define REGISTER A0\n")
                case "s390x":
                        ptrSize = 8
                        fmt.Fprintf(&buf, "#define REGISTER R10\n")
@@ -282,12 +309,21 @@ TestCases:
                        fmt.Fprintf(&buf, "#define REGISTER AX\n")
                }
 
+               // Since all of the functions we're generating are
+               // ABI0, first enter ABI0 via a splittable function
+               // and then go to the chain we're testing. This way we
+               // don't have to account for ABI wrappers in the chain.
+               fmt.Fprintf(&gobuf, "func main0()\n")
+               fmt.Fprintf(&gobuf, "func main() { main0() }\n")
+               fmt.Fprintf(&buf, "TEXT ·main0(SB),0,$0-0\n\tCALL ·start(SB)\n")
+
+               adjusted := false
                for _, line := range strings.Split(lines, "\n") {
                        line = strings.TrimSpace(line)
                        if line == "" {
                                continue
                        }
-                       for i, subline := range strings.Split(line, ";") {
+                       for _, subline := range strings.Split(line, ";") {
                                subline = strings.TrimSpace(subline)
                                if subline == "" {
                                        continue
@@ -301,27 +337,23 @@ TestCases:
                                name := m[1]
                                size, _ := strconv.Atoi(m[2])
 
-                               // The limit was originally 128 but is now 592.
-                               // Instead of rewriting the test cases above, adjust
-                               // the first stack frame to use up the extra bytes.
-                               if i == 0 {
-                                       size += (720 - 128) - 128
-                                       // Noopt builds have a larger stackguard.
-                                       // See ../src/cmd/dist/buildruntime.go:stackGuardMultiplier
-                                       // This increase is included in obj.StackGuard
-                                       for _, s := range strings.Split(os.Getenv("GO_GCFLAGS"), " ") {
-                                               if s == "-N" {
-                                                       size += 720
-                                               }
-                                       }
-                               }
-
-                               if size%ptrSize == 4 || goarch == "arm64" && size != 0 && (size+8)%16 != 0 {
+                               if size%ptrSize == 4 {
                                        continue TestCases
                                }
                                nosplit := m[3]
                                body := m[4]
 
+                               // The limit was originally 128 but is now 800.
+                               // Instead of rewriting the test cases above, adjust
+                               // the first nosplit frame to use up the extra bytes.
+                               // This isn't exactly right because we could have
+                               // nosplit -> split -> nosplit, but it's good enough.
+                               if !adjusted && nosplit != "" {
+                                       const stackNosplitBase = 800 // internal/abi.StackNosplitBase
+                                       adjusted = true
+                                       size += stackNosplitBase - 128
+                               }
+
                                if nosplit != "" {
                                        nosplit = ",7"
                                } else {
@@ -335,6 +367,12 @@ TestCases:
                        }
                }
 
+               if debug {
+                       fmt.Printf("===\n%s\n", strings.TrimSpace(stanza))
+                       fmt.Printf("-- main.go --\n%s", gobuf.String())
+                       fmt.Printf("-- asm.s --\n%s", buf.String())
+               }
+
                if err := ioutil.WriteFile(filepath.Join(dir, "asm.s"), buf.Bytes(), 0666); err != nil {
                        log.Fatal(err)
                }