]> Cypherpunks.ru repositories - gostls13.git/commitdiff
[dev.ssa] cmd/compile: default compile+test with SSA
authorDavid Chase <drchase@google.com>
Mon, 26 Oct 2015 21:34:06 +0000 (17:34 -0400)
committerDavid Chase <drchase@google.com>
Fri, 30 Oct 2015 20:35:20 +0000 (20:35 +0000)
Some tests disabled, some bifurcated into _ssa and not,
with appropriate logging added to compiler.

"tests/live.go" in particular needs attention.

SSA-specific testing removed, since it's all SSA now.

Added "-run_skips" option to tests/run.go to simplify
checking whether a test still fails (or how it fails)
on a skipped platform.

The compiler now compiles with SSA by default.
If you don't want SSA, specify GOSSAHASH=n (or N) as
an environment variable.  Function names ending in "_ssa"
are always SSA-compiled.

GOSSAFUNC=fname retains its "SSA for fname, log to ssa.html"
GOSSAPKG=pkg only has an effect when GOSSAHASH=n
GOSSAHASH=10101 etc retains its name-hash-matching behavior
for purposes of debugging.

See #13068

Change-Id: I8217bfeb34173533eaeb391b5f6935483c7d6b43
Reviewed-on: https://go-review.googlesource.com/16299
Reviewed-by: Keith Randall <khr@golang.org>
Run-TryBot: David Chase <drchase@google.com>

13 files changed:
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/ssa/config.go
src/cmd/compile/internal/ssa/export_test.go
src/cmd/compile/internal/ssa/nilcheck.go
src/cmd/dist/test.go
test/live.go
test/live2.go
test/nilcheck.go
test/nilcheck_ssa.go [new file with mode: 0644]
test/nilptr3.go
test/nilptr3_ssa.go [new file with mode: 0644]
test/run.go
test/sliceopt.go

