From 729abfa35ca19a3ec9bd11a8c25eecac5eba6cc9 Mon Sep 17 00:00:00 2001 From: David Chase Date: Mon, 26 Oct 2015 17:34:06 -0400 Subject: [PATCH] [dev.ssa] cmd/compile: default compile+test with SSA 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 Run-TryBot: David Chase --- src/cmd/compile/internal/gc/ssa.go | 48 +++-- src/cmd/compile/internal/ssa/config.go | 14 +- src/cmd/compile/internal/ssa/export_test.go | 8 +- src/cmd/compile/internal/ssa/nilcheck.go | 7 + src/cmd/dist/test.go | 34 ---- test/live.go | 1 + test/live2.go | 1 + test/nilcheck.go | 99 +++++----- test/nilcheck_ssa.go | 187 +++++++++++++++++++ test/nilptr3.go | 2 +- test/nilptr3_ssa.go | 194 ++++++++++++++++++++ test/run.go | 7 + test/sliceopt.go | 1 + 13 files changed, 500 insertions(+), 103 deletions(-) create mode 100644 test/nilcheck_ssa.go create mode 100644 test/nilptr3_ssa.go diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index b96661d15e..521e6d7ffa 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -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 } diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index cfba10bc24..014c960267 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -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? diff --git a/src/cmd/compile/internal/ssa/export_test.go b/src/cmd/compile/internal/ssa/export_test.go index d0ba7b1c09..c37db75803 100644 --- a/src/cmd/compile/internal/ssa/export_test.go +++ b/src/cmd/compile/internal/ssa/export_test.go @@ -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 } diff --git a/src/cmd/compile/internal/ssa/nilcheck.go b/src/cmd/compile/internal/ssa/nilcheck.go index 5b012a8551..f8caa7b042 100644 --- a/src/cmd/compile/internal/ssa/nilcheck.go +++ b/src/cmd/compile/internal/ssa/nilcheck.go @@ -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 diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index be6cdb5c0b..0afe4c6060 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -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) diff --git a/test/live.go b/test/live.go index ae982f4957..c54f091d1b 100644 --- a/test/live.go +++ b/test/live.go @@ -1,3 +1,4 @@ +// +build !amd64 // errorcheck -0 -l -live -wb=0 // Copyright 2014 The Go Authors. All rights reserved. diff --git a/test/live2.go b/test/live2.go index 7474756157..430f9feb7e 100644 --- a/test/live2.go +++ b/test/live2.go @@ -1,3 +1,4 @@ +// +build !amd64 // errorcheck -0 -live -wb=0 // Copyright 2014 The Go Authors. All rights reserved. diff --git a/test/nilcheck.go b/test/nilcheck.go index 99c3c5fdb6..173fcb33a6 100644 --- a/test/nilcheck.go +++ b/test/nilcheck.go @@ -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 index 0000000000..a20cfd8ae6 --- /dev/null +++ b/test/nilcheck_ssa.go @@ -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 +} diff --git a/test/nilptr3.go b/test/nilptr3.go index 607c6fb984..33045207b2 100644 --- a/test/nilptr3.go +++ b/test/nilptr3.go @@ -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 index 0000000000..9824ce1cc0 --- /dev/null +++ b/test/nilptr3_ssa.go @@ -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" +} diff --git a/test/run.go b/test/run.go index 57b386de99..425db6ed4e 100644 --- a/test/run.go +++ b/test/run.go @@ -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: diff --git a/test/sliceopt.go b/test/sliceopt.go index c9d089f7d2..90ec75086e 100644 --- a/test/sliceopt.go +++ b/test/sliceopt.go @@ -1,3 +1,4 @@ +// +build !amd64 // errorcheck -0 -d=append,slice // Copyright 2015 The Go Authors. All rights reserved.