index b96661d15ede0f8593b28cbd713d56d273dd99c5..521e6d7ffa31eefc8ffa11a74752768c2c0ff53e 100644 (file)
@@ -34,10 +34,10 @@ func buildssa(fn *Node) (ssafn *ssa.Func, usessa bool) {
        // 1. IF GOSSAFUNC == current function name THEN
        //       compile this function with SSA and log output to ssa.html
 
-       // 2. IF GOSSAHASH == "y" or "Y" THEN
+       // 2. IF GOSSAHASH == "" THEN
        //       compile this function (and everything else) with SSA
 
-       // 3. IF GOSSAHASH == "" THEN
+       // 3. IF GOSSAHASH == "n" or "N"
        //       IF GOSSAPKG == current package name THEN
        //          compile this function (and everything in this package) with SSA
        //       ELSE
@@ -49,9 +49,10 @@ func buildssa(fn *Node) (ssafn *ssa.Func, usessa bool) {
        //       ELSE
        //          compile this function with the old back end.
 
-       // Plan is for 3 to be remove, and the 2) dependence on GOSSAHASH changes
-       // from "y"/"Y" to empty -- then SSA is default, and is disabled by setting
-       // GOSSAHASH to a value that is neither 0 nor 1 (e.g., "N" or "X")
+       // Plan is for 3 to be removed when the tests are revised.
+       // SSA is now default, and is disabled by setting
+       // GOSSAHASH to n or N, or selectively with strings of
+       // 0 and 1.
 
        if usessa {
                fmt.Println("generating SSA for", name)
@@ -183,10 +184,11 @@ func buildssa(fn *Node) (ssafn *ssa.Func, usessa bool) {
        // Main call to ssa package to compile function
        ssa.Compile(s.f)
 
-       if usessa || gossahash == "y" || gossahash == "Y" {
+       // gossahash = "y" is historical/symmetric-with-"n" -- i.e., not really needed.
+       if usessa || gossahash == "" || gossahash == "y" || gossahash == "Y" {
                return s.f, true
        }
-       if gossahash == "" {
+       if gossahash == "n" || gossahash == "N" {
                if localpkg.Name != os.Getenv("GOSSAPKG") {
                        return s.f, false
                }
@@ -298,9 +300,11 @@ func (s *state) label(sym *Sym) *ssaLabel {
        return lab
 }
 
-func (s *state) Logf(msg string, args ...interface{})           { s.config.Logf(msg, args...) }
-func (s *state) Fatalf(msg string, args ...interface{})         { s.config.Fatalf(msg, args...) }
-func (s *state) Unimplementedf(msg string, args ...interface{}) { s.config.Unimplementedf(msg, args...) }
+func (s *state) Logf(msg string, args ...interface{})            { s.config.Logf(msg, args...) }
+func (s *state) Fatalf(msg string, args ...interface{})          { s.config.Fatalf(msg, args...) }
+func (s *state) Unimplementedf(msg string, args ...interface{})  { s.config.Unimplementedf(msg, args...) }
+func (s *state) Warnl(line int, msg string, args ...interface{}) { s.config.Warnl(line, msg, args...) }
+func (s *state) Debug_checknil() bool                            { return s.config.Debug_checknil() }
 
 var (
        // dummy node for the memory variable
@@ -1997,7 +2001,7 @@ func (s *state) expr(n *Node) *ssa.Value {
                        if haspointers(et) {
                                // TODO: just one write barrier call for all of these writes?
                                // TODO: maybe just one writeBarrierEnabled check?
-                               s.insertWB(et, addr)
+                               s.insertWB(et, addr, n.Lineno)
                        }
                }
 
@@ -2044,7 +2048,7 @@ func (s *state) assign(left *Node, right *ssa.Value, wb bool) {
        }
        s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, t.Size(), addr, right, s.mem())
        if wb {
-               s.insertWB(left.Type, addr)
+               s.insertWB(left.Type, addr, left.Lineno)
        }
 }
 
@@ -2566,7 +2570,7 @@ func (s *state) rtcall(fn *Node, returns bool, results []*Type, args ...*ssa.Val
 // been stored at location p.  Tell the runtime about this write.
 // Note: there must be no GC suspension points between the write and
 // the call that this function inserts.
-func (s *state) insertWB(t *Type, p *ssa.Value) {
+func (s *state) insertWB(t *Type, p *ssa.Value, line int32) {
        // if writeBarrierEnabled {
        //   typedmemmove_nostore(&t, p)
        // }
@@ -2586,6 +2590,10 @@ func (s *state) insertWB(t *Type, p *ssa.Value) {
        taddr := s.newValue1A(ssa.OpAddr, Types[TUINTPTR], &ssa.ExternSymbol{Types[TUINTPTR], typenamesym(t)}, s.sb)
        s.rtcall(typedmemmove_nostore, true, nil, taddr, p)
 
+       if Debug_wb > 0 {
+               Warnl(int(line), "write barrier")
+       }
+
        b.AddEdgeTo(s.curBlock)
 }
 
@@ -2985,6 +2993,10 @@ func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) {
                Fatalf("dottype needs a direct iface type %s", n.Type)
        }
 
+       if Debug_typeassert > 0 {
+               Warnl(int(n.Lineno), "type assertion inlined")
+       }
+
        // TODO:  If we have a nonempty interface and its itab field is nil,
        // then this test is redundant and ifaceType should just branch directly to bFail.
        cond := s.newValue2(ssa.OpEqPtr, Types[TBOOL], typ, target)
@@ -4523,6 +4535,16 @@ func (e *ssaExport) Unimplementedf(msg string, args ...interface{}) {
        e.unimplemented = true
 }
 
+// Warnl reports a "warning", which is usually flag-triggered
+// logging output for the benefit of tests.
+func (e *ssaExport) Warnl(line int, fmt_ string, args ...interface{}) {
+       Warnl(line, fmt_, args...)
+}
+
+func (e *ssaExport) Debug_checknil() bool {
+       return Debug_checknil != 0
+}
+
 func (n *Node) Typ() ssa.Type {
        return n.Type
 }
index cfba10bc244ae53bdf6695cd4f2c800618399baf..014c9602675c758229134969759f063e0da5aec6 100644 (file)
@@ -49,6 +49,12 @@ type Logger interface {
        // Unimplemented reports that the function cannot be compiled.
        // It will be removed once SSA work is complete.
        Unimplementedf(msg string, args ...interface{})
+
+       // Warnl writes compiler messages in the form expected by "errorcheck" tests
+       Warnl(line int, fmt_ string, args ...interface{})
+
+       // Fowards the Debug_checknil flag from gc
+       Debug_checknil() bool
 }
 
 type Frontend interface {
@@ -100,9 +106,11 @@ func (c *Config) NewFunc() *Func {
        return &Func{Config: c, NamedValues: map[GCNode][]*Value{}}
 }
 
-func (c *Config) Logf(msg string, args ...interface{})           { c.fe.Logf(msg, args...) }
-func (c *Config) Fatalf(msg string, args ...interface{})         { c.fe.Fatalf(msg, args...) }
-func (c *Config) Unimplementedf(msg string, args ...interface{}) { c.fe.Unimplementedf(msg, args...) }
+func (c *Config) Logf(msg string, args ...interface{})            { c.fe.Logf(msg, args...) }
+func (c *Config) Fatalf(msg string, args ...interface{})          { c.fe.Fatalf(msg, args...) }
+func (c *Config) Unimplementedf(msg string, args ...interface{})  { c.fe.Unimplementedf(msg, args...) }
+func (c *Config) Warnl(line int, msg string, args ...interface{}) { c.fe.Warnl(line, msg, args...) }
+func (c *Config) Debug_checknil() bool                            { return c.fe.Debug_checknil() }
 
 // TODO(khr): do we really need a separate Config, or can we just
 // store all its fields inside a Func?
index d0ba7b1c09566d4ab2c3cf83f99345459eee55be..c37db75803ba9e660a76e4477c0ba77267554887 100644 (file)
@@ -32,9 +32,11 @@ func (DummyFrontend) Auto(t Type) GCNode {
        return nil
 }
 
-func (d DummyFrontend) Logf(msg string, args ...interface{})           { d.t.Logf(msg, args...) }
-func (d DummyFrontend) Fatalf(msg string, args ...interface{})         { d.t.Fatalf(msg, args...) }
-func (d DummyFrontend) Unimplementedf(msg string, args ...interface{}) { d.t.Fatalf(msg, args...) }
+func (d DummyFrontend) Logf(msg string, args ...interface{})            { d.t.Logf(msg, args...) }
+func (d DummyFrontend) Fatalf(msg string, args ...interface{})          { d.t.Fatalf(msg, args...) }
+func (d DummyFrontend) Unimplementedf(msg string, args ...interface{})  { d.t.Fatalf(msg, args...) }
+func (d DummyFrontend) Warnl(line int, msg string, args ...interface{}) { d.t.Logf(msg, args...) }
+func (d DummyFrontend) Debug_checknil() bool                            { return false }
 
 func (d DummyFrontend) TypeBool() Type    { return TypeBool }
 func (d DummyFrontend) TypeInt8() Type    { return TypeInt8 }
index 5b012a8551f2871852cb6a0baaf3b5df48026791..f8caa7b04275470cf3fac4b410074332f16f1c25 100644 (file)
@@ -88,6 +88,13 @@ func nilcheckelim(f *Func) {
                                        // Eliminate the nil check.
                                        // The deadcode pass will remove vestigial values,
                                        // and the fuse pass will join this block with its successor.
+
+                                       // Logging in the style of the former compiler -- and omit line 1,
+                                       // which is usually in generated code.
+                                       if f.Config.Debug_checknil() && int(node.block.Control.Line) > 1 {
+                                               f.Config.Warnl(int(node.block.Control.Line), "removed nil check")
+                                       }
+
                                        switch node.block.Kind {
                                        case BlockIf:
                                                node.block.Kind = BlockFirst
index be6cdb5c0ba1c48bd487cf71f063590c71381b6f..0afe4c6060452505820357fdf1dc0835ee8f25ad 100644 (file)
@@ -13,7 +13,6 @@ import (
        "log"
        "os"
        "os/exec"
-       "path"
        "path/filepath"
        "regexp"
        "strconv"
@@ -276,31 +275,6 @@ func (t *tester) registerStdTest(pkg string) {
        })
 }
 
-// TODO: Remove when SSA codegen is used by default.
-func (t *tester) registerSSATest(pkg string) {
-       t.tests = append(t.tests, distTest{
-               name:    "go_test_ssa:" + pkg,
-               heading: "Testing packages with SSA codegen.",
-               fn: func() error {
-                       args := []string{
-                               "test",
-                               "-short",
-                               t.timeout(180 * 3), // SSA generates slower code right now
-                               "-gcflags=" + os.Getenv("GO_GCFLAGS"),
-                       }
-                       if t.race {
-                               args = append(args, "-race")
-                       }
-                       args = append(args, pkg)
-                       cmd := exec.Command("go", args...)
-                       cmd.Env = mergeEnvLists([]string{"GOSSAPKG=" + path.Base(pkg)}, os.Environ())
-                       cmd.Stdout = os.Stdout
-                       cmd.Stderr = os.Stderr
-                       return cmd.Run()
-               },
-       })
-}
-
 func (t *tester) registerRaceBenchTest(pkg string) {
        testName := "go_test_bench:" + pkg
        if t.runRx == nil || t.runRx.MatchString(testName) {
@@ -344,9 +318,6 @@ func (t *tester) registerTests() {
                        if strings.HasPrefix(name, "go_test_bench:") {
                                t.registerRaceBenchTest(strings.TrimPrefix(name, "go_test_bench:"))
                        }
-                       if t.goarch == "amd64" && strings.HasPrefix(name, "go_test_ssa:") {
-                               t.registerSSATest(strings.TrimPrefix(name, "go_test_ssa:"))
-                       }
                }
        } else {
                // Use a format string to only list packages and commands that have tests.
@@ -363,11 +334,6 @@ func (t *tester) registerTests() {
                for _, pkg := range pkgs {
                        t.registerStdTest(pkg)
                }
-               if t.goarch == "amd64" {
-                       for _, pkg := range pkgs {
-                               t.registerSSATest(pkg)
-                       }
-               }
                if t.race {
                        for _, pkg := range pkgs {
                                t.registerRaceBenchTest(pkg)
index ae982f495742b17cf82d149962077e429f40aee1..c54f091d1b0729c0e6ab92025e635c5203703361 100644 (file)
@@ -1,3 +1,4 @@
+// +build !amd64
 // errorcheck -0 -l -live -wb=0
 
 // Copyright 2014 The Go Authors.  All rights reserved.
index 747475615703bd503067240c0acf5a3915966825..430f9feb7e08ed784f3686086dbd26e417ec2d6e 100644 (file)
@@ -1,3 +1,4 @@
+// +build !amd64
 // errorcheck -0 -live -wb=0
 
 // Copyright 2014 The Go Authors.  All rights reserved.
index 99c3c5fdb6f150033d3eb2013b60cac015990187..173fcb33a62e8a269bc642c67e7c4ff4ec1617be 100644 (file)
@@ -1,3 +1,4 @@
+// +build !amd64
 // errorcheck -0 -N -d=nil
 
 // Copyright 2013 The Go Authors.  All rights reserved.
@@ -17,7 +18,7 @@ type Struct struct {
 type BigStruct struct {
        X int
        Y float64
-       A [1<<20]int
+       A [1 << 20]int
        Z string
 }
 
@@ -29,86 +30,86 @@ type Empty1 struct {
 }
 
 var (
-       intp *int
-       arrayp *[10]int
-       array0p *[0]int
-       bigarrayp *[1<<26]int
-       structp *Struct
+       intp       *int
+       arrayp     *[10]int
+       array0p    *[0]int
+       bigarrayp  *[1 << 26]int
+       structp    *Struct
        bigstructp *BigStruct
-       emptyp *Empty
-       empty1p *Empty1
+       emptyp     *Empty
+       empty1p    *Empty1
 )
 
 func f1() {
-       _ = *intp // ERROR "nil check"
-       _ = *arrayp // ERROR "nil check"
+       _ = *intp    // ERROR "nil check"
+       _ = *arrayp  // ERROR "nil check"
        _ = *array0p // ERROR "nil check"
        _ = *array0p // ERROR "nil check"
-       _ = *intp // ERROR "nil check"
-       _ = *arrayp // ERROR "nil check"
+       _ = *intp    // ERROR "nil check"
+       _ = *arrayp  // ERROR "nil check"
        _ = *structp // ERROR "nil check"
-       _ = *emptyp // ERROR "nil check"
-       _ = *arrayp // ERROR "nil check"
+       _ = *emptyp  // ERROR "nil check"
+       _ = *arrayp  // ERROR "nil check"
 }
 
 func f2() {
        var (
-               intp *int
-               arrayp *[10]int
-               array0p *[0]int
-               bigarrayp *[1<<20]int
-               structp *Struct
+               intp       *int
+               arrayp     *[10]int
+               array0p    *[0]int
+               bigarrayp  *[1 << 20]int
+               structp    *Struct
                bigstructp *BigStruct
-               emptyp *Empty
-               empty1p *Empty1
+               emptyp     *Empty
+               empty1p    *Empty1
        )
 
-       _ = *intp // ERROR "nil check"
-       _ = *arrayp // ERROR "nil check"
-       _ = *array0p // ERROR "nil check"
-       _ = *array0p // ERROR "nil check"
-       _ = *intp // ERROR "nil check"
-       _ = *arrayp // ERROR "nil check"
-       _ = *structp // ERROR "nil check"
-       _ = *emptyp // ERROR "nil check"
-       _ = *arrayp // ERROR "nil check"
-       _ = *bigarrayp // ERROR "nil check"
+       _ = *intp       // ERROR "nil check"
+       _ = *arrayp     // ERROR "nil check"
+       _ = *array0p    // ERROR "nil check"
+       _ = *array0p    // ERROR "nil check"
+       _ = *intp       // ERROR "nil check"
+       _ = *arrayp     // ERROR "nil check"
+       _ = *structp    // ERROR "nil check"
+       _ = *emptyp     // ERROR "nil check"
+       _ = *arrayp     // ERROR "nil check"
+       _ = *bigarrayp  // ERROR "nil check"
        _ = *bigstructp // ERROR "nil check"
-       _ = *empty1p // ERROR "nil check"
+       _ = *empty1p    // ERROR "nil check"
 }
 
 func fx10k() *[10000]int
-var b bool
 
+var b bool
 
 func f3(x *[10000]int) {
        // Using a huge type and huge offsets so the compiler
        // does not expect the memory hardware to fault.
        _ = x[9999] // ERROR "nil check"
-       
+
        for {
                if x[9999] != 0 { // ERROR "nil check"
                        break
                }
        }
-       
-       x = fx10k() 
+
+       x = fx10k()
        _ = x[9999] // ERROR "nil check"
        if b {
                _ = x[9999] // ERROR "nil check"
        } else {
                _ = x[9999] // ERROR "nil check"
-       }       
+       }
        _ = x[9999] // ERROR "nil check"
 
-       x = fx10k() 
+       x = fx10k()
        if b {
                _ = x[9999] // ERROR "nil check"
        } else {
                _ = x[9999] // ERROR "nil check"
-       }       
+       }
        _ = x[9999] // ERROR "nil check"
-       
+
        fx10k()
        // This one is a bit redundant, if we figured out that
        // x wasn't going to change across the function call.
@@ -138,7 +139,7 @@ func f3b() {
        _ = &x[9] // ERROR "nil check"
 }
 
-func fx10() *[10]int 
+func fx10() *[10]int
 
 func f4(x *[10]int) {
        // Most of these have no checks because a real memory reference follows,
@@ -146,33 +147,33 @@ func f4(x *[10]int) {
        // in the first unmapped page of memory.
 
        _ = x[9] // ERROR "nil check"
-       
+
        for {
                if x[9] != 0 { // ERROR "nil check"
                        break
                }
        }
-       
-       x = fx10() 
+
+       x = fx10()
        _ = x[9] // ERROR "nil check"
        if b {
                _ = x[9] // ERROR "nil check"
        } else {
                _ = x[9] // ERROR "nil check"
-       }       
+       }
        _ = x[9] // ERROR "nil check"
 
-       x = fx10() 
+       x = fx10()
        if b {
                _ = x[9] // ERROR "nil check"
        } else {
                _ = &x[9] // ERROR "nil check"
-       }       
+       }
        _ = x[9] // ERROR "nil check"
-       
+
        fx10()
        _ = x[9] // ERROR "nil check"
-       
+
        x = fx10()
        y := fx10()
        _ = &x[9] // ERROR "nil check"
diff --git a/test/nilcheck_ssa.go b/test/nilcheck_ssa.go
new file mode 100644 (file)
index 0000000..a20cfd8
--- /dev/null
@@ -0,0 +1,187 @@
+// +build amd64
+// errorcheck -0 -N -d=nil
+
+// Copyright 2013 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.
+
+// Test that nil checks are inserted.
+// Optimization is disabled, so redundant checks are not removed.
+
+package p
+
+type Struct struct {
+       X int
+       Y float64
+}
+
+type BigStruct struct {
+       X int
+       Y float64
+       A [1 << 20]int
+       Z string
+}
+
+type Empty struct {
+}
+
+type Empty1 struct {
+       Empty
+}
+
+var (
+       intp       *int
+       arrayp     *[10]int
+       array0p    *[0]int
+       bigarrayp  *[1 << 26]int
+       structp    *Struct
+       bigstructp *BigStruct
+       emptyp     *Empty
+       empty1p    *Empty1
+)
+
+func f1() {
+       _ = *intp    // ERROR "nil check"
+       _ = *arrayp  // ERROR "nil check"
+       _ = *array0p // ERROR "nil check"
+       _ = *array0p // ERROR "nil check"
+       _ = *intp    // ERROR "nil check"
+       _ = *arrayp  // ERROR "nil check"
+       _ = *structp // ERROR "nil check"
+       _ = *emptyp  // ERROR "nil check"
+       _ = *arrayp  // ERROR "nil check"
+}
+
+func f2() {
+       var (
+               intp       *int
+               arrayp     *[10]int
+               array0p    *[0]int
+               bigarrayp  *[1 << 20]int
+               structp    *Struct
+               bigstructp *BigStruct
+               emptyp     *Empty
+               empty1p    *Empty1
+       )
+
+       _ = *intp       // ERROR "nil check"
+       _ = *arrayp     // ERROR "nil check"
+       _ = *array0p    // ERROR "nil check"
+       _ = *array0p    // ERROR "removed nil check"
+       _ = *intp       // ERROR "removed nil check"
+       _ = *arrayp     // ERROR "removed nil check"
+       _ = *structp    // ERROR "nil check"
+       _ = *emptyp     // ERROR "nil check"
+       _ = *arrayp     // ERROR "removed nil check"
+       _ = *bigarrayp  // ERROR "nil check"
+       _ = *bigstructp // ERROR "nil check"
+       _ = *empty1p    // ERROR "nil check"
+}
+
+func fx10k() *[10000]int
+
+var b bool
+
+func f3(x *[10000]int) {
+       // Using a huge type and huge offsets so the compiler
+       // does not expect the memory hardware to fault.
+       _ = x[9999] // ERROR "nil check"
+
+       for {
+               if x[9999] != 0 { // ERROR "removed nil check"
+                       break
+               }
+       }
+
+       x = fx10k()
+       _ = x[9999] // ERROR "nil check"
+       if b {
+               _ = x[9999] // ERROR "removed nil check"
+       } else {
+               _ = x[9999] // ERROR "removed nil check"
+       }
+       _ = x[9999] // ERROR "removed nil check"
+
+       x = fx10k()
+       if b {
+               _ = x[9999] // ERROR "nil check"
+       } else {
+               _ = x[9999] // ERROR "nil check"
+       }
+       _ = x[9999] // ERROR "nil check"
+
+       fx10k()
+       // SSA nilcheck removal works across calls.
+       _ = x[9999] // ERROR "removed nil check"
+}
+
+func f3a() {
+       x := fx10k()
+       y := fx10k()
+       z := fx10k()
+       _ = &x[9] // ERROR "nil check"
+       y = z
+       _ = &x[9] // ERROR "removed nil check"
+       x = y
+       _ = &x[9] // ERROR "nil check"
+}
+
+func f3b() {
+       x := fx10k()
+       y := fx10k()
+       _ = &x[9] // ERROR "nil check"
+       y = x
+       _ = &x[9] // ERROR "removed nil check"
+       x = y
+       _ = &x[9] // ERROR "removed nil check"
+}
+
+func fx10() *[10]int
+
+func f4(x *[10]int) {
+       // Most of these have no checks because a real memory reference follows,
+       // and the offset is small enough that if x is nil, the address will still be
+       // in the first unmapped page of memory.
+
+       _ = x[9] // ERROR "nil check"
+
+       for {
+               if x[9] != 0 { // ERROR "removed nil check"
+                       break
+               }
+       }
+
+       x = fx10()
+       _ = x[9] // ERROR "nil check"
+       if b {
+               _ = x[9] // ERROR "removed nil check"
+       } else {
+               _ = x[9] // ERROR "removed nil check"
+       }
+       _ = x[9] // ERROR "removed nil check"
+
+       x = fx10()
+       if b {
+               _ = x[9] // ERROR "nil check"
+       } else {
+               _ = &x[9] // ERROR "nil check"
+       }
+       _ = x[9] // ERROR "nil check"
+
+       fx10()
+       _ = x[9] // ERROR "removed nil check"
+
+       x = fx10()
+       y := fx10()
+       _ = &x[9] // ERROR "nil check"
+       y = x
+       _ = &x[9] // ERROR "removed nil check"
+       x = y
+       _ = &x[9] // ERROR "removed nil check"
+}
+
+func f5(m map[string]struct{}) bool {
+       // Existence-only map lookups should not generate a nil check
+       _, ok := m[""]
+       return ok
+}
index 607c6fb9846c0f20475b7a0bb5e405bcf07dccf4..33045207b21daf50562e8762f0c1ae73409ea415 100644 (file)
@@ -1,7 +1,7 @@
 // errorcheck -0 -d=nil
 // Fails on ppc64x because of incomplete optimization.
 // See issues 9058.
-// +build !ppc64,!ppc64le
+// +build !ppc64,!ppc64le,!amd64
 
 // Copyright 2013 The Go Authors.  All rights reserved.
 // Use of this source code is governed by a BSD-style
diff --git a/test/nilptr3_ssa.go b/test/nilptr3_ssa.go
new file mode 100644 (file)
index 0000000..9824ce1
--- /dev/null
@@ -0,0 +1,194 @@
+// errorcheck -0 -d=nil
+// Fails on ppc64x because of incomplete optimization.
+// See issues 9058.
+// +build !ppc64,!ppc64le,amd64
+
+// Copyright 2013 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.
+
+// Test that nil checks are removed.
+// Optimization is enabled.
+
+package p
+
+type Struct struct {
+       X int
+       Y float64
+}
+
+type BigStruct struct {
+       X int
+       Y float64
+       A [1 << 20]int
+       Z string
+}
+
+type Empty struct {
+}
+
+type Empty1 struct {
+       Empty
+}
+
+var (
+       intp       *int
+       arrayp     *[10]int
+       array0p    *[0]int
+       bigarrayp  *[1 << 26]int
+       structp    *Struct
+       bigstructp *BigStruct
+       emptyp     *Empty
+       empty1p    *Empty1
+)
+
+func f1() {
+       _ = *intp // ERROR "generated nil check"
+
+       // This one should be removed but the block copy needs
+       // to be turned into its own pseudo-op in order to see
+       // the indirect.
+       _ = *arrayp // ERROR "generated nil check"
+
+       // 0-byte indirect doesn't suffice.
+       // we don't registerize globals, so there are no removed.* nil checks.
+       _ = *array0p // ERROR "generated nil check"
+       _ = *array0p // ERROR "removed nil check"
+
+       _ = *intp    // ERROR "removed nil check"
+       _ = *arrayp  // ERROR "removed nil check"
+       _ = *structp // ERROR "generated nil check"
+       _ = *emptyp  // ERROR "generated nil check"
+       _ = *arrayp  // ERROR "removed nil check"
+}
+
+func f2() {
+       var (
+               intp       *int
+               arrayp     *[10]int
+               array0p    *[0]int
+               bigarrayp  *[1 << 20]int
+               structp    *Struct
+               bigstructp *BigStruct
+               emptyp     *Empty
+               empty1p    *Empty1
+       )
+
+       _ = *intp       // ERROR "generated nil check"
+       _ = *arrayp     // ERROR "generated nil check"
+       _ = *array0p    // ERROR "generated nil check"
+       _ = *array0p    // ERROR "removed.* nil check"
+       _ = *intp       // ERROR "removed.* nil check"
+       _ = *arrayp     // ERROR "removed.* nil check"
+       _ = *structp    // ERROR "generated nil check"
+       _ = *emptyp     // ERROR "generated nil check"
+       _ = *arrayp     // ERROR "removed.* nil check"
+       _ = *bigarrayp  // ERROR "generated nil check" ARM removed nil check before indirect!!
+       _ = *bigstructp // ERROR "generated nil check"
+       _ = *empty1p    // ERROR "generated nil check"
+}
+
+func fx10k() *[10000]int
+
+var b bool
+
+func f3(x *[10000]int) {
+       // Using a huge type and huge offsets so the compiler
+       // does not expect the memory hardware to fault.
+       _ = x[9999] // ERROR "generated nil check"
+
+       for {
+               if x[9999] != 0 { // ERROR "removed nil check"
+                       break
+               }
+       }
+
+       x = fx10k()
+       _ = x[9999] // ERROR "generated nil check"
+       if b {
+               _ = x[9999] // ERROR "removed.* nil check"
+       } else {
+               _ = x[9999] // ERROR "removed.* nil check"
+       }
+       _ = x[9999] // ERROR "removed nil check"
+
+       x = fx10k()
+       if b {
+               _ = x[9999] // ERROR "generated nil check"
+       } else {
+               _ = x[9999] // ERROR "generated nil check"
+       }
+       _ = x[9999] // ERROR "generated nil check"
+
+       fx10k()
+       // This one is a bit redundant, if we figured out that
+       // x wasn't going to change across the function call.
+       // But it's a little complex to do and in practice doesn't
+       // matter enough.
+       _ = x[9999] // ERROR "removed nil check"
+}
+
+func f3a() {
+       x := fx10k()
+       y := fx10k()
+       z := fx10k()
+       _ = &x[9] // ERROR "generated nil check"
+       y = z
+       _ = &x[9] // ERROR "removed.* nil check"
+       x = y
+       _ = &x[9] // ERROR "generated nil check"
+}
+
+func f3b() {
+       x := fx10k()
+       y := fx10k()
+       _ = &x[9] // ERROR "generated nil check"
+       y = x
+       _ = &x[9] // ERROR "removed.* nil check"
+       x = y
+       _ = &x[9] // ERROR "removed.* nil check"
+}
+
+func fx10() *[10]int
+
+func f4(x *[10]int) {
+       // Most of these have no checks because a real memory reference follows,
+       // and the offset is small enough that if x is nil, the address will still be
+       // in the first unmapped page of memory.
+
+       _ = x[9] // ERROR "generated nil check" // bug would like to remove before indirect
+
+       for {
+               if x[9] != 0 { // ERROR "removed nil check"
+                       break
+               }
+       }
+
+       x = fx10()
+       _ = x[9] // ERROR "generated nil check" // bug would like to remove before indirect
+       if b {
+               _ = x[9] // ERROR "removed nil check"
+       } else {
+               _ = x[9] // ERROR "removed nil check"
+       }
+       _ = x[9] // ERROR "removed nil check"
+
+       x = fx10()
+       if b {
+               _ = x[9] // ERROR "generated nil check"  // bug would like to remove before indirect
+       } else {
+               _ = &x[9] // ERROR "generated nil check"
+       }
+       _ = x[9] // ERROR "generated nil check"  // bug would like to remove before indirect
+
+       fx10()
+       _ = x[9] // ERROR "removed nil check"
+
+       x = fx10()
+       y := fx10()
+       _ = &x[9] // ERROR "generated nil check"
+       y = x
+       _ = &x[9] // ERROR "removed[a-z ]* nil check"
+       x = y
+       _ = &x[9] // ERROR "removed[a-z ]* nil check"
+}
index 57b386de9910b6cff528269243a36fce3782339f..425db6ed4ec431877d482cf3ad23b1e901835eef 100644 (file)
@@ -37,6 +37,7 @@ var (
        numParallel    = flag.Int("n", runtime.NumCPU(), "number of parallel tests to run")
        summary        = flag.Bool("summary", false, "show summary of results")
        showSkips      = flag.Bool("show_skips", false, "show skipped tests")
+       runSkips       = flag.Bool("run_skips", false, "run skipped tests (ignore skip and build tags)")
        updateErrors   = flag.Bool("update_errors", false, "update error messages in test file based on compiler output")
        runoutputLimit = flag.Int("l", defaultRunOutputLimit(), "number of parallel runoutput tests to run")
 
@@ -328,6 +329,9 @@ type context struct {
 // shouldTest looks for build tags in a source file and returns
 // whether the file should be used according to the tags.
 func shouldTest(src string, goos, goarch string) (ok bool, whyNot string) {
+       if *runSkips {
+               return true, ""
+       }
        for _, line := range strings.Split(src, "\n") {
                line = strings.TrimSpace(line)
                if strings.HasPrefix(line, "//") {
@@ -470,6 +474,9 @@ func (t *test) run() {
                        args = args[1:]
                }
        case "skip":
+               if *runSkips {
+                       break
+               }
                t.action = "skip"
                return
        default:
index c9d089f7d24dae56c5c294e5bf2a178e5d4a2e73..90ec75086e72032a1719d9de6ca1028375974e7e 100644 (file)
@@ -1,3 +1,4 @@
+// +build !amd64
 // errorcheck -0 -d=append,slice
 
 // Copyright 2015 The Go Authors.  All rights reserved.