diff options
author | Dan Willemsen <dwillemsen@google.com> | 2018-08-27 23:21:26 -0700 |
---|---|---|
committer | Dan Willemsen <dwillemsen@google.com> | 2018-08-27 23:21:26 -0700 |
commit | c74133257abfed957ccfd7cf361759c4e4364263 (patch) | |
tree | 3662c9f5bd50c1ea4e162a7ffbdb9829cc8a7445 /test | |
parent | ee04cf91e69ed1ea9cb2e00c5a4764f6b67c4175 (diff) | |
download | darwin-x86-c74133257abfed957ccfd7cf361759c4e4364263.tar.gz |
Update prebuilts to go1.11 ab/4979655android-o-mr1-iot-release-smart-display-r3android-o-mr1-iot-release-1.0.5android-o-mr1-iot-release-1.0.4oreo-mr1-1.2-iot-releasemaster-cuttlefish-testing-release
Test: m -j blueprint_tools
Change-Id: I3c22814607204e2bd3ab0de6e67775f1c052714c
Diffstat (limited to 'test')
202 files changed, 10911 insertions, 895 deletions
diff --git a/test/README.md b/test/README.md index ca6a8c658..068dc1b22 100644 --- a/test/README.md +++ b/test/README.md @@ -4,7 +4,7 @@ They are run as part of all.bash. To run just these tests, execute: - go run run.go + ../bin/go run run.go Standard library tests should be written as regular Go tests in the appropriate package. diff --git a/test/alias2.go b/test/alias2.go index 32c365499..7ea1b2908 100644 --- a/test/alias2.go +++ b/test/alias2.go @@ -95,10 +95,10 @@ type _ = reflect.ValueOf // ERROR "reflect.ValueOf is not a type|expected type" func (A1) m() {} // ERROR "cannot define new methods on non-local type int|may not define methods on non-local type" func (A2) m() {} // ERROR "invalid receiver type" func (A3) m() {} // ERROR "cannot define new methods on non-local type reflect.Value|may not define methods on non-local type" -func (A4) m() {} // ERROR "reflect.Value.m redeclared in this block" "cannot define new methods on non-local type reflect.Value|may not define methods on non-local type" +func (A4) m() {} // ERROR "cannot define new methods on non-local type reflect.Value|may not define methods on non-local type" type B1 = struct{} -func (B1) m() {} // ERROR "m redeclared in this block" "invalid receiver type" +func (B1) m() {} // ERROR "invalid receiver type" // TODO(gri) expand diff --git a/test/align.go b/test/align.go new file mode 100644 index 000000000..61d07c49b --- /dev/null +++ b/test/align.go @@ -0,0 +1,29 @@ +// run + +// Copyright 2018 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. + +package main + +// On 32-bit archs, one of the f fields of a [2]T +// will be unaligned (address of 4 mod 8). +// Make sure we can access the f fields successfully, +// particularly for load-add combo instructions +// introduced by CL 102036. +type T struct { + pad uint32 + f float64 +} + +//go:noinline +func f(t, u *T) float64 { + return 3.0 + t.f + u.f +} + +func main() { + t := [2]T{{0, 1.0}, {0, 2.0}} + sink = f(&t[0], &t[1]) +} + +var sink float64 diff --git a/test/append.go b/test/append.go index 3f6251ee5..3d1606340 100644 --- a/test/append.go +++ b/test/append.go @@ -13,14 +13,12 @@ import ( "reflect" ) - func verify(name string, result, expected interface{}) { if !reflect.DeepEqual(result, expected) { panic(name) } } - func main() { for _, t := range tests { verify(t.name, t.result, t.expected) @@ -30,6 +28,10 @@ func main() { verifyType() } +var ( + zero int = 0 + one int = 1 +) var tests = []struct { name string @@ -49,7 +51,6 @@ var tests = []struct { {"bool i", append([]bool{true, false, true}, []bool{true}...), []bool{true, false, true, true}}, {"bool j", append([]bool{true, false, true}, []bool{true, true, true}...), []bool{true, false, true, true, true, true}}, - {"byte a", append([]byte{}), []byte{}}, {"byte b", append([]byte{}, 0), []byte{0}}, {"byte c", append([]byte{}, 0, 1, 2, 3), []byte{0, 1, 2, 3}}, @@ -84,7 +85,6 @@ var tests = []struct { {"int16 i", append([]int16{0, 1, 2}, []int16{3}...), []int16{0, 1, 2, 3}}, {"int16 j", append([]int16{0, 1, 2}, []int16{3, 4, 5}...), []int16{0, 1, 2, 3, 4, 5}}, - {"uint32 a", append([]uint32{}), []uint32{}}, {"uint32 b", append([]uint32{}, 0), []uint32{0}}, {"uint32 c", append([]uint32{}, 0, 1, 2, 3), []uint32{0, 1, 2, 3}}, @@ -99,7 +99,6 @@ var tests = []struct { {"uint32 i", append([]uint32{0, 1, 2}, []uint32{3}...), []uint32{0, 1, 2, 3}}, {"uint32 j", append([]uint32{0, 1, 2}, []uint32{3, 4, 5}...), []uint32{0, 1, 2, 3, 4, 5}}, - {"float64 a", append([]float64{}), []float64{}}, {"float64 b", append([]float64{}, 0), []float64{0}}, {"float64 c", append([]float64{}, 0, 1, 2, 3), []float64{0, 1, 2, 3}}, @@ -114,7 +113,6 @@ var tests = []struct { {"float64 i", append([]float64{0, 1, 2}, []float64{3}...), []float64{0, 1, 2, 3}}, {"float64 j", append([]float64{0, 1, 2}, []float64{3, 4, 5}...), []float64{0, 1, 2, 3, 4, 5}}, - {"complex128 a", append([]complex128{}), []complex128{}}, {"complex128 b", append([]complex128{}, 0), []complex128{0}}, {"complex128 c", append([]complex128{}, 0, 1, 2, 3), []complex128{0, 1, 2, 3}}, @@ -129,7 +127,6 @@ var tests = []struct { {"complex128 i", append([]complex128{0, 1, 2}, []complex128{3}...), []complex128{0, 1, 2, 3}}, {"complex128 j", append([]complex128{0, 1, 2}, []complex128{3, 4, 5}...), []complex128{0, 1, 2, 3, 4, 5}}, - {"string a", append([]string{}), []string{}}, {"string b", append([]string{}, "0"), []string{"0"}}, {"string c", append([]string{}, "0", "1", "2", "3"), []string{"0", "1", "2", "3"}}, @@ -143,8 +140,19 @@ var tests = []struct { {"string i", append([]string{"0", "1", "2"}, []string{"3"}...), []string{"0", "1", "2", "3"}}, {"string j", append([]string{"0", "1", "2"}, []string{"3", "4", "5"}...), []string{"0", "1", "2", "3", "4", "5"}}, -} + {"make a", append([]string{}, make([]string, 0)...), []string{}}, + {"make b", append([]string(nil), make([]string, 0)...), []string(nil)}, + + {"make c", append([]struct{}{}, make([]struct{}, 0)...), []struct{}{}}, + {"make d", append([]struct{}{}, make([]struct{}, 2)...), make([]struct{}, 2)}, + + {"make e", append([]int{0, 1}, make([]int, 0)...), []int{0, 1}}, + {"make f", append([]int{0, 1}, make([]int, 2)...), []int{0, 1, 0, 0}}, + + {"make g", append([]*int{&zero, &one}, make([]*int, 0)...), []*int{&zero, &one}}, + {"make h", append([]*int{&zero, &one}, make([]*int, 2)...), []*int{&zero, &one, nil, nil}}, +} func verifyStruct() { type T struct { @@ -185,7 +193,6 @@ func verifyStruct() { verify("struct m", append(s, e...), r) } - func verifyInterface() { type T interface{} type S []T diff --git a/test/append1.go b/test/append1.go index 6d42368e4..0fe24c095 100644 --- a/test/append1.go +++ b/test/append1.go @@ -17,4 +17,6 @@ func main() { _ = append(s...) // ERROR "cannot use ... on first argument" _ = append(s, 2, s...) // ERROR "too many arguments to append" + _ = append(s, make([]int, 0)) // ERROR "cannot use make.* as type int in append" + _ = append(s, make([]int, -1)...) // ERROR "negative len argument in make" } diff --git a/test/bounds.go b/test/bounds.go index a0febb515..34c444877 100644 --- a/test/bounds.go +++ b/test/bounds.go @@ -12,23 +12,23 @@ package foo var ( s []int - a1 [1]int - a1k [1000]int + a1 [1]int + a1k [1000]int a100k [100000]int - p1 *[1]int - p1k *[1000]int + p1 *[1]int + p1k *[1000]int p100k *[100000]int - i int - ui uint - i8 int8 - ui8 uint8 - i16 int16 + i int + ui uint + i8 int8 + ui8 uint8 + i16 int16 ui16 uint16 - i32 int32 + i32 int32 ui32 uint32 - i64 int64 + i64 int64 ui64 uint64 ) @@ -61,11 +61,11 @@ func main() { // Unsigned 8-bit numbers don't need checks for len >= 2⁸. use(s[ui8]) use(a1[ui8]) - use(a1k[ui8]) // ERROR "index bounds check elided" - use(a100k[ui8]) // ERROR "index bounds check elided" + use(a1k[ui8]) // ERROR "index bounds check elided" + use(a100k[ui8]) // ERROR "index bounds check elided" use(p1[ui8]) - use(p1k[ui8]) // ERROR "index bounds check elided" - use(p100k[ui8]) // ERROR "index bounds check elided" + use(p1k[ui8]) // ERROR "index bounds check elided" + use(p100k[ui8]) // ERROR "index bounds check elided" use(s[i16]) use(a1[i16]) @@ -79,10 +79,10 @@ func main() { use(s[ui16]) use(a1[ui16]) use(a1k[ui16]) - use(a100k[ui16]) // ERROR "index bounds check elided" + use(a100k[ui16]) // ERROR "index bounds check elided" use(p1[ui16]) use(p1k[ui16]) - use(p100k[ui16]) // ERROR "index bounds check elided" + use(p100k[ui16]) // ERROR "index bounds check elided" use(s[i32]) use(a1[i32]) @@ -128,11 +128,11 @@ func main() { use(s[ui%999]) use(a1[ui%999]) - use(a1k[ui%999]) // ERROR "index bounds check elided" - use(a100k[ui%999]) // ERROR "index bounds check elided" + use(a1k[ui%999]) // ERROR "index bounds check elided" + use(a100k[ui%999]) // ERROR "index bounds check elided" use(p1[ui%999]) - use(p1k[ui%999]) // ERROR "index bounds check elided" - use(p100k[ui%999]) // ERROR "index bounds check elided" + use(p1k[ui%999]) // ERROR "index bounds check elided" + use(p100k[ui%999]) // ERROR "index bounds check elided" use(s[i%1000]) use(a1[i%1000]) @@ -144,11 +144,11 @@ func main() { use(s[ui%1000]) use(a1[ui%1000]) - use(a1k[ui%1000]) // ERROR "index bounds check elided" - use(a100k[ui%1000]) // ERROR "index bounds check elided" + use(a1k[ui%1000]) // ERROR "index bounds check elided" + use(a100k[ui%1000]) // ERROR "index bounds check elided" use(p1[ui%1000]) - use(p1k[ui%1000]) // ERROR "index bounds check elided" - use(p100k[ui%1000]) // ERROR "index bounds check elided" + use(p1k[ui%1000]) // ERROR "index bounds check elided" + use(p100k[ui%1000]) // ERROR "index bounds check elided" use(s[i%1001]) use(a1[i%1001]) @@ -161,45 +161,45 @@ func main() { use(s[ui%1001]) use(a1[ui%1001]) use(a1k[ui%1001]) - use(a100k[ui%1001]) // ERROR "index bounds check elided" + use(a100k[ui%1001]) // ERROR "index bounds check elided" use(p1[ui%1001]) use(p1k[ui%1001]) - use(p100k[ui%1001]) // ERROR "index bounds check elided" + use(p100k[ui%1001]) // ERROR "index bounds check elided" // Bitwise and truncates the maximum value to the mask value. // The result (for a positive mask) cannot be negative, so elision // applies to both signed and unsigned indexes. use(s[i&999]) use(a1[i&999]) - use(a1k[i&999]) // ERROR "index bounds check elided" - use(a100k[i&999]) // ERROR "index bounds check elided" + use(a1k[i&999]) // ERROR "index bounds check elided" + use(a100k[i&999]) // ERROR "index bounds check elided" use(p1[i&999]) - use(p1k[i&999]) // ERROR "index bounds check elided" - use(p100k[i&999]) // ERROR "index bounds check elided" + use(p1k[i&999]) // ERROR "index bounds check elided" + use(p100k[i&999]) // ERROR "index bounds check elided" use(s[ui&999]) use(a1[ui&999]) - use(a1k[ui&999]) // ERROR "index bounds check elided" - use(a100k[ui&999]) // ERROR "index bounds check elided" + use(a1k[ui&999]) // ERROR "index bounds check elided" + use(a100k[ui&999]) // ERROR "index bounds check elided" use(p1[ui&999]) - use(p1k[ui&999]) // ERROR "index bounds check elided" - use(p100k[ui&999]) // ERROR "index bounds check elided" + use(p1k[ui&999]) // ERROR "index bounds check elided" + use(p100k[ui&999]) // ERROR "index bounds check elided" use(s[i&1000]) use(a1[i&1000]) use(a1k[i&1000]) - use(a100k[i&1000]) // ERROR "index bounds check elided" + use(a100k[i&1000]) // ERROR "index bounds check elided" use(p1[i&1000]) use(p1k[i&1000]) - use(p100k[i&1000]) // ERROR "index bounds check elided" + use(p100k[i&1000]) // ERROR "index bounds check elided" use(s[ui&1000]) use(a1[ui&1000]) use(a1k[ui&1000]) - use(a100k[ui&1000]) // ERROR "index bounds check elided" + use(a100k[ui&1000]) // ERROR "index bounds check elided" use(p1[ui&1000]) use(p1k[ui&1000]) - use(p100k[ui&1000]) // ERROR "index bounds check elided" + use(p100k[ui&1000]) // ERROR "index bounds check elided" // Right shift cuts the effective number of bits in the index, // but only for unsigned (signed stays negative). @@ -214,10 +214,10 @@ func main() { use(s[ui32>>22]) use(a1[ui32>>22]) use(a1k[ui32>>22]) - use(a100k[ui32>>22]) // ERROR "index bounds check elided" + use(a100k[ui32>>22]) // ERROR "index bounds check elided" use(p1[ui32>>22]) use(p1k[ui32>>22]) - use(p100k[ui32>>22]) // ERROR "index bounds check elided" + use(p100k[ui32>>22]) // ERROR "index bounds check elided" use(s[i32>>23]) use(a1[i32>>23]) @@ -229,11 +229,11 @@ func main() { use(s[ui32>>23]) use(a1[ui32>>23]) - use(a1k[ui32>>23]) // ERROR "index bounds check elided" - use(a100k[ui32>>23]) // ERROR "index bounds check elided" + use(a1k[ui32>>23]) // ERROR "index bounds check elided" + use(a100k[ui32>>23]) // ERROR "index bounds check elided" use(p1[ui32>>23]) - use(p1k[ui32>>23]) // ERROR "index bounds check elided" - use(p100k[ui32>>23]) // ERROR "index bounds check elided" + use(p1k[ui32>>23]) // ERROR "index bounds check elided" + use(p100k[ui32>>23]) // ERROR "index bounds check elided" // Division cuts the range like right shift does. use(s[i/1e6]) @@ -263,7 +263,7 @@ func main() { use(p1[ui/1e7]) } -var sum int +var sum int func use(x int) { sum += x diff --git a/test/chancap.go b/test/chancap.go index b08478a13..9675e38bd 100644 --- a/test/chancap.go +++ b/test/chancap.go @@ -42,11 +42,10 @@ func main() { shouldPanic("makechan: size out of range", func() { _ = make(T, n) }) shouldPanic("makechan: size out of range", func() { _ = make(T, int64(n)) }) if ptrSize == 8 { - n = 1 << 20 - n <<= 20 - shouldPanic("makechan: size out of range", func() { _ = make(T, n) }) - n <<= 20 - shouldPanic("makechan: size out of range", func() { _ = make(T, n) }) + var n2 int64 = 1 << 50 + shouldPanic("makechan: size out of range", func() { _ = make(T, int(n2)) }) + n2 = 1<<63 - 1 + shouldPanic("makechan: size out of range", func() { _ = make(T, int(n2)) }) } else { n = 1<<31 - 1 shouldPanic("makechan: size out of range", func() { _ = make(T, n) }) diff --git a/test/closure3.dir/main.go b/test/closure3.dir/main.go index 436434316..e382ad980 100644 --- a/test/closure3.dir/main.go +++ b/test/closure3.dir/main.go @@ -15,12 +15,12 @@ func main() { if x := func() int { // ERROR "can inline main.func1" return 1 }(); x != 1 { // ERROR "inlining call to main.func1" - panic("x != 1") + ppanic("x != 1") } if x := func() int { // ERROR "can inline main.func2" "func literal does not escape" return 1 }; x() != 1 { // ERROR "inlining call to main.func2" - panic("x() != 1") + ppanic("x() != 1") } } @@ -28,12 +28,12 @@ func main() { if y := func(x int) int { // ERROR "can inline main.func3" return x + 2 }(40); y != 42 { // ERROR "inlining call to main.func3" - panic("y != 42") + ppanic("y != 42") } if y := func(x int) int { // ERROR "can inline main.func4" "func literal does not escape" return x + 2 }; y(40) != 42 { // ERROR "inlining call to main.func4" - panic("y(40) != 42") + ppanic("y(40) != 42") } } @@ -45,7 +45,7 @@ func main() { return x + 1 } if y(40) != 41 { - panic("y(40) != 41") + ppanic("y(40) != 41") } } @@ -58,7 +58,7 @@ func main() { return x + 1 } if y(40) != 41 { - panic("y(40) != 41") + ppanic("y(40) != 41") } }() } @@ -71,7 +71,7 @@ func main() { return x + 1 }, 42 if y(40) != 41 { - panic("y(40) != 41") + ppanic("y(40) != 41") } } @@ -84,7 +84,7 @@ func main() { return x + 1 }, 42 if y(40) != 41 { - panic("y(40) != 41") + ppanic("y(40) != 41") } }() } @@ -93,13 +93,13 @@ func main() { y := func(x int) int { // ERROR "can inline main.func11" "func literal does not escape" return x + 2 } - y, sink = func() (func(int)int, int) { // ERROR "func literal does not escape" + y, sink = func() (func(int) int, int) { // ERROR "func literal does not escape" return func(x int) int { // ERROR "can inline main.func12" "func literal escapes" return x + 1 }, 42 }() if y(40) != 41 { - panic("y(40) != 41") + ppanic("y(40) != 41") } } @@ -114,7 +114,7 @@ func main() { }, 42 }() if y(40) != 41 { - panic("y(40) != 41") + ppanic("y(40) != 41") } }() } @@ -123,11 +123,11 @@ func main() { y := func(x int) int { // ERROR "can inline main.func14" "func literal does not escape" return x + 2 } - y, ok = map[int]func(int)int { // ERROR "does not escape" - 0: func (x int) int { return x + 1 }, // ERROR "can inline main.func15" "func literal escapes" + y, ok = map[int]func(int) int{ // ERROR "does not escape" + 0: func(x int) int { return x + 1 }, // ERROR "can inline main.func15" "func literal escapes" }[0] if y(40) != 41 { - panic("y(40) != 41") + ppanic("y(40) != 41") } } @@ -136,11 +136,11 @@ func main() { y := func(x int) int { // ERROR "can inline main.func16.1" "func literal does not escape" return x + 2 } - y, ok = map[int]func(int) int{// ERROR "does not escape" + y, ok = map[int]func(int) int{ // ERROR "does not escape" 0: func(x int) int { return x + 1 }, // ERROR "can inline main.func16.2" "func literal escapes" }[0] if y(40) != 41 { - panic("y(40) != 41") + ppanic("y(40) != 41") } }() } @@ -149,11 +149,11 @@ func main() { y := func(x int) int { // ERROR "can inline main.func17" "func literal does not escape" return x + 2 } - y, ok = interface{}(func (x int) int { // ERROR "can inline main.func18" "does not escape" + y, ok = interface{}(func(x int) int { // ERROR "can inline main.func18" "does not escape" return x + 1 - }).(func(int)int) + }).(func(int) int) if y(40) != 41 { - panic("y(40) != 41") + ppanic("y(40) != 41") } } @@ -166,7 +166,7 @@ func main() { return x + 1 }).(func(int) int) if y(40) != 41 { - panic("y(40) != 41") + ppanic("y(40) != 41") } }() } @@ -176,12 +176,12 @@ func main() { if y := func() int { // ERROR "can inline main.func20" return x }(); y != 42 { // ERROR "inlining call to main.func20" - panic("y != 42") + ppanic("y != 42") } if y := func() int { // ERROR "can inline main.func21" "func literal does not escape" return x }; y() != 42 { // ERROR "inlining call to main.func21" - panic("y() != 42") + ppanic("y() != 42") } } @@ -192,14 +192,14 @@ func main() { return x + y }() // ERROR "inlining call to main.func22.1" }(1); z != 43 { - panic("z != 43") + ppanic("z != 43") } if z := func(y int) int { // ERROR "func literal does not escape" return func() int { // ERROR "can inline main.func23.1" return x + y }() // ERROR "inlining call to main.func23.1" }; z(1) != 43 { - panic("z(1) != 43") + ppanic("z(1) != 43") } } @@ -211,7 +211,7 @@ func main() { }() // ERROR "inlining call to main.func24" "&a does not escape" }() if a != 2 { - panic("a != 2") + ppanic("a != 2") } } @@ -222,11 +222,11 @@ func main() { b = 3 }() // ERROR "inlining call to main.func25.1" "&b does not escape" if b != 3 { - panic("b != 3") + ppanic("b != 3") } }(b) if b != 2 { - panic("b != 2") + ppanic("b != 2") } } @@ -236,12 +236,12 @@ func main() { c = 4 func() { // ERROR "func literal does not escape" if c != 4 { - panic("c != 4") + ppanic("c != 4") } }() }() if c != 4 { - panic("c != 4") + ppanic("c != 4") } } @@ -256,7 +256,7 @@ func main() { }(10) // ERROR "inlining call to main.func27.1.1" }(100) }(1000); r != 2350 { - panic("r != 2350") + ppanic("r != 2350") } } @@ -274,10 +274,15 @@ func main() { return a + c }(100) + b }(1000); r != 2350 { - panic("r != 2350") + ppanic("r != 2350") } if a != 2000 { - panic("a != 2000") + ppanic("a != 2000") } } } + +//go:noinline +func ppanic(s string) { // ERROR "leaking param: s" + panic(s) +} diff --git a/test/closure4.go b/test/closure4.go new file mode 100644 index 000000000..ec4e0a18e --- /dev/null +++ b/test/closure4.go @@ -0,0 +1,21 @@ +// run + +// Copyright 2018 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. + +// Check that calling a nil func causes a proper panic. + +package main + +func main() { + defer func() { + err := recover() + if err == nil { + panic("panic expected") + } + }() + + var f func() + f() +} diff --git a/test/codegen/README b/test/codegen/README new file mode 100644 index 000000000..298d807bd --- /dev/null +++ b/test/codegen/README @@ -0,0 +1,147 @@ +// Copyright 2018 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. + +The codegen directory contains code generation tests for the gc +compiler. + + +- Introduction + +The test harness compiles Go code inside files in this directory and +then matches the generated assembly (the output of `go tool compile -S`) +against a set of regexps specified in comments that follow a special +syntax (described below). The test driver is implemented as a step of +the top-level test/run.go suite, called "asmcheck". + +The codegen tests run during all.bash, but can also be run in +isolation by using + + $ ../bin/go run run.go -v codegen + +in the top-level test directory. + +The test harness compiles the tests with the same go toolchain that is +used to run run.go. After writing tests for a newly added codegen +transformation, it can be useful to first run the test harness with a +toolchain from a released Go version (and verify that the new tests +fail), and then re-runnig the tests using the devel toolchain. + + +- Regexps comments syntax + +Instructions to match are specified inside plain comments that start +with an architecture tag, followed by a colon and a quoted Go-style +regexp to be matched. For example, the following test: + + func Sqrt(x float64) float64 { + // amd64:"SQRTSD" + // arm64:"FSQRTD" + return math.Sqrt(x) + } + +verifies that math.Sqrt calls are intrinsified to a SQRTSD instruction +on amd64, and to a FSQRTD instruction on arm64. + +It is possible to put multiple architectures checks into the same +line, as: + + // amd64:"SQRTSD" arm64:"FSQRTD" + +although this form should be avoided when doing so would make the +regexps line excessively long and difficult to read. + +Comments that are on their own line will be matched against the first +subsequent non-comment line. Inline comments are also supported; the +regexp will be matched against the code found on the same line: + + func Sqrt(x float64) float64 { + return math.Sqrt(x) // arm:"SQRTD" + } + +It's possible to specify a comma-separated list of regexps to be +matched. For example, the following test: + + func TZ8(n uint8) int { + // amd64:"BSFQ","ORQ\t\\$256" + return bits.TrailingZeros8(n) + } + +verifies that the code generated for a bits.TrailingZeros8 call on +amd64 contains both a "BSFQ" instruction and an "ORQ $256". + +Note how the ORQ regex includes a tab char (\t). In the Go assembly +syntax, operands are separated from opcodes by a tabulation. + +Regexps can be quoted using either " or `. Special characters must be +escaped accordingly. Both of these are accepted, and equivalent: + + // amd64:"ADDQ\t\\$3" + // amd64:`ADDQ\t\$3` + +and they'll match this assembly line: + + ADDQ $3 + +Negative matches can be specified using a - before the quoted regexp. +For example: + + func MoveSmall() { + x := [...]byte{1, 2, 3, 4, 5, 6, 7} + copy(x[1:], x[:]) // arm64:-".*memmove" + } + +verifies that NO memmove call is present in the assembly generated for +the copy() line. + +- Architecture specifiers + +There are three different ways to specify on which architecture a test +should be run: + +* Specify only the architecture (eg: "amd64"). This indicates that the + check should be run on all the supported architecture variants. For + instance, arm checks will be run against all supported GOARM + variations (5,6,7). +* Specify both the architecture and a variant, separated by a slash + (eg: "arm/7"). This means that the check will be run only on that + specific variant. +* Specify the operating system, the architecture and the variant, + separated by slashes (eg: "plan9/386/sse2", "plan9/amd64/"). This is + needed in the rare case that you need to do a codegen test affected + by a specific operating system; by default, tests are compiled only + targeting linux. + + +- Remarks, and Caveats + +-- Write small test functions + +As a general guideline, test functions should be small, to avoid +possible interactions between unrelated lines of code that may be +introduced, for example, by the compiler's optimization passes. + +Any given line of Go code could get assigned more instructions that it +may appear from reading the source. In particular, matching all MOV +instructions should be avoided; the compiler may add them for +unrelated reasons and this may render the test ineffective. + +-- Line matching logic + +Regexps are always matched from the start of the instructions line. +This means, for example, that the "MULQ" regexp is equivalent to +"^MULQ" (^ representing the start of the line), and it will NOT match +the following assembly line: + + IMULQ $99, AX + +To force a match at any point of the line, ".*MULQ" should be used. + +For the same reason, a negative regexp like -"memmove" is not enough +to make sure that no memmove call is included in the assembly. A +memmove call looks like this: + + CALL runtime.memmove(SB) + +To make sure that the "memmove" symbol does not appear anywhere in the +assembly, the negative regexp to be used is -".*memmove". diff --git a/test/codegen/arithmetic.go b/test/codegen/arithmetic.go new file mode 100644 index 000000000..f358020f5 --- /dev/null +++ b/test/codegen/arithmetic.go @@ -0,0 +1,184 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +// This file contains codegen tests related to arithmetic +// simplifications and optimizations on integer types. +// For codegen tests on float types, see floats.go. + +// ----------------- // +// Subtraction // +// ----------------- // + +func SubMem(arr []int, b int) int { + // 386:`SUBL\s[A-Z]+,\s8\([A-Z]+\)` + arr[2] -= b + // 386:`SUBL\s[A-Z]+,\s12\([A-Z]+\)` + arr[3] -= b + // 386:"SUBL\t4" + // amd64:"SUBQ\t8" + return arr[0] - arr[1] +} + +// -------------------- // +// Multiplication // +// -------------------- // + +func Pow2Muls(n1, n2 int) (int, int) { + // amd64:"SHLQ\t[$]5",-"IMULQ" + // 386:"SHLL\t[$]5",-"IMULL" + // arm:"SLL\t[$]5",-"MUL" + // arm64:"LSL\t[$]5",-"MUL" + a := n1 * 32 + + // amd64:"SHLQ\t[$]6",-"IMULQ" + // 386:"SHLL\t[$]6",-"IMULL" + // arm:"SLL\t[$]6",-"MUL" + // arm64:"LSL\t[$]6",-"MUL" + b := -64 * n2 + + return a, b +} + +func Mul_96(n int) int { + // amd64:`SHLQ\t[$]5`,`LEAQ\t\(.*\)\(.*\*2\),` + return n * 96 +} + +// Multiplications merging tests + +func MergeMuls1(n int) int { + // amd64:"IMUL3Q\t[$]46" + // 386:"IMUL3L\t[$]46" + return 15*n + 31*n // 46n +} + +func MergeMuls2(n int) int { + // amd64:"IMUL3Q\t[$]23","ADDQ\t[$]29" + // 386:"IMUL3L\t[$]23","ADDL\t[$]29" + return 5*n + 7*(n+1) + 11*(n+2) // 23n + 29 +} + +func MergeMuls3(a, n int) int { + // amd64:"ADDQ\t[$]19",-"IMULQ\t[$]19" + // 386:"ADDL\t[$]19",-"IMULL\t[$]19" + return a*n + 19*n // (a+19)n +} + +func MergeMuls4(n int) int { + // amd64:"IMUL3Q\t[$]14" + // 386:"IMUL3L\t[$]14" + return 23*n - 9*n // 14n +} + +func MergeMuls5(a, n int) int { + // amd64:"ADDQ\t[$]-19",-"IMULQ\t[$]19" + // 386:"ADDL\t[$]-19",-"IMULL\t[$]19" + return a*n - 19*n // (a-19)n +} + +// -------------- // +// Division // +// -------------- // + +func Pow2Divs(n1 uint, n2 int) (uint, int) { + // 386:"SHRL\t[$]5",-"DIVL" + // amd64:"SHRQ\t[$]5",-"DIVQ" + // arm:"SRL\t[$]5",-".*udiv" + // arm64:"LSR\t[$]5",-"UDIV" + a := n1 / 32 // unsigned + + // amd64:"SARQ\t[$]6",-"IDIVQ" + // 386:"SARL\t[$]6",-"IDIVL" + // arm:"SRA\t[$]6",-".*udiv" + // arm64:"ASR\t[$]6",-"SDIV" + b := n2 / 64 // signed + + return a, b +} + +// Check that constant divisions get turned into MULs +func ConstDivs(n1 uint, n2 int) (uint, int) { + // amd64:"MOVQ\t[$]-1085102592571150095","MULQ",-"DIVQ" + a := n1 / 17 // unsigned + + // amd64:"MOVQ\t[$]-1085102592571150095","IMULQ",-"IDIVQ" + b := n2 / 17 // signed + + return a, b +} + +func Pow2Mods(n1 uint, n2 int) (uint, int) { + // 386:"ANDL\t[$]31",-"DIVL" + // amd64:"ANDQ\t[$]31",-"DIVQ" + // arm:"AND\t[$]31",-".*udiv" + // arm64:"AND\t[$]31",-"UDIV" + a := n1 % 32 // unsigned + + // 386:-"IDIVL" + // amd64:-"IDIVQ" + // arm:-".*udiv" + // arm64:-"REM" + b := n2 % 64 // signed + + return a, b +} + +// Check that constant modulo divs get turned into MULs +func ConstMods(n1 uint, n2 int) (uint, int) { + // amd64:"MOVQ\t[$]-1085102592571150095","MULQ",-"DIVQ" + a := n1 % 17 // unsigned + + // amd64:"MOVQ\t[$]-1085102592571150095","IMULQ",-"IDIVQ" + b := n2 % 17 // signed + + return a, b +} + +// Check that len() and cap() calls divided by powers of two are +// optimized into shifts and ands + +func LenDiv1(a []int) int { + // 386:"SHRL\t[$]10" + // amd64:"SHRQ\t[$]10" + return len(a) / 1024 +} + +func LenDiv2(s string) int { + // 386:"SHRL\t[$]11" + // amd64:"SHRQ\t[$]11" + return len(s) / (4097 >> 1) +} + +func LenMod1(a []int) int { + // 386:"ANDL\t[$]1023" + // amd64:"ANDQ\t[$]1023" + return len(a) % 1024 +} + +func LenMod2(s string) int { + // 386:"ANDL\t[$]2047" + // amd64:"ANDQ\t[$]2047" + return len(s) % (4097 >> 1) +} + +func CapDiv(a []int) int { + // 386:"SHRL\t[$]12" + // amd64:"SHRQ\t[$]12" + return cap(a) / ((1 << 11) + 2048) +} + +func CapMod(a []int) int { + // 386:"ANDL\t[$]4095" + // amd64:"ANDQ\t[$]4095" + return cap(a) % ((1 << 11) + 2048) +} + +func AddMul(x int) int { + // amd64:"LEAQ\t1" + return 2*x + 1 +} diff --git a/test/codegen/bitfield.go b/test/codegen/bitfield.go new file mode 100644 index 000000000..08788f144 --- /dev/null +++ b/test/codegen/bitfield.go @@ -0,0 +1,232 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +// This file contains codegen tests related to bit field +// insertion/extraction simplifications/optimizations. + +func extr1(x, x2 uint64) uint64 { + return x<<7 + x2>>57 // arm64:"EXTR\t[$]57," +} + +func extr2(x, x2 uint64) uint64 { + return x<<7 | x2>>57 // arm64:"EXTR\t[$]57," +} + +func extr3(x, x2 uint64) uint64 { + return x<<7 ^ x2>>57 // arm64:"EXTR\t[$]57," +} + +func extr4(x, x2 uint32) uint32 { + return x<<7 + x2>>25 // arm64:"EXTRW\t[$]25," +} + +func extr5(x, x2 uint32) uint32 { + return x<<7 | x2>>25 // arm64:"EXTRW\t[$]25," +} + +func extr6(x, x2 uint32) uint32 { + return x<<7 ^ x2>>25 // arm64:"EXTRW\t[$]25," +} + +// check 32-bit shift masking +func mask32(x uint32) uint32 { + return (x << 29) >> 29 // arm64:"AND\t[$]7, R[0-9]+",-"LSR",-"LSL" +} + +// check 16-bit shift masking +func mask16(x uint16) uint16 { + return (x << 14) >> 14 // arm64:"AND\t[$]3, R[0-9]+",-"LSR",-"LSL" +} + +// check 8-bit shift masking +func mask8(x uint8) uint8 { + return (x << 7) >> 7 // arm64:"AND\t[$]1, R[0-9]+",-"LSR",-"LSL" +} + +func maskshift(x uint64) uint64 { + // arm64:"AND\t[$]4095, R[0-9]+",-"LSL",-"LSR",-"UBFIZ",-"UBFX" + return ((x << 5) & (0xfff << 5)) >> 5 +} + +// bitfield ops +// bfi +func bfi1(x, y uint64) uint64 { + // arm64:"BFI\t[$]4, R[0-9]+, [$]12",-"LSL",-"LSR",-"AND" + return ((x & 0xfff) << 4) | (y & 0xffffffffffff000f) +} + +func bfi2(x, y uint64) uint64 { + // arm64:"BFI\t[$]12, R[0-9]+, [$]40",-"LSL",-"LSR",-"AND" + return (x << 24 >> 12) | (y & 0xfff0000000000fff) +} + +// bfxil +func bfxil1(x, y uint64) uint64 { + // arm64:"BFXIL\t[$]5, R[0-9]+, [$]12",-"LSL",-"LSR",-"AND" + return ((x >> 5) & 0xfff) | (y & 0xfffffffffffff000) +} + +func bfxil2(x, y uint64) uint64 { + // arm64:"BFXIL\t[$]12, R[0-9]+, [$]40",-"LSL",-"LSR",-"AND" + return (x << 12 >> 24) | (y & 0xffffff0000000000) +} + +// sbfiz +func sbfiz1(x int64) int64 { + // arm64:"SBFIZ\t[$]1, R[0-9]+, [$]60",-"LSL",-"ASR" + return (x << 4) >> 3 +} + +func sbfiz2(x int32) int64 { + return int64(x << 3) // arm64:"SBFIZ\t[$]3, R[0-9]+, [$]29",-"LSL" +} + +func sbfiz3(x int16) int64 { + return int64(x << 3) // arm64:"SBFIZ\t[$]3, R[0-9]+, [$]13",-"LSL" +} + +func sbfiz4(x int8) int64 { + return int64(x << 3) // arm64:"SBFIZ\t[$]3, R[0-9]+, [$]5",-"LSL" +} + +func sbfiz5(x int32) int32 { + // arm64:"SBFIZ\t[$]1, R[0-9]+, [$]28",-"LSL",-"ASR" + return (x << 4) >> 3 +} + +// sbfx +func sbfx1(x int64) int64 { + return (x << 3) >> 4 // arm64:"SBFX\t[$]1, R[0-9]+, [$]60",-"LSL",-"ASR" +} + +func sbfx2(x int64) int64 { + return (x << 60) >> 60 // arm64:"SBFX\tZR, R[0-9]+, [$]4",-"LSL",-"ASR" +} + +func sbfx3(x int32) int64 { + return int64(x) >> 3 // arm64:"SBFX\t[$]3, R[0-9]+, [$]29",-"ASR" +} + +func sbfx4(x int16) int64 { + return int64(x) >> 3 // arm64:"SBFX\t[$]3, R[0-9]+, [$]13",-"ASR" +} + +func sbfx5(x int8) int64 { + return int64(x) >> 3 // arm64:"SBFX\t[$]3, R[0-9]+, [$]5",-"ASR" +} + +func sbfx6(x int32) int32 { + return (x << 3) >> 4 // arm64:"SBFX\t[$]1, R[0-9]+, [$]28",-"LSL",-"ASR" +} + +// ubfiz +func ubfiz1(x uint64) uint64 { + // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]12",-"LSL",-"AND" + return (x & 0xfff) << 3 +} + +func ubfiz2(x uint64) uint64 { + // arm64:"UBFIZ\t[$]4, R[0-9]+, [$]12",-"LSL",-"AND" + return (x << 4) & 0xfff0 +} + +func ubfiz3(x uint32) uint64 { + return uint64(x+1) << 3 // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]32",-"LSL" +} + +func ubfiz4(x uint16) uint64 { + return uint64(x+1) << 3 // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]16",-"LSL" +} + +func ubfiz5(x uint8) uint64 { + return uint64(x+1) << 3 // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]8",-"LSL" +} + +func ubfiz6(x uint64) uint64 { + // arm64:"UBFIZ\t[$]1, R[0-9]+, [$]60",-"LSL",-"LSR" + return (x << 4) >> 3 +} + +func ubfiz7(x uint32) uint32 { + // arm64:"UBFIZ\t[$]1, R[0-9]+, [$]28",-"LSL",-"LSR" + return (x << 4) >> 3 +} + +func ubfiz8(x uint64) uint64 { + // arm64:"UBFIZ\t[$]1, R[0-9]+, [$]20",-"LSL",-"LSR" + return ((x & 0xfffff) << 4) >> 3 +} + +func ubfiz9(x uint64) uint64 { + // arm64:"UBFIZ\t[$]5, R[0-9]+, [$]13",-"LSL",-"LSR",-"AND" + return ((x << 3) & 0xffff) << 2 +} + +func ubfiz10(x uint64) uint64 { + // arm64:"UBFIZ\t[$]7, R[0-9]+, [$]12",-"LSL",-"LSR",-"AND" + return ((x << 5) & (0xfff << 5)) << 2 +} + +// ubfx +func ubfx1(x uint64) uint64 { + // arm64:"UBFX\t[$]25, R[0-9]+, [$]10",-"LSR",-"AND" + return (x >> 25) & 1023 +} + +func ubfx2(x uint64) uint64 { + // arm64:"UBFX\t[$]4, R[0-9]+, [$]8",-"LSR",-"AND" + return (x & 0x0ff0) >> 4 +} + +func ubfx3(x uint32) uint64 { + return uint64(x >> 15) // arm64:"UBFX\t[$]15, R[0-9]+, [$]17",-"LSR" +} + +func ubfx4(x uint16) uint64 { + return uint64(x >> 9) // arm64:"UBFX\t[$]9, R[0-9]+, [$]7",-"LSR" +} + +func ubfx5(x uint8) uint64 { + return uint64(x >> 3) // arm64:"UBFX\t[$]3, R[0-9]+, [$]5",-"LSR" +} + +func ubfx6(x uint64) uint64 { + return (x << 1) >> 2 // arm64:"UBFX\t[$]1, R[0-9]+, [$]62",-"LSL",-"LSR" +} + +func ubfx7(x uint32) uint32 { + return (x << 1) >> 2 // arm64:"UBFX\t[$]1, R[0-9]+, [$]30",-"LSL",-"LSR" +} + +func ubfx8(x uint64) uint64 { + // arm64:"UBFX\t[$]1, R[0-9]+, [$]12",-"LSL",-"LSR",-"AND" + return ((x << 1) >> 2) & 0xfff +} + +func ubfx9(x uint64) uint64 { + // arm64:"UBFX\t[$]4, R[0-9]+, [$]11",-"LSL",-"LSR",-"AND" + return ((x >> 3) & 0xfff) >> 1 +} + +func ubfx10(x uint64) uint64 { + // arm64:"UBFX\t[$]5, R[0-9]+, [$]56",-"LSL",-"LSR" + return ((x >> 2) << 5) >> 8 +} + +func ubfx11(x uint64) uint64 { + // arm64:"UBFX\t[$]1, R[0-9]+, [$]19",-"LSL",-"LSR" + return ((x & 0xfffff) << 3) >> 4 +} + +// Check that we don't emit comparisons for constant shifts. +//go:nosplit +func shift_no_cmp(x int) int { + // arm64:`LSL\t[$]17`,-`CMP` + // mips64:`SLLV\t[$]17`,-`SGT` + return x << 17 +} diff --git a/test/codegen/bits.go b/test/codegen/bits.go new file mode 100644 index 000000000..9de2201cb --- /dev/null +++ b/test/codegen/bits.go @@ -0,0 +1,292 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +/************************************ + * 64-bit instructions + ************************************/ + +func bitcheck64_constleft(a uint64) (n int) { + // amd64:"BTQ\t[$]63" + if a&(1<<63) != 0 { + return 1 + } + // amd64:"BTQ\t[$]60" + if a&(1<<60) != 0 { + return 1 + } + // amd64:"BTL\t[$]0" + if a&(1<<0) != 0 { + return 1 + } + return 0 +} + +func bitcheck64_constright(a [8]uint64) (n int) { + // amd64:"BTQ\t[$]63" + if (a[0]>>63)&1 != 0 { + return 1 + } + // amd64:"BTQ\t[$]63" + if a[1]>>63 != 0 { + return 1 + } + // amd64:"BTQ\t[$]63" + if a[2]>>63 == 0 { + return 1 + } + // amd64:"BTQ\t[$]60" + if (a[3]>>60)&1 == 0 { + return 1 + } + // amd64:"BTL\t[$]1" + if (a[4]>>1)&1 == 0 { + return 1 + } + // amd64:"BTL\t[$]0" + if (a[5]>>0)&1 == 0 { + return 1 + } + // amd64:"BTL\t[$]7" + if (a[6]>>5)&4 == 0 { + return 1 + } + return 0 +} + +func bitcheck64_var(a, b uint64) (n int) { + // amd64:"BTQ" + if a&(1<<(b&63)) != 0 { + return 1 + } + // amd64:"BTQ",-"BT.\t[$]0" + if (b>>(a&63))&1 != 0 { + return 1 + } + return 0 +} + +func bitcheck64_mask(a uint64) (n int) { + // amd64:"BTQ\t[$]63" + if a&0x8000000000000000 != 0 { + return 1 + } + // amd64:"BTQ\t[$]59" + if a&0x800000000000000 != 0 { + return 1 + } + // amd64:"BTL\t[$]0" + if a&0x1 != 0 { + return 1 + } + return 0 +} + +func biton64(a, b uint64) (n uint64) { + // amd64:"BTSQ" + n += b | (1 << (a & 63)) + + // amd64:"BTSQ\t[$]63" + n += a | (1 << 63) + + // amd64:"BTSQ\t[$]60" + n += a | (1 << 60) + + // amd64:"ORQ\t[$]1" + n += a | (1 << 0) + + return n +} + +func bitoff64(a, b uint64) (n uint64) { + // amd64:"BTRQ" + n += b &^ (1 << (a & 63)) + + // amd64:"BTRQ\t[$]63" + n += a &^ (1 << 63) + + // amd64:"BTRQ\t[$]60" + n += a &^ (1 << 60) + + // amd64:"ANDQ\t[$]-2" + n += a &^ (1 << 0) + + return n +} + +func bitcompl64(a, b uint64) (n uint64) { + // amd64:"BTCQ" + n += b ^ (1 << (a & 63)) + + // amd64:"BTCQ\t[$]63" + n += a ^ (1 << 63) + + // amd64:"BTCQ\t[$]60" + n += a ^ (1 << 60) + + // amd64:"XORQ\t[$]1" + n += a ^ (1 << 0) + + return n +} + +/************************************ + * 32-bit instructions + ************************************/ + +func bitcheck32_constleft(a uint32) (n int) { + // amd64:"BTL\t[$]31" + if a&(1<<31) != 0 { + return 1 + } + // amd64:"BTL\t[$]28" + if a&(1<<28) != 0 { + return 1 + } + // amd64:"BTL\t[$]0" + if a&(1<<0) != 0 { + return 1 + } + return 0 +} + +func bitcheck32_constright(a [8]uint32) (n int) { + // amd64:"BTL\t[$]31" + if (a[0]>>31)&1 != 0 { + return 1 + } + // amd64:"BTL\t[$]31" + if a[1]>>31 != 0 { + return 1 + } + // amd64:"BTL\t[$]31" + if a[2]>>31 == 0 { + return 1 + } + // amd64:"BTL\t[$]28" + if (a[3]>>28)&1 == 0 { + return 1 + } + // amd64:"BTL\t[$]1" + if (a[4]>>1)&1 == 0 { + return 1 + } + // amd64:"BTL\t[$]0" + if (a[5]>>0)&1 == 0 { + return 1 + } + // amd64:"BTL\t[$]7" + if (a[6]>>5)&4 == 0 { + return 1 + } + return 0 +} + +func bitcheck32_var(a, b uint32) (n int) { + // amd64:"BTL" + if a&(1<<(b&31)) != 0 { + return 1 + } + // amd64:"BTL",-"BT.\t[$]0" + if (b>>(a&31))&1 != 0 { + return 1 + } + return 0 +} + +func bitcheck32_mask(a uint32) (n int) { + // amd64:"BTL\t[$]31" + if a&0x80000000 != 0 { + return 1 + } + // amd64:"BTL\t[$]27" + if a&0x8000000 != 0 { + return 1 + } + // amd64:"BTL\t[$]0" + if a&0x1 != 0 { + return 1 + } + return 0 +} + +func biton32(a, b uint32) (n uint32) { + // amd64:"BTSL" + n += b | (1 << (a & 31)) + + // amd64:"BTSL\t[$]31" + n += a | (1 << 31) + + // amd64:"BTSL\t[$]28" + n += a | (1 << 28) + + // amd64:"ORL\t[$]1" + n += a | (1 << 0) + + return n +} + +func bitoff32(a, b uint32) (n uint32) { + // amd64:"BTRL" + n += b &^ (1 << (a & 31)) + + // amd64:"BTRL\t[$]31" + n += a &^ (1 << 31) + + // amd64:"BTRL\t[$]28" + n += a &^ (1 << 28) + + // amd64:"ANDL\t[$]-2" + n += a &^ (1 << 0) + + return n +} + +func bitcompl32(a, b uint32) (n uint32) { + // amd64:"BTCL" + n += b ^ (1 << (a & 31)) + + // amd64:"BTCL\t[$]31" + n += a ^ (1 << 31) + + // amd64:"BTCL\t[$]28" + n += a ^ (1 << 28) + + // amd64:"XORL\t[$]1" + n += a ^ (1 << 0) + + return n +} + +// Check AND masking on arm64 (Issue #19857) + +func and_mask_1(a uint64) uint64 { + // arm64:`AND\t` + return a & ((1 << 63) - 1) +} + +func and_mask_2(a uint64) uint64 { + // arm64:`AND\t` + return a & (1 << 63) +} + +// Check generation of arm64 BIC/EON/ORN instructions + +func op_bic(x, y uint32) uint32 { + // arm64:`BIC\t`,-`AND` + return x &^ y +} + +func op_eon(x, y uint32) uint32 { + // arm64:`EON\t`,-`XOR` + return x ^ ^y +} + +func op_orn(x, y uint32) uint32 { + // arm64:`ORN\t`,-`ORR` + return x | ^y +} diff --git a/test/codegen/comparisons.go b/test/codegen/comparisons.go new file mode 100644 index 000000000..2f010bcba --- /dev/null +++ b/test/codegen/comparisons.go @@ -0,0 +1,149 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +import "unsafe" + +// This file contains code generation tests related to the comparison +// operators. + +// -------------- // +// Equality // +// -------------- // + +// Check that compare to constant string use 2/4/8 byte compares + +func CompareString1(s string) bool { + // amd64:`CMPW\t\(.*\), [$]` + // arm64:`MOVHU\t\(.*\), [R]`,`CMPW\t[$]` + // ppc64le:`MOVHZ\t\(.*\), [R]`,`CMPW\t.*, [$]` + // s390x:`MOVHBR\t\(.*\), [R]`,`CMPW\t.*, [$]` + return s == "xx" +} + +func CompareString2(s string) bool { + // amd64:`CMPL\t\(.*\), [$]` + // arm64:`MOVWU\t\(.*\), [R]`,`CMPW\t.*, [R]` + // ppc64le:`MOVWZ\t\(.*\), [R]`,`CMPW\t.*, [R]` + // s390x:`MOVWBR\t\(.*\), [R]`,`CMPW\t.*, [$]` + return s == "xxxx" +} + +func CompareString3(s string) bool { + // amd64:`CMPQ\t\(.*\), [A-Z]` + // arm64:-`CMPW\t` + // ppc64le:-`CMPW\t` + // s390x:-`CMPW\t` + return s == "xxxxxxxx" +} + +// Check that arrays compare use 2/4/8 byte compares + +func CompareArray1(a, b [2]byte) bool { + // amd64:`CMPW\t""[.+_a-z0-9]+\(SP\), [A-Z]` + // arm64:-`MOVBU\t` + // ppc64le:-`MOVBZ\t` + // s390x:-`MOVBZ\t` + return a == b +} + +func CompareArray2(a, b [3]uint16) bool { + // amd64:`CMPL\t""[.+_a-z0-9]+\(SP\), [A-Z]` + // amd64:`CMPW\t""[.+_a-z0-9]+\(SP\), [A-Z]` + return a == b +} + +func CompareArray3(a, b [3]int16) bool { + // amd64:`CMPL\t""[.+_a-z0-9]+\(SP\), [A-Z]` + // amd64:`CMPW\t""[.+_a-z0-9]+\(SP\), [A-Z]` + return a == b +} + +func CompareArray4(a, b [12]int8) bool { + // amd64:`CMPQ\t""[.+_a-z0-9]+\(SP\), [A-Z]` + // amd64:`CMPL\t""[.+_a-z0-9]+\(SP\), [A-Z]` + return a == b +} + +func CompareArray5(a, b [15]byte) bool { + // amd64:`CMPQ\t""[.+_a-z0-9]+\(SP\), [A-Z]` + return a == b +} + +// This was a TODO in mapaccess1_faststr +func CompareArray6(a, b unsafe.Pointer) bool { + // amd64:`CMPL\t\(.*\), [A-Z]` + // arm64:`MOVWU\t\(.*\), [R]`,`CMPW\t.*, [R]` + // ppc64le:`MOVWZ\t\(.*\), [R]`,`CMPW\t.*, [R]` + // s390x:`MOVWBR\t\(.*\), [R]`,`CMPW\t.*, [R]` + return *((*[4]byte)(a)) != *((*[4]byte)(b)) +} + +// -------------- // +// Ordering // +// -------------- // + +// Test that LEAQ/ADDQconst are folded into SETx ops + +func CmpFold(x uint32) bool { + // amd64:`SETHI\t.*\(SP\)` + return x > 4 +} + +// Test that direct comparisons with memory are generated when +// possible + +func CmpMem1(p int, q *int) bool { + // amd64:`CMPQ\t\(.*\), [A-Z]` + return p < *q +} + +func CmpMem2(p *int, q int) bool { + // amd64:`CMPQ\t\(.*\), [A-Z]` + return *p < q +} + +func CmpMem3(p *int) bool { + // amd64:`CMPQ\t\(.*\), [$]7` + return *p < 7 +} + +func CmpMem4(p *int) bool { + // amd64:`CMPQ\t\(.*\), [$]7` + return 7 < *p +} + +func CmpMem5(p **int) { + // amd64:`CMPL\truntime.writeBarrier\(SB\), [$]0` + *p = nil +} + +// Check tbz/tbnz are generated when comparing against zero on arm64 + +func CmpZero1(a int32, ptr *int) { + if a < 0 { // arm64:"TBZ" + *ptr = 0 + } +} + +func CmpZero2(a int64, ptr *int) { + if a < 0 { // arm64:"TBZ" + *ptr = 0 + } +} + +func CmpZero3(a int32, ptr *int) { + if a >= 0 { // arm64:"TBNZ" + *ptr = 0 + } +} + +func CmpZero4(a int64, ptr *int) { + if a >= 0 { // arm64:"TBNZ" + *ptr = 0 + } +} diff --git a/test/codegen/condmove.go b/test/codegen/condmove.go new file mode 100644 index 000000000..32039c16a --- /dev/null +++ b/test/codegen/condmove.go @@ -0,0 +1,182 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +func cmovint(c int) int { + x := c + 4 + if x < 0 { + x = 182 + } + // amd64:"CMOVQLT" + // arm64:"CSEL\tLT" + return x +} + +func cmovchan(x, y chan int) chan int { + if x != y { + x = y + } + // amd64:"CMOVQNE" + // arm64:"CSEL\tNE" + return x +} + +func cmovuintptr(x, y uintptr) uintptr { + if x < y { + x = -y + } + // amd64:"CMOVQCS" + // arm64:"CSEL\tLO" + return x +} + +func cmov32bit(x, y uint32) uint32 { + if x < y { + x = -y + } + // amd64:"CMOVLCS" + // arm64:"CSEL\tLO" + return x +} + +func cmov16bit(x, y uint16) uint16 { + if x < y { + x = -y + } + // amd64:"CMOVWCS" + // arm64:"CSEL\tLO" + return x +} + +// Floating point comparison. For EQ/NE, we must +// generate special code to handle NaNs. +func cmovfloateq(x, y float64) int { + a := 128 + if x == y { + a = 256 + } + // amd64:"CMOVQNE","CMOVQPC" + // arm64:"CSEL\tEQ" + return a +} + +func cmovfloatne(x, y float64) int { + a := 128 + if x != y { + a = 256 + } + // amd64:"CMOVQNE","CMOVQPS" + // arm64:"CSEL\tNE" + return a +} + +//go:noinline +func frexp(f float64) (frac float64, exp int) { + return 1.0, 4 +} + +//go:noinline +func ldexp(frac float64, exp int) float64 { + return 1.0 +} + +// Generate a CMOV with a floating comparison and integer move. +func cmovfloatint2(x, y float64) float64 { + yfr, yexp := 4.0, 5 + + r := x + for r >= y { + rfr, rexp := frexp(r) + if rfr < yfr { + rexp = rexp - 1 + } + // amd64:"CMOVQHI" + // arm64:"CSEL\tGT" + r = r - ldexp(y, (rexp-yexp)) + } + return r +} + +func cmovloaded(x [4]int, y int) int { + if x[2] != 0 { + y = x[2] + } else { + y = y >> 2 + } + // amd64:"CMOVQNE" + // arm64:"CSEL\tNE" + return y +} + +func cmovuintptr2(x, y uintptr) uintptr { + a := x * 2 + if a == 0 { + a = 256 + } + // amd64:"CMOVQEQ" + // arm64:"CSEL\tEQ" + return a +} + +// Floating point CMOVs are not supported by amd64/arm64 +func cmovfloatmove(x, y int) float64 { + a := 1.0 + if x <= y { + a = 2.0 + } + // amd64:-"CMOV" + // arm64:-"CSEL" + return a +} + +// On amd64, the following patterns trigger comparison inversion. +// Test that we correctly invert the CMOV condition +var gsink int64 +var gusink uint64 + +func cmovinvert1(x, y int64) int64 { + if x < gsink { + y = -y + } + // amd64:"CMOVQGT" + return y +} +func cmovinvert2(x, y int64) int64 { + if x <= gsink { + y = -y + } + // amd64:"CMOVQGE" + return y +} +func cmovinvert3(x, y int64) int64 { + if x == gsink { + y = -y + } + // amd64:"CMOVQEQ" + return y +} +func cmovinvert4(x, y int64) int64 { + if x != gsink { + y = -y + } + // amd64:"CMOVQNE" + return y +} +func cmovinvert5(x, y uint64) uint64 { + if x > gusink { + y = -y + } + // amd64:"CMOVQCS" + return y +} +func cmovinvert6(x, y uint64) uint64 { + if x >= gusink { + y = -y + } + // amd64:"CMOVQLS" + return y +} diff --git a/test/codegen/copy.go b/test/codegen/copy.go new file mode 100644 index 000000000..5c3837bc7 --- /dev/null +++ b/test/codegen/copy.go @@ -0,0 +1,80 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +import "runtime" + +// Check small copies are replaced with moves. + +func movesmall4() { + x := [...]byte{1, 2, 3, 4} + // 386:-".*memmove" + // amd64:-".*memmove" + // arm:-".*memmove" + // arm64:-".*memmove" + copy(x[1:], x[:]) +} + +func movesmall7() { + x := [...]byte{1, 2, 3, 4, 5, 6, 7} + // 386:-".*memmove" + // amd64:-".*memmove" + // arm64:-".*memmove" + copy(x[1:], x[:]) +} + +func movesmall16() { + x := [...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} + // amd64:-".*memmove" + copy(x[1:], x[:]) +} + +var x [256]byte + +// Check that large disjoint copies are replaced with moves. + +func moveDisjointStack() { + var s [256]byte + // s390x:-".*memmove" + copy(s[:], x[:]) + runtime.KeepAlive(&s) +} + +func moveDisjointArg(b *[256]byte) { + var s [256]byte + // s390x:-".*memmove" + copy(s[:], b[:]) + runtime.KeepAlive(&s) +} + +func moveDisjointNoOverlap(a *[256]byte) { + // s390x:-".*memmove" + copy(a[:], a[128:]) +} + +// Check that no branches are generated when the pointers are [not] equal. + +func ptrEqual() { + // amd64:-"JEQ",-"JNE" + // ppc64le:-"BEQ",-"BNE" + // s390x:-"BEQ",-"BNE" + copy(x[:], x[:]) +} + +func ptrOneOffset() { + // amd64:-"JEQ",-"JNE" + // ppc64le:-"BEQ",-"BNE" + // s390x:-"BEQ",-"BNE" + copy(x[1:], x[:]) +} + +func ptrBothOffset() { + // amd64:-"JEQ",-"JNE" + // ppc64le:-"BEQ",-"BNE" + // s390x:-"BEQ",-"BNE" + copy(x[1:], x[2:]) +} diff --git a/test/codegen/floats.go b/test/codegen/floats.go new file mode 100644 index 000000000..e0e4d973a --- /dev/null +++ b/test/codegen/floats.go @@ -0,0 +1,99 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +// This file contains codegen tests related to arithmetic +// simplifications and optimizations on float types. +// For codegen tests on integer types, see arithmetic.go. + +// --------------------- // +// Strength-reduce // +// --------------------- // + +func Mul2(f float64) float64 { + // 386/sse2:"ADDSD",-"MULSD" + // 386/387:"FADDDP",-"FMULDP" + // amd64:"ADDSD",-"MULSD" + // arm/7:"ADDD",-"MULD" + // arm64:"FADDD",-"FMULD" + return f * 2.0 +} + +func DivPow2(f1, f2, f3 float64) (float64, float64, float64) { + // 386/sse2:"MULSD",-"DIVSD" + // 386/387:"FMULDP",-"FDIVDP" + // amd64:"MULSD",-"DIVSD" + // arm/7:"MULD",-"DIVD" + // arm64:"FMULD",-"FDIVD" + x := f1 / 16.0 + + // 386/sse2:"MULSD",-"DIVSD" + // 386/387:"FMULDP",-"FDIVDP" + // amd64:"MULSD",-"DIVSD" + // arm/7:"MULD",-"DIVD" + // arm64:"FMULD",-"FDIVD" + y := f2 / 0.125 + + // 386/sse2:"ADDSD",-"DIVSD",-"MULSD" + // 386/387:"FADDDP",-"FDIVDP",-"FMULDP" + // amd64:"ADDSD",-"DIVSD",-"MULSD" + // arm/7:"ADDD",-"MULD",-"DIVD" + // arm64:"FADDD",-"FMULD",-"FDIVD" + z := f3 / 0.5 + + return x, y, z +} + +// ----------- // +// Fused // +// ----------- // + +func FusedAdd32(x, y, z float32) float32 { + // s390x:"FMADDS\t" + // ppc64le:"FMADDS\t" + return x*y + z +} + +func FusedSub32(x, y, z float32) float32 { + // s390x:"FMSUBS\t" + // ppc64le:"FMSUBS\t" + return x*y - z +} + +func FusedAdd64(x, y, z float64) float64 { + // s390x:"FMADD\t" + // ppc64le:"FMADD\t" + return x*y + z +} + +func FusedSub64(x, y, z float64) float64 { + // s390x:"FMSUB\t" + // ppc64le:"FMSUB\t" + return x*y - z +} + +// ---------------- // +// Non-floats // +// ---------------- // + +// We should make sure that the compiler doesn't generate floating point +// instructions for non-float operations on Plan 9, because floating point +// operations are not allowed in the note handler. + +func ArrayZero() [16]byte { + // amd64:"MOVUPS" + // plan9/amd64/:-"MOVUPS" + var a [16]byte + return a +} + +func ArrayCopy(a [16]byte) (b [16]byte) { + // amd64:"MOVUPS" + // plan9/amd64/:-"MOVUPS" + b = a + return +} diff --git a/test/codegen/issue22703.go b/test/codegen/issue22703.go new file mode 100644 index 000000000..0201de653 --- /dev/null +++ b/test/codegen/issue22703.go @@ -0,0 +1,535 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +type I interface { + foo000() + foo001() + foo002() + foo003() + foo004() + foo005() + foo006() + foo007() + foo008() + foo009() + foo010() + foo011() + foo012() + foo013() + foo014() + foo015() + foo016() + foo017() + foo018() + foo019() + foo020() + foo021() + foo022() + foo023() + foo024() + foo025() + foo026() + foo027() + foo028() + foo029() + foo030() + foo031() + foo032() + foo033() + foo034() + foo035() + foo036() + foo037() + foo038() + foo039() + foo040() + foo041() + foo042() + foo043() + foo044() + foo045() + foo046() + foo047() + foo048() + foo049() + foo050() + foo051() + foo052() + foo053() + foo054() + foo055() + foo056() + foo057() + foo058() + foo059() + foo060() + foo061() + foo062() + foo063() + foo064() + foo065() + foo066() + foo067() + foo068() + foo069() + foo070() + foo071() + foo072() + foo073() + foo074() + foo075() + foo076() + foo077() + foo078() + foo079() + foo080() + foo081() + foo082() + foo083() + foo084() + foo085() + foo086() + foo087() + foo088() + foo089() + foo090() + foo091() + foo092() + foo093() + foo094() + foo095() + foo096() + foo097() + foo098() + foo099() + foo100() + foo101() + foo102() + foo103() + foo104() + foo105() + foo106() + foo107() + foo108() + foo109() + foo110() + foo111() + foo112() + foo113() + foo114() + foo115() + foo116() + foo117() + foo118() + foo119() + foo120() + foo121() + foo122() + foo123() + foo124() + foo125() + foo126() + foo127() + foo128() + foo129() + foo130() + foo131() + foo132() + foo133() + foo134() + foo135() + foo136() + foo137() + foo138() + foo139() + foo140() + foo141() + foo142() + foo143() + foo144() + foo145() + foo146() + foo147() + foo148() + foo149() + foo150() + foo151() + foo152() + foo153() + foo154() + foo155() + foo156() + foo157() + foo158() + foo159() + foo160() + foo161() + foo162() + foo163() + foo164() + foo165() + foo166() + foo167() + foo168() + foo169() + foo170() + foo171() + foo172() + foo173() + foo174() + foo175() + foo176() + foo177() + foo178() + foo179() + foo180() + foo181() + foo182() + foo183() + foo184() + foo185() + foo186() + foo187() + foo188() + foo189() + foo190() + foo191() + foo192() + foo193() + foo194() + foo195() + foo196() + foo197() + foo198() + foo199() + foo200() + foo201() + foo202() + foo203() + foo204() + foo205() + foo206() + foo207() + foo208() + foo209() + foo210() + foo211() + foo212() + foo213() + foo214() + foo215() + foo216() + foo217() + foo218() + foo219() + foo220() + foo221() + foo222() + foo223() + foo224() + foo225() + foo226() + foo227() + foo228() + foo229() + foo230() + foo231() + foo232() + foo233() + foo234() + foo235() + foo236() + foo237() + foo238() + foo239() + foo240() + foo241() + foo242() + foo243() + foo244() + foo245() + foo246() + foo247() + foo248() + foo249() + foo250() + foo251() + foo252() + foo253() + foo254() + foo255() + foo256() + foo257() + foo258() + foo259() + foo260() + foo261() + foo262() + foo263() + foo264() + foo265() + foo266() + foo267() + foo268() + foo269() + foo270() + foo271() + foo272() + foo273() + foo274() + foo275() + foo276() + foo277() + foo278() + foo279() + foo280() + foo281() + foo282() + foo283() + foo284() + foo285() + foo286() + foo287() + foo288() + foo289() + foo290() + foo291() + foo292() + foo293() + foo294() + foo295() + foo296() + foo297() + foo298() + foo299() + foo300() + foo301() + foo302() + foo303() + foo304() + foo305() + foo306() + foo307() + foo308() + foo309() + foo310() + foo311() + foo312() + foo313() + foo314() + foo315() + foo316() + foo317() + foo318() + foo319() + foo320() + foo321() + foo322() + foo323() + foo324() + foo325() + foo326() + foo327() + foo328() + foo329() + foo330() + foo331() + foo332() + foo333() + foo334() + foo335() + foo336() + foo337() + foo338() + foo339() + foo340() + foo341() + foo342() + foo343() + foo344() + foo345() + foo346() + foo347() + foo348() + foo349() + foo350() + foo351() + foo352() + foo353() + foo354() + foo355() + foo356() + foo357() + foo358() + foo359() + foo360() + foo361() + foo362() + foo363() + foo364() + foo365() + foo366() + foo367() + foo368() + foo369() + foo370() + foo371() + foo372() + foo373() + foo374() + foo375() + foo376() + foo377() + foo378() + foo379() + foo380() + foo381() + foo382() + foo383() + foo384() + foo385() + foo386() + foo387() + foo388() + foo389() + foo390() + foo391() + foo392() + foo393() + foo394() + foo395() + foo396() + foo397() + foo398() + foo399() + foo400() + foo401() + foo402() + foo403() + foo404() + foo405() + foo406() + foo407() + foo408() + foo409() + foo410() + foo411() + foo412() + foo413() + foo414() + foo415() + foo416() + foo417() + foo418() + foo419() + foo420() + foo421() + foo422() + foo423() + foo424() + foo425() + foo426() + foo427() + foo428() + foo429() + foo430() + foo431() + foo432() + foo433() + foo434() + foo435() + foo436() + foo437() + foo438() + foo439() + foo440() + foo441() + foo442() + foo443() + foo444() + foo445() + foo446() + foo447() + foo448() + foo449() + foo450() + foo451() + foo452() + foo453() + foo454() + foo455() + foo456() + foo457() + foo458() + foo459() + foo460() + foo461() + foo462() + foo463() + foo464() + foo465() + foo466() + foo467() + foo468() + foo469() + foo470() + foo471() + foo472() + foo473() + foo474() + foo475() + foo476() + foo477() + foo478() + foo479() + foo480() + foo481() + foo482() + foo483() + foo484() + foo485() + foo486() + foo487() + foo488() + foo489() + foo490() + foo491() + foo492() + foo493() + foo494() + foo495() + foo496() + foo497() + foo498() + foo499() + foo500() + foo501() + foo502() + foo503() + foo504() + foo505() + foo506() + foo507() + foo508() + foo509() + foo510() + foo511() +} + +// Nil checks before calling interface methods. +// We need it only when the offset is large. + +func callMethodSmallOffset(i I) { + // amd64:-"TESTB" + i.foo001() +} + +func callMethodLargeOffset(i I) { + // amd64:"TESTB" + i.foo511() +} diff --git a/test/codegen/mapaccess.go b/test/codegen/mapaccess.go new file mode 100644 index 000000000..35620e741 --- /dev/null +++ b/test/codegen/mapaccess.go @@ -0,0 +1,462 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +// These tests check that mapaccess calls are not used. +// Issues #23661 and #24364. + +func mapCompoundAssignmentInt8() { + m := make(map[int8]int8, 0) + var k int8 = 0 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] += 67 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] -= 123 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] *= 45 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] |= 78 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] ^= 89 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] <<= 9 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] >>= 10 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k]++ + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k]-- +} + +func mapCompoundAssignmentInt32() { + m := make(map[int32]int32, 0) + var k int32 = 0 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] += 67890 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] -= 123 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] *= 456 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] |= 78 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] ^= 89 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] <<= 9 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] >>= 10 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k]++ + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k]-- +} + +func mapCompoundAssignmentInt64() { + m := make(map[int64]int64, 0) + var k int64 = 0 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] += 67890 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] -= 123 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] *= 456 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] |= 78 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] ^= 89 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] <<= 9 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] >>= 10 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k]++ + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k]-- +} + +func mapCompoundAssignmentComplex128() { + m := make(map[complex128]complex128, 0) + var k complex128 = 0 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] += 67890 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] -= 123 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] *= 456 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k]++ + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k]-- +} + +func mapCompoundAssignmentString() { + m := make(map[string]string, 0) + var k string = "key" + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] += "value" +} + +var sinkAppend bool + +// TODO: optimization is not applied because of mapslow flag. +func mapAppendAssignmentInt8() { + m := make(map[int8][]int8, 0) + var k int8 = 0 + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + m[k] = append(m[k], 1) + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + m[k] = append(m[k], 1, 2, 3) + + a := []int8{7, 8, 9, 0} + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + m[k] = append(m[k], a...) + + // Exceptions + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + m[k] = append(a, m[k]...) + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + sinkAppend, m[k] = !sinkAppend, append(m[k], 99) + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + m[k] = append(m[k+1], 100) +} + +func mapAppendAssignmentInt32() { + m := make(map[int32][]int32, 0) + var k int32 = 0 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] = append(m[k], 1) + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] = append(m[k], 1, 2, 3) + + a := []int32{7, 8, 9, 0} + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] = append(m[k], a...) + + // Exceptions + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + m[k] = append(a, m[k]...) + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + sinkAppend, m[k] = !sinkAppend, append(m[k], 99) + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + m[k] = append(m[k+1], 100) +} + +func mapAppendAssignmentInt64() { + m := make(map[int64][]int64, 0) + var k int64 = 0 + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] = append(m[k], 1) + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] = append(m[k], 1, 2, 3) + + a := []int64{7, 8, 9, 0} + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] = append(m[k], a...) + + // Exceptions + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + m[k] = append(a, m[k]...) + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + sinkAppend, m[k] = !sinkAppend, append(m[k], 99) + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + m[k] = append(m[k+1], 100) +} + +// TODO: optimization is not applied because of mapslow flag. +func mapAppendAssignmentComplex128() { + m := make(map[complex128][]complex128, 0) + var k complex128 = 0 + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + m[k] = append(m[k], 1) + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + m[k] = append(m[k], 1, 2, 3) + + a := []complex128{7, 8, 9, 0} + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + m[k] = append(m[k], a...) + + // Exceptions + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + m[k] = append(a, m[k]...) + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + sinkAppend, m[k] = !sinkAppend, append(m[k], 99) + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + m[k] = append(m[k+1], 100) +} + +func mapAppendAssignmentString() { + m := make(map[string][]string, 0) + var k string = "key" + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] = append(m[k], "1") + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] = append(m[k], "1", "2", "3") + + a := []string{"7", "8", "9", "0"} + + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" + m[k] = append(m[k], a...) + + // Exceptions + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + m[k] = append(a, m[k]...) + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + sinkAppend, m[k] = !sinkAppend, append(m[k], "99") + + // 386:".*mapaccess" + // amd64:".*mapaccess" + // arm:".*mapaccess" + // arm64:".*mapaccess" + m[k] = append(m[k+"1"], "100") +} diff --git a/test/codegen/maps.go b/test/codegen/maps.go new file mode 100644 index 000000000..d16771589 --- /dev/null +++ b/test/codegen/maps.go @@ -0,0 +1,95 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +// This file contains code generation tests related to the handling of +// map types. + +// ------------------- // +// Access Const // +// ------------------- // + +// Direct use of constants in fast map access calls (Issue #19015). + +func AccessInt1(m map[int]int) int { + // amd64:"MOVQ\t[$]5" + return m[5] +} + +func AccessInt2(m map[int]int) bool { + // amd64:"MOVQ\t[$]5" + _, ok := m[5] + return ok +} + +func AccessString1(m map[string]int) int { + // amd64:`.*"abc"` + return m["abc"] +} + +func AccessString2(m map[string]int) bool { + // amd64:`.*"abc"` + _, ok := m["abc"] + return ok +} + +// ------------------- // +// Map Clear // +// ------------------- // + +// Optimization of map clear idiom (Issue #20138). + +func MapClearReflexive(m map[int]int) { + // amd64:`.*runtime\.mapclear` + // amd64:-`.*runtime\.mapiterinit` + for k := range m { + delete(m, k) + } +} + +func MapClearIndirect(m map[int]int) { + s := struct{ m map[int]int }{m: m} + // amd64:`.*runtime\.mapclear` + // amd64:-`.*runtime\.mapiterinit` + for k := range s.m { + delete(s.m, k) + } +} + +func MapClearPointer(m map[*byte]int) { + // amd64:`.*runtime\.mapclear` + // amd64:-`.*runtime\.mapiterinit` + for k := range m { + delete(m, k) + } +} + +func MapClearNotReflexive(m map[float64]int) { + // amd64:`.*runtime\.mapiterinit` + // amd64:-`.*runtime\.mapclear` + for k := range m { + delete(m, k) + } +} + +func MapClearInterface(m map[interface{}]int) { + // amd64:`.*runtime\.mapiterinit` + // amd64:-`.*runtime\.mapclear` + for k := range m { + delete(m, k) + } +} + +func MapClearSideEffect(m map[int]int) int { + k := 0 + // amd64:`.*runtime\.mapiterinit` + // amd64:-`.*runtime\.mapclear` + for k = range m { + delete(m, k) + } + return k +} diff --git a/test/codegen/math.go b/test/codegen/math.go new file mode 100644 index 000000000..1ecba2684 --- /dev/null +++ b/test/codegen/math.go @@ -0,0 +1,146 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +import "math" + +var sink64 [8]float64 + +func approx(x float64) { + // s390x:"FIDBR\t[$]6" + // arm64:"FRINTPD" + // ppc64le:"FRIP" + sink64[0] = math.Ceil(x) + + // s390x:"FIDBR\t[$]7" + // arm64:"FRINTMD" + // ppc64le:"FRIM" + sink64[1] = math.Floor(x) + + // s390x:"FIDBR\t[$]1" + // arm64:"FRINTAD" + // ppc64le:"FRIN" + sink64[2] = math.Round(x) + + // s390x:"FIDBR\t[$]5" + // arm64:"FRINTZD" + // ppc64le:"FRIZ" + sink64[3] = math.Trunc(x) + + // s390x:"FIDBR\t[$]4" + sink64[4] = math.RoundToEven(x) +} + +func sqrt(x float64) float64 { + // amd64:"SQRTSD" + // 386/387:"FSQRT" 386/sse2:"SQRTSD" + // arm64:"FSQRTD" + // arm/7:"SQRTD" + // mips/hardfloat:"SQRTD" mips/softfloat:-"SQRTD" + // mips64/hardfloat:"SQRTD" mips64/softfloat:-"SQRTD" + return math.Sqrt(x) +} + +// Check that it's using integer registers +func abs(x, y float64) { + // amd64:"BTRQ\t[$]63" + // s390x:"LPDFR\t",-"MOVD\t" (no integer load/store) + // ppc64le:"FABS\t" + sink64[0] = math.Abs(x) + + // amd64:"BTRQ\t[$]63","PXOR" (TODO: this should be BTSQ) + // s390x:"LNDFR\t",-"MOVD\t" (no integer load/store) + // ppc64le:"FNABS\t" + sink64[1] = -math.Abs(y) +} + +// Check that it's using integer registers +func abs32(x float32) float32 { + // s390x:"LPDFR",-"LDEBR",-"LEDBR" (no float64 conversion) + return float32(math.Abs(float64(x))) +} + +// Check that it's using integer registers +func copysign(a, b, c float64) { + // amd64:"BTRQ\t[$]63","SHRQ\t[$]63","SHLQ\t[$]63","ORQ" + // s390x:"CPSDR",-"MOVD" (no integer load/store) + // ppc64le:"FCPSGN" + sink64[0] = math.Copysign(a, b) + + // amd64:"BTSQ\t[$]63" + // s390x:"LNDFR\t",-"MOVD\t" (no integer load/store) + // ppc64le:"FCPSGN" + sink64[1] = math.Copysign(c, -1) + + // Like math.Copysign(c, -1), but with integer operations. Useful + // for platforms that have a copysign opcode to see if it's detected. + // s390x:"LNDFR\t",-"MOVD\t" (no integer load/store) + sink64[2] = math.Float64frombits(math.Float64bits(a) | 1<<63) + + // amd64:-"SHLQ\t[$]1",-"SHRQ\t[$]1","SHRQ\t[$]63","SHLQ\t[$]63","ORQ" + // s390x:"CPSDR\t",-"MOVD\t" (no integer load/store) + // ppc64le:"FCPSGN" + sink64[3] = math.Copysign(-1, c) +} + +func fromFloat64(f64 float64) uint64 { + // amd64:"MOVQ\tX.*, [^X].*" + return math.Float64bits(f64+1) + 1 +} + +func fromFloat32(f32 float32) uint32 { + // amd64:"MOVL\tX.*, [^X].*" + return math.Float32bits(f32+1) + 1 +} + +func toFloat64(u64 uint64) float64 { + // amd64:"MOVQ\t[^X].*, X.*" + return math.Float64frombits(u64+1) + 1 +} + +func toFloat32(u32 uint32) float32 { + // amd64:"MOVL\t[^X].*, X.*" + return math.Float32frombits(u32+1) + 1 +} + +// Test that comparisons with constants converted to float +// are evaluated at compile-time + +func constantCheck64() bool { + // amd64:"MOVB\t[$]0",-"FCMP",-"MOVB\t[$]1" + // s390x:"MOV(B|BZ|D)\t[$]0,",-"FCMPU",-"MOV(B|BZ|D)\t[$]1," + return 0.5 == float64(uint32(1)) || 1.5 > float64(uint64(1<<63)) || math.NaN() == math.NaN() +} + +func constantCheck32() bool { + // amd64:"MOVB\t[$]1",-"FCMP",-"MOVB\t[$]0" + // s390x:"MOV(B|BZ|D)\t[$]1,",-"FCMPU",-"MOV(B|BZ|D)\t[$]0," + return float32(0.5) <= float32(int64(1)) && float32(1.5) >= float32(int32(-1<<31)) && float32(math.NaN()) != float32(math.NaN()) +} + +// Test that integer constants are converted to floating point constants +// at compile-time + +func constantConvert32(x float32) float32 { + // amd64:"MOVSS\t[$]f32.3f800000\\(SB\\)" + // s390x:"FMOVS\t[$]f32.3f800000\\(SB\\)" + // ppc64le:"FMOVS\t[$]f32.3f800000\\(SB\\)" + if x > math.Float32frombits(0x3f800000) { + return -x + } + return x +} + +func constantConvertInt32(x uint32) uint32 { + // amd64:-"MOVSS" + // s390x:-"FMOVS" + // ppc64le:-"FMOVS" + if x > math.Float32bits(1) { + return -x + } + return x +} diff --git a/test/codegen/mathbits.go b/test/codegen/mathbits.go new file mode 100644 index 000000000..85c54ea61 --- /dev/null +++ b/test/codegen/mathbits.go @@ -0,0 +1,269 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +import "math/bits" + +// ----------------------- // +// bits.LeadingZeros // +// ----------------------- // + +func LeadingZeros(n uint) int { + // amd64:"BSRQ" + // s390x:"FLOGR" + // arm:"CLZ" arm64:"CLZ" + // mips:"CLZ" + return bits.LeadingZeros(n) +} + +func LeadingZeros64(n uint64) int { + // amd64:"BSRQ" + // s390x:"FLOGR" + // arm:"CLZ" arm64:"CLZ" + // mips:"CLZ" + return bits.LeadingZeros64(n) +} + +func LeadingZeros32(n uint32) int { + // amd64:"BSRQ","LEAQ",-"CMOVQEQ" + // s390x:"FLOGR" + // arm:"CLZ" arm64:"CLZ" + // mips:"CLZ" + return bits.LeadingZeros32(n) +} + +func LeadingZeros16(n uint16) int { + // amd64:"BSRL","LEAL",-"CMOVQEQ" + // s390x:"FLOGR" + // arm:"CLZ" arm64:"CLZ" + // mips:"CLZ" + return bits.LeadingZeros16(n) +} + +func LeadingZeros8(n uint8) int { + // amd64:"BSRL","LEAL",-"CMOVQEQ" + // s390x:"FLOGR" + // arm:"CLZ" arm64:"CLZ" + // mips:"CLZ" + return bits.LeadingZeros8(n) +} + +// --------------- // +// bits.Len* // +// --------------- // + +func Len(n uint) int { + // amd64:"BSRQ" + // s390x:"FLOGR" + // arm:"CLZ" arm64:"CLZ" + // mips:"CLZ" + return bits.Len(n) +} + +func Len64(n uint64) int { + // amd64:"BSRQ" + // s390x:"FLOGR" + // arm:"CLZ" arm64:"CLZ" + // mips:"CLZ" + return bits.Len64(n) +} + +func Len32(n uint32) int { + // amd64:"BSRQ","LEAQ",-"CMOVQEQ" + // s390x:"FLOGR" + // arm:"CLZ" arm64:"CLZ" + // mips:"CLZ" + return bits.Len32(n) +} + +func Len16(n uint16) int { + // amd64:"BSRL","LEAL",-"CMOVQEQ" + // s390x:"FLOGR" + // arm:"CLZ" arm64:"CLZ" + // mips:"CLZ" + return bits.Len16(n) +} + +func Len8(n uint8) int { + // amd64:"BSRL","LEAL",-"CMOVQEQ" + // s390x:"FLOGR" + // arm:"CLZ" arm64:"CLZ" + // mips:"CLZ" + return bits.Len8(n) +} + +// -------------------- // +// bits.OnesCount // +// -------------------- // + +func OnesCount(n uint) int { + // amd64:"POPCNTQ",".*support_popcnt" + // arm64:"VCNT","VUADDLV" + return bits.OnesCount(n) +} + +func OnesCount64(n uint64) int { + // amd64:"POPCNTQ",".*support_popcnt" + // arm64:"VCNT","VUADDLV" + return bits.OnesCount64(n) +} + +func OnesCount32(n uint32) int { + // amd64:"POPCNTL",".*support_popcnt" + // arm64:"VCNT","VUADDLV" + return bits.OnesCount32(n) +} + +func OnesCount16(n uint16) int { + // amd64:"POPCNTL",".*support_popcnt" + // arm64:"VCNT","VUADDLV" + return bits.OnesCount16(n) +} + +// ----------------------- // +// bits.ReverseBytes // +// ----------------------- // + +func ReverseBytes(n uint) uint { + // amd64:"BSWAPQ" + // s390x:"MOVDBR" + // arm64:"REV" + return bits.ReverseBytes(n) +} + +func ReverseBytes64(n uint64) uint64 { + // amd64:"BSWAPQ" + // s390x:"MOVDBR" + // arm64:"REV" + return bits.ReverseBytes64(n) +} + +func ReverseBytes32(n uint32) uint32 { + // amd64:"BSWAPL" + // s390x:"MOVWBR" + // arm64:"REVW" + return bits.ReverseBytes32(n) +} + +func ReverseBytes16(n uint16) uint16 { + // amd64:"ROLW" + return bits.ReverseBytes16(n) +} + +// --------------------- // +// bits.RotateLeft // +// --------------------- // + +func RotateLeft64(n uint64) uint64 { + // amd64:"ROLQ" + // arm64:"ROR" + // ppc64:"ROTL" + return bits.RotateLeft64(n, 37) +} + +func RotateLeft32(n uint32) uint32 { + // amd64:"ROLL" 386:"ROLL" + // arm64:"RORW" + // ppc64:"ROTLW" + return bits.RotateLeft32(n, 9) +} + +func RotateLeft16(n uint16) uint16 { + // amd64:"ROLW" 386:"ROLW" + return bits.RotateLeft16(n, 5) +} + +func RotateLeft8(n uint8) uint8 { + // amd64:"ROLB" 386:"ROLB" + return bits.RotateLeft8(n, 5) +} + +// ------------------------ // +// bits.TrailingZeros // +// ------------------------ // + +func TrailingZeros(n uint) int { + // amd64:"BSFQ","MOVL\t\\$64","CMOVQEQ" + // s390x:"FLOGR" + return bits.TrailingZeros(n) +} + +func TrailingZeros64(n uint64) int { + // amd64:"BSFQ","MOVL\t\\$64","CMOVQEQ" + // s390x:"FLOGR" + return bits.TrailingZeros64(n) +} + +func TrailingZeros32(n uint32) int { + // amd64:"BTSQ\\t\\$32","BSFQ" + // s390x:"FLOGR","MOVWZ" + return bits.TrailingZeros32(n) +} + +func TrailingZeros16(n uint16) int { + // amd64:"BSFL","BTSL\\t\\$16" + // s390x:"FLOGR","OR\t\\$65536" + return bits.TrailingZeros16(n) +} + +func TrailingZeros8(n uint8) int { + // amd64:"BSFL","BTSL\\t\\$8" + // s390x:"FLOGR","OR\t\\$256" + return bits.TrailingZeros8(n) +} + +// IterateBitsNN checks special handling of TrailingZerosNN when the input is known to be non-zero. + +func IterateBits(n uint) int { + i := 0 + for n != 0 { + // amd64:"BSFQ",-"CMOVEQ" + i += bits.TrailingZeros(n) + n &= n - 1 + } + return i +} + +func IterateBits64(n uint64) int { + i := 0 + for n != 0 { + // amd64:"BSFQ",-"CMOVEQ" + i += bits.TrailingZeros64(n) + n &= n - 1 + } + return i +} + +func IterateBits32(n uint32) int { + i := 0 + for n != 0 { + // amd64:"BSFL",-"BTSQ" + i += bits.TrailingZeros32(n) + n &= n - 1 + } + return i +} + +func IterateBits16(n uint16) int { + i := 0 + for n != 0 { + // amd64:"BSFL",-"BTSL" + i += bits.TrailingZeros16(n) + n &= n - 1 + } + return i +} + +func IterateBits8(n uint8) int { + i := 0 + for n != 0 { + // amd64:"BSFL",-"BTSL" + i += bits.TrailingZeros8(n) + n &= n - 1 + } + return i +} diff --git a/test/codegen/memcombine.go b/test/codegen/memcombine.go new file mode 100644 index 000000000..0db366250 --- /dev/null +++ b/test/codegen/memcombine.go @@ -0,0 +1,580 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +import ( + "encoding/binary" + "runtime" +) + +var sink64 uint64 +var sink32 uint32 +var sink16 uint16 + +// ------------- // +// Loading // +// ------------- // + +func load_le64(b []byte) { + // amd64:`MOVQ\s\(.*\),` + // s390x:`MOVDBR\s\(.*\),` + // arm64:`MOVD\s\(R[0-9]+\),`,-`MOV[BHW]` + // ppc64le:`MOVD\s`,-`MOV[BHW]Z` + sink64 = binary.LittleEndian.Uint64(b) +} + +func load_le64_idx(b []byte, idx int) { + // amd64:`MOVQ\s\(.*\)\(.*\*1\),` + // s390x:`MOVDBR\s\(.*\)\(.*\*1\),` + // arm64:`MOVD\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOV[BHW]` + // ppc64le:`MOVD\s`,-`MOV[BHW]Z\s` + sink64 = binary.LittleEndian.Uint64(b[idx:]) +} + +func load_le32(b []byte) { + // amd64:`MOVL\s\(.*\),` 386:`MOVL\s\(.*\),` + // s390x:`MOVWBR\s\(.*\),` + // arm64:`MOVWU\s\(R[0-9]+\),`,-`MOV[BH]` + // ppc64le:`MOVWZ\s` + sink32 = binary.LittleEndian.Uint32(b) +} + +func load_le32_idx(b []byte, idx int) { + // amd64:`MOVL\s\(.*\)\(.*\*1\),` 386:`MOVL\s\(.*\)\(.*\*1\),` + // s390x:`MOVWBR\s\(.*\)\(.*\*1\),` + // arm64:`MOVWU\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOV[BH]` + // ppc64le:`MOVWZ\s` + sink32 = binary.LittleEndian.Uint32(b[idx:]) +} + +func load_le16(b []byte) { + // amd64:`MOVWLZX\s\(.*\),` + // ppc64le:`MOVHZ\s` + // arm64:`MOVHU\s\(R[0-9]+\),`,-`MOVB` + sink16 = binary.LittleEndian.Uint16(b) +} + +func load_le16_idx(b []byte, idx int) { + // amd64:`MOVWLZX\s\(.*\),` + // ppc64le:`MOVHZ\s` + // arm64:`MOVHU\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOVB` + sink16 = binary.LittleEndian.Uint16(b[idx:]) +} + +func load_be64(b []byte) { + // amd64:`BSWAPQ` + // s390x:`MOVD\s\(.*\),` + // arm64:`REV`,`MOVD\s\(R[0-9]+\),`,-`MOV[BHW]`,-`REVW`,-`REV16W` + // ppc64le:`MOVDBR` + sink64 = binary.BigEndian.Uint64(b) +} + +func load_be64_idx(b []byte, idx int) { + // amd64:`BSWAPQ` + // s390x:`MOVD\s\(.*\)\(.*\*1\),` + // arm64:`REV`,`MOVD\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOV[WHB]`,-`REVW`,-`REV16W` + // ppc64le:`MOVDBR` + sink64 = binary.BigEndian.Uint64(b[idx:]) +} + +func load_be32(b []byte) { + // amd64:`BSWAPL` + // s390x:`MOVWZ\s\(.*\),` + // arm64:`REVW`,`MOVWU\s\(R[0-9]+\),`,-`MOV[BH]`,-`REV16W` + // ppc64le:`MOVWBR` + sink32 = binary.BigEndian.Uint32(b) +} + +func load_be32_idx(b []byte, idx int) { + // amd64:`BSWAPL` + // s390x:`MOVWZ\s\(.*\)\(.*\*1\),` + // arm64:`REVW`,`MOVWU\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOV[HB]`,-`REV16W` + // ppc64le:`MOVWBR` + sink32 = binary.BigEndian.Uint32(b[idx:]) +} + +func load_be16(b []byte) { + // amd64:`ROLW\s\$8` + // arm64:`REV16W`,`MOVHU\s\(R[0-9]+\),`,-`MOVB` + // ppc64le:`MOVHBR` + sink16 = binary.BigEndian.Uint16(b) +} + +func load_be16_idx(b []byte, idx int) { + // amd64:`ROLW\s\$8` + // arm64:`REV16W`,`MOVHU\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOVB` + // ppc64le:`MOVHBR` + sink16 = binary.BigEndian.Uint16(b[idx:]) +} + +func load_le_byte2_uint16(s []byte) uint16 { + // arm64:`MOVHU\t\(R[0-9]+\)`,-`ORR`,-`MOVB` + return uint16(s[0]) | uint16(s[1])<<8 +} + +func load_le_byte2_uint16_inv(s []byte) uint16 { + // arm64:`MOVHU\t\(R[0-9]+\)`,-`ORR`,-`MOVB` + return uint16(s[1])<<8 | uint16(s[0]) +} + +func load_le_byte4_uint32(s []byte) uint32 { + // arm64:`MOVWU\t\(R[0-9]+\)`,-`ORR`,-`MOV[BH]` + return uint32(s[0]) | uint32(s[1])<<8 | uint32(s[2])<<16 | uint32(s[3])<<24 +} + +func load_le_byte4_uint32_inv(s []byte) uint32 { + // arm64:`MOVWU\t\(R[0-9]+\)`,-`ORR`,-`MOV[BH]` + return uint32(s[3])<<24 | uint32(s[2])<<16 | uint32(s[1])<<8 | uint32(s[0]) +} + +func load_le_byte8_uint64(s []byte) uint64 { + // arm64:`MOVD\t\(R[0-9]+\)`,-`ORR`,-`MOV[BHW]` + return uint64(s[0]) | uint64(s[1])<<8 | uint64(s[2])<<16 | uint64(s[3])<<24 | uint64(s[4])<<32 | uint64(s[5])<<40 | uint64(s[6])<<48 | uint64(s[7])<<56 +} + +func load_le_byte8_uint64_inv(s []byte) uint64 { + // arm64:`MOVD\t\(R[0-9]+\)`,-`ORR`,-`MOV[BHW]` + return uint64(s[7])<<56 | uint64(s[6])<<48 | uint64(s[5])<<40 | uint64(s[4])<<32 | uint64(s[3])<<24 | uint64(s[2])<<16 | uint64(s[1])<<8 | uint64(s[0]) +} + +func load_be_byte2_uint16(s []byte) uint16 { + // arm64:`MOVHU\t\(R[0-9]+\)`,`REV16W`,-`ORR`,-`MOVB` + return uint16(s[0])<<8 | uint16(s[1]) +} + +func load_be_byte2_uint16_inv(s []byte) uint16 { + // arm64:`MOVHU\t\(R[0-9]+\)`,`REV16W`,-`ORR`,-`MOVB` + return uint16(s[1]) | uint16(s[0])<<8 +} + +func load_be_byte4_uint32(s []byte) uint32 { + // arm64:`MOVWU\t\(R[0-9]+\)`,`REVW`,-`ORR`,-`REV16W`,-`MOV[BH]` + return uint32(s[0])<<24 | uint32(s[1])<<16 | uint32(s[2])<<8 | uint32(s[3]) +} + +func load_be_byte4_uint32_inv(s []byte) uint32 { + // arm64:`MOVWU\t\(R[0-9]+\)`,`REVW`,-`ORR`,-`REV16W`,-`MOV[BH]` + return uint32(s[3]) | uint32(s[2])<<8 | uint32(s[1])<<16 | uint32(s[0])<<24 +} + +func load_be_byte8_uint64(s []byte) uint64 { + // arm64:`MOVD\t\(R[0-9]+\)`,`REV`,-`ORR`,-`REVW`,-`REV16W`,-`MOV[BHW]` + return uint64(s[0])<<56 | uint64(s[1])<<48 | uint64(s[2])<<40 | uint64(s[3])<<32 | uint64(s[4])<<24 | uint64(s[5])<<16 | uint64(s[6])<<8 | uint64(s[7]) +} + +func load_be_byte8_uint64_inv(s []byte) uint64 { + // arm64:`MOVD\t\(R[0-9]+\)`,`REV`,-`ORR`,-`REVW`,-`REV16W`,-`MOV[BHW]` + return uint64(s[7]) | uint64(s[6])<<8 | uint64(s[5])<<16 | uint64(s[4])<<24 | uint64(s[3])<<32 | uint64(s[2])<<40 | uint64(s[1])<<48 | uint64(s[0])<<56 +} + +func load_le_byte2_uint16_idx(s []byte, idx int) uint16 { + // arm64:`MOVHU\s\(R[0-9]+\)\(R[0-9]+\)`,-`ORR`,-`MOVB` + return uint16(s[idx]) | uint16(s[idx+1])<<8 +} + +func load_le_byte2_uint16_idx_inv(s []byte, idx int) uint16 { + // arm64:`MOVHU\s\(R[0-9]+\)\(R[0-9]+\)`,-`ORR`,-`MOVB` + return uint16(s[idx+1])<<8 | uint16(s[idx]) +} + +func load_le_byte4_uint32_idx(s []byte, idx int) uint32 { + // arm64:`MOVWU\s\(R[0-9]+\)\(R[0-9]+\)`,-`ORR`,-`MOV[BH]` + return uint32(s[idx]) | uint32(s[idx+1])<<8 | uint32(s[idx+2])<<16 | uint32(s[idx+3])<<24 +} + +func load_le_byte4_uint32_idx_inv(s []byte, idx int) uint32 { + // arm64:`MOVWU\s\(R[0-9]+\)\(R[0-9]+\)`,-`ORR`,-`MOV[BH]` + return uint32(s[idx+3])<<24 | uint32(s[idx+2])<<16 | uint32(s[idx+1])<<8 | uint32(s[idx]) +} + +func load_le_byte8_uint64_idx(s []byte, idx int) uint64 { + // arm64:`MOVD\s\(R[0-9]+\)\(R[0-9]+\)`,-`ORR`,-`MOV[BHW]` + return uint64(s[idx]) | uint64(s[idx+1])<<8 | uint64(s[idx+2])<<16 | uint64(s[idx+3])<<24 | uint64(s[idx+4])<<32 | uint64(s[idx+5])<<40 | uint64(s[idx+6])<<48 | uint64(s[idx+7])<<56 +} + +func load_le_byte8_uint64_idx_inv(s []byte, idx int) uint64 { + // arm64:`MOVD\s\(R[0-9]+\)\(R[0-9]+\)`,-`ORR`,-`MOV[BHW]` + return uint64(s[idx+7])<<56 | uint64(s[idx+6])<<48 | uint64(s[idx+5])<<40 | uint64(s[idx+4])<<32 | uint64(s[idx+3])<<24 | uint64(s[idx+2])<<16 | uint64(s[idx+1])<<8 | uint64(s[idx]) +} + +func load_be_byte2_uint16_idx(s []byte, idx int) uint16 { + // arm64:`MOVHU\s\(R[0-9]+\)\(R[0-9]+\)`,`REV16W`,-`ORR`,-`MOVB` + return uint16(s[idx])<<8 | uint16(s[idx+1]) +} + +func load_be_byte2_uint16_idx_inv(s []byte, idx int) uint16 { + // arm64:`MOVHU\s\(R[0-9]+\)\(R[0-9]+\)`,`REV16W`,-`ORR`,-`MOVB` + return uint16(s[idx+1]) | uint16(s[idx])<<8 +} + +func load_be_byte4_uint32_idx(s []byte, idx int) uint32 { + // arm64:`MOVWU\s\(R[0-9]+\)\(R[0-9]+\)`,`REVW`,-`ORR`,-`MOV[BH]`,-`REV16W` + return uint32(s[idx])<<24 | uint32(s[idx+1])<<16 | uint32(s[idx+2])<<8 | uint32(s[idx+3]) +} + +func load_be_byte8_uint64_idx(s []byte, idx int) uint64 { + // arm64:`MOVD\s\(R[0-9]+\)\(R[0-9]+\)`,`REV`,-`ORR`,-`MOV[BHW]`,-`REVW`,-`REV16W` + return uint64(s[idx])<<56 | uint64(s[idx+1])<<48 | uint64(s[idx+2])<<40 | uint64(s[idx+3])<<32 | uint64(s[idx+4])<<24 | uint64(s[idx+5])<<16 | uint64(s[idx+6])<<8 | uint64(s[idx+7]) +} + +func load_le_byte2_uint16_idx2(s []byte, idx int) uint16 { + // arm64:`MOVHU\s\(R[0-9]+\)\(R[0-9]+<<1\)`,-`ORR`,-`MOVB` + return uint16(s[idx<<1]) | uint16(s[(idx<<1)+1])<<8 +} + +func load_le_byte2_uint16_idx2_inv(s []byte, idx int) uint16 { + // arm64:`MOVHU\s\(R[0-9]+\)\(R[0-9]+<<1\)`,-`ORR`,-`MOVB` + return uint16(s[(idx<<1)+1])<<8 | uint16(s[idx<<1]) +} + +func load_le_byte4_uint32_idx4(s []byte, idx int) uint32 { + // arm64:`MOVWU\s\(R[0-9]+\)\(R[0-9]+<<2\)`,-`ORR`,-`MOV[BH]` + return uint32(s[idx<<2]) | uint32(s[(idx<<2)+1])<<8 | uint32(s[(idx<<2)+2])<<16 | uint32(s[(idx<<2)+3])<<24 +} + +func load_le_byte4_uint32_idx4_inv(s []byte, idx int) uint32 { + // arm64:`MOVWU\s\(R[0-9]+\)\(R[0-9]+<<2\)`,-`ORR`,-`MOV[BH]` + return uint32(s[(idx<<2)+3])<<24 | uint32(s[(idx<<2)+2])<<16 | uint32(s[(idx<<2)+1])<<8 | uint32(s[idx<<2]) +} + +func load_le_byte8_uint64_idx8(s []byte, idx int) uint64 { + // arm64:`MOVD\s\(R[0-9]+\)\(R[0-9]+<<3\)`,-`ORR`,-`MOV[BHW]` + return uint64(s[idx<<3]) | uint64(s[(idx<<3)+1])<<8 | uint64(s[(idx<<3)+2])<<16 | uint64(s[(idx<<3)+3])<<24 | uint64(s[(idx<<3)+4])<<32 | uint64(s[(idx<<3)+5])<<40 | uint64(s[(idx<<3)+6])<<48 | uint64(s[(idx<<3)+7])<<56 +} + +func load_le_byte8_uint64_idx8_inv(s []byte, idx int) uint64 { + // arm64:`MOVD\s\(R[0-9]+\)\(R[0-9]+<<3\)`,-`ORR`,-`MOV[BHW]` + return uint64(s[(idx<<3)+7])<<56 | uint64(s[(idx<<3)+6])<<48 | uint64(s[(idx<<3)+5])<<40 | uint64(s[(idx<<3)+4])<<32 | uint64(s[(idx<<3)+3])<<24 | uint64(s[(idx<<3)+2])<<16 | uint64(s[(idx<<3)+1])<<8 | uint64(s[idx<<3]) +} + +func load_be_byte2_uint16_idx2(s []byte, idx int) uint16 { + // arm64:`MOVHU\s\(R[0-9]+\)\(R[0-9]+<<1\)`,`REV16W`,-`ORR`,-`MOVB` + return uint16(s[idx<<1])<<8 | uint16(s[(idx<<1)+1]) +} + +func load_be_byte2_uint16_idx2_inv(s []byte, idx int) uint16 { + // arm64:`MOVHU\s\(R[0-9]+\)\(R[0-9]+<<1\)`,`REV16W`,-`ORR`,-`MOVB` + return uint16(s[(idx<<1)+1]) | uint16(s[idx<<1])<<8 +} + +func load_be_byte4_uint32_idx4(s []byte, idx int) uint32 { + // arm64:`MOVWU\s\(R[0-9]+\)\(R[0-9]+<<2\)`,`REVW`,-`ORR`,-`MOV[BH]`,-`REV16W` + return uint32(s[idx<<2])<<24 | uint32(s[(idx<<2)+1])<<16 | uint32(s[(idx<<2)+2])<<8 | uint32(s[(idx<<2)+3]) +} + +func load_be_byte8_uint64_idx8(s []byte, idx int) uint64 { + // arm64:`MOVD\s\(R[0-9]+\)\(R[0-9]+<<3\)`,`REV`,-`ORR`,-`MOV[BHW]`,-`REVW`,-`REV16W` + return uint64(s[idx<<3])<<56 | uint64(s[(idx<<3)+1])<<48 | uint64(s[(idx<<3)+2])<<40 | uint64(s[(idx<<3)+3])<<32 | uint64(s[(idx<<3)+4])<<24 | uint64(s[(idx<<3)+5])<<16 | uint64(s[(idx<<3)+6])<<8 | uint64(s[(idx<<3)+7]) +} + +// Check load combining across function calls. + +func fcall_byte(a, b byte) (byte, byte) { + return fcall_byte(fcall_byte(a, b)) // amd64:`MOVW` +} + +func fcall_uint16(a, b uint16) (uint16, uint16) { + return fcall_uint16(fcall_uint16(a, b)) // amd64:`MOVL` +} + +func fcall_uint32(a, b uint32) (uint32, uint32) { + return fcall_uint32(fcall_uint32(a, b)) // amd64:`MOVQ` +} + +// We want to merge load+op in the first function, but not in the +// second. See Issue 19595. +func load_op_merge(p, q *int) { + x := *p + *q += x // amd64:`ADDQ\t\(` +} +func load_op_no_merge(p, q *int) { + x := *p + for i := 0; i < 10; i++ { + *q += x // amd64:`ADDQ\t[A-Z]` + } +} + +// Make sure offsets are folded into loads and stores. +func offsets_fold(_, a [20]byte) (b [20]byte) { + // arm64:`MOVD\t""\.a\+[0-9]+\(FP\), R[0-9]+`,`MOVD\tR[0-9]+, ""\.b\+[0-9]+\(FP\)` + b = a + return +} + +// Make sure we don't put pointers in SSE registers across safe +// points. + +func safe_point(p, q *[2]*int) { + a, b := p[0], p[1] // amd64:-`MOVUPS` + runtime.GC() + q[0], q[1] = a, b // amd64:-`MOVUPS` +} + +// ------------- // +// Storing // +// ------------- // + +func store_le64(b []byte) { + // amd64:`MOVQ\s.*\(.*\)$`,-`SHR.` + // arm64:`MOVD`,-`MOV[WBH]` + // ppc64le:`MOVD\s`,-`MOV[BHW]\s` + binary.LittleEndian.PutUint64(b, sink64) +} + +func store_le64_idx(b []byte, idx int) { + // amd64:`MOVQ\s.*\(.*\)\(.*\*1\)$`,-`SHR.` + // arm64:`MOVD\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOV[BHW]` + // ppc64le:`MOVD\s`,-`MOV[BHW]\s` + binary.LittleEndian.PutUint64(b[idx:], sink64) +} + +func store_le32(b []byte) { + // amd64:`MOVL\s` + // arm64:`MOVW`,-`MOV[BH]` + // ppc64le:`MOVW\s` + binary.LittleEndian.PutUint32(b, sink32) +} + +func store_le32_idx(b []byte, idx int) { + // amd64:`MOVL\s` + // arm64:`MOVW\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOV[BH]` + // ppc64le:`MOVW\s` + binary.LittleEndian.PutUint32(b[idx:], sink32) +} + +func store_le16(b []byte) { + // amd64:`MOVW\s` + // arm64:`MOVH`,-`MOVB` + // ppc64le:`MOVH\s` + binary.LittleEndian.PutUint16(b, sink16) +} + +func store_le16_idx(b []byte, idx int) { + // amd64:`MOVW\s` + // arm64:`MOVH\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOVB` + // ppc64le:`MOVH\s` + binary.LittleEndian.PutUint16(b[idx:], sink16) +} + +func store_be64(b []byte) { + // amd64:`BSWAPQ`,-`SHR.` + // arm64:`MOVD`,`REV`,-`MOV[WBH]`,-`REVW`,-`REV16W` + // ppc64le:`MOVDBR` + binary.BigEndian.PutUint64(b, sink64) +} + +func store_be64_idx(b []byte, idx int) { + // amd64:`BSWAPQ`,-`SHR.` + // arm64:`REV`,`MOVD\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOV[BHW]`,-`REV16W`,-`REVW` + // ppc64le:`MOVDBR` + binary.BigEndian.PutUint64(b[idx:], sink64) +} + +func store_be32(b []byte) { + // amd64:`BSWAPL`,-`SHR.` + // arm64:`MOVW`,`REVW`,-`MOV[BH]`,-`REV16W` + // ppc64le:`MOVWBR` + binary.BigEndian.PutUint32(b, sink32) +} + +func store_be32_idx(b []byte, idx int) { + // amd64:`BSWAPL`,-`SHR.` + // arm64:`REVW`,`MOVW\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOV[BH]`,-`REV16W` + // ppc64le:`MOVWBR` + binary.BigEndian.PutUint32(b[idx:], sink32) +} + +func store_be16(b []byte) { + // amd64:`ROLW\s\$8`,-`SHR.` + // arm64:`MOVH`,`REV16W`,-`MOVB` + // ppc64le:`MOVHBR` + binary.BigEndian.PutUint16(b, sink16) +} + +func store_be16_idx(b []byte, idx int) { + // amd64:`ROLW\s\$8`,-`SHR.` + // arm64:`MOVH\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,`REV16W`,-`MOVB` + // ppc64le:`MOVHBR` + binary.BigEndian.PutUint16(b[idx:], sink16) +} + +func store_le_byte_2(b []byte, val uint16) { + _ = b[2] + // arm64:`MOVH\sR[0-9]+,\s1\(R[0-9]+\)`,-`MOVB` + b[1], b[2] = byte(val), byte(val>>8) +} + +func store_le_byte_4(b []byte, val uint32) { + _ = b[4] + // arm64:`MOVW\sR[0-9]+,\s1\(R[0-9]+\)`,-`MOVB`,-`MOVH` + b[1], b[2], b[3], b[4] = byte(val), byte(val>>8), byte(val>>16), byte(val>>24) +} + +func store_le_byte_8(b []byte, val uint64) { + _ = b[8] + // arm64:`MOVD\sR[0-9]+,\s1\(R[0-9]+\)`,-`MOVB`,-`MOVH`,-`MOVW` + b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8] = byte(val), byte(val>>8), byte(val>>16), byte(val>>24), byte(val>>32), byte(val>>40), byte(val>>48), byte(val>>56) +} + +func store_be_byte_2(b []byte, val uint16) { + _ = b[2] + // arm64:`REV16W`,`MOVH\sR[0-9]+,\s1\(R[0-9]+\)`,-`MOVB` + b[1], b[2] = byte(val>>8), byte(val) +} + +func store_be_byte_4(b []byte, val uint32) { + _ = b[4] + // arm64:`REVW`,`MOVW\sR[0-9]+,\s1\(R[0-9]+\)`,-`MOVB`,-`MOVH`,-`REV16W` + b[1], b[2], b[3], b[4] = byte(val>>24), byte(val>>16), byte(val>>8), byte(val) +} + +func store_be_byte_8(b []byte, val uint64) { + _ = b[8] + // arm64:`REV`,`MOVD\sR[0-9]+,\s1\(R[0-9]+\)`,-`MOVB`,-`MOVH`,-`MOVW`,-`REV16W`,-`REVW` + b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8] = byte(val>>56), byte(val>>48), byte(val>>40), byte(val>>32), byte(val>>24), byte(val>>16), byte(val>>8), byte(val) +} + +func store_le_byte_2_idx(b []byte, idx int, val uint16) { + _, _ = b[idx+0], b[idx+1] + // arm64:`MOVH\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOVB` + b[idx+1], b[idx+0] = byte(val>>8), byte(val) +} + +func store_le_byte_4_idx(b []byte, idx int, val uint32) { + _, _, _, _ = b[idx+0], b[idx+1], b[idx+2], b[idx+3] + // arm64:`MOVW\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOVB`,-`MOVH` + b[idx+3], b[idx+2], b[idx+1], b[idx+0] = byte(val>>24), byte(val>>16), byte(val>>8), byte(val) +} + +func store_be_byte_2_idx(b []byte, idx int, val uint16) { + _, _ = b[idx+0], b[idx+1] + // arm64:`REV16W`,`MOVH\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOVB` + b[idx+0], b[idx+1] = byte(val>>8), byte(val) +} + +func store_be_byte_4_idx(b []byte, idx int, val uint32) { + _, _, _, _ = b[idx+0], b[idx+1], b[idx+2], b[idx+3] + // arm64:`REVW`,`MOVW\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOVB`,-`MOVH`,-`REV16W` + b[idx+0], b[idx+1], b[idx+2], b[idx+3] = byte(val>>24), byte(val>>16), byte(val>>8), byte(val) +} + +func store_be_byte_2_idx2(b []byte, idx int, val uint16) { + _, _ = b[(idx<<1)+0], b[(idx<<1)+1] + // arm64:`REV16W`,`MOVH\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+<<1\)`,-`MOVB` + b[(idx<<1)+0], b[(idx<<1)+1] = byte(val>>8), byte(val) +} + +// ------------- // +// Zeroing // +// ------------- // + +// Check that zero stores are combined into larger stores + +func zero_byte_2(b1, b2 []byte) { + // bounds checks to guarantee safety of writes below + _, _ = b1[1], b2[1] + b1[0], b1[1] = 0, 0 // arm64:"MOVH\tZR",-"MOVB" + b2[1], b2[0] = 0, 0 // arm64:"MOVH\tZR",-"MOVB" +} + +func zero_byte_4(b1, b2 []byte) { + _, _ = b1[3], b2[3] + b1[0], b1[1], b1[2], b1[3] = 0, 0, 0, 0 // arm64:"MOVW\tZR",-"MOVB",-"MOVH" + b2[2], b2[3], b2[1], b2[0] = 0, 0, 0, 0 // arm64:"MOVW\tZR",-"MOVB",-"MOVH" +} + +func zero_byte_8(b []byte) { + _ = b[7] + b[0], b[1], b[2], b[3] = 0, 0, 0, 0 + b[4], b[5], b[6], b[7] = 0, 0, 0, 0 // arm64:"MOVD\tZR",-"MOVB",-"MOVH",-"MOVW" +} + +func zero_byte_16(b []byte) { + _ = b[15] + b[0], b[1], b[2], b[3] = 0, 0, 0, 0 + b[4], b[5], b[6], b[7] = 0, 0, 0, 0 + b[8], b[9], b[10], b[11] = 0, 0, 0, 0 + b[12], b[13], b[14], b[15] = 0, 0, 0, 0 // arm64:"STP",-"MOVB",-"MOVH",-"MOVW" +} + +/* TODO: enable them when corresponding optimization are implemented +func zero_byte_4_idx(b []byte, idx int) { + // arm64: `MOVW\sZR,\s\(R[0-9]+\)\(R[0-9]+<<2\)`,-`MOV[BH]` + b[(idx<<2)+0] = 0 + b[(idx<<2)+1] = 0 + b[(idx<<2)+2] = 0 + b[(idx<<2)+3] = 0 +} + +func zero_byte_8_idx(b []byte, idx int) { + // arm64: `MOVD\sZR,\s\(R[0-9]+\)\(R[0-9]+<<3\)`,-`MOV[BHW]` + b[(idx<<3)+0] = 0 + b[(idx<<3)+1] = 0 + b[(idx<<3)+2] = 0 + b[(idx<<3)+3] = 0 + b[(idx<<3)+4] = 0 + b[(idx<<3)+5] = 0 + b[(idx<<3)+6] = 0 + b[(idx<<3)+7] = 0 +} +*/ + +func zero_byte_30(a *[30]byte) { + *a = [30]byte{} // arm64:"STP",-"MOVB",-"MOVH",-"MOVW" +} + +func zero_byte_39(a *[39]byte) { + *a = [39]byte{} // arm64:"MOVD",-"MOVB",-"MOVH",-"MOVW" +} + +func zero_byte_2_idx(b []byte, idx int) { + _, _ = b[idx+0], b[idx+1] + // arm64:`MOVH\sZR,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOVB` + b[idx+0], b[idx+1] = 0, 0 +} + +func zero_byte_2_idx2(b []byte, idx int) { + _, _ = b[(idx<<1)+0], b[(idx<<1)+1] + // arm64:`MOVH\sZR,\s\(R[0-9]+\)\(R[0-9]+<<1\)`,-`MOVB` + b[(idx<<1)+0], b[(idx<<1)+1] = 0, 0 +} + +func zero_uint16_2(h1, h2 []uint16) { + _, _ = h1[1], h2[1] + h1[0], h1[1] = 0, 0 // arm64:"MOVW\tZR",-"MOVB",-"MOVH" + h2[1], h2[0] = 0, 0 // arm64:"MOVW\tZR",-"MOVB",-"MOVH" +} + +func zero_uint16_4(h1, h2 []uint16) { + _, _ = h1[3], h2[3] + h1[0], h1[1], h1[2], h1[3] = 0, 0, 0, 0 // arm64:"MOVD\tZR",-"MOVB",-"MOVH",-"MOVW" + h2[2], h2[3], h2[1], h2[0] = 0, 0, 0, 0 // arm64:"MOVD\tZR",-"MOVB",-"MOVH",-"MOVW" +} + +func zero_uint16_8(h []uint16) { + _ = h[7] + h[0], h[1], h[2], h[3] = 0, 0, 0, 0 + h[4], h[5], h[6], h[7] = 0, 0, 0, 0 // arm64:"STP",-"MOVB",-"MOVH" +} + +func zero_uint32_2(w1, w2 []uint32) { + _, _ = w1[1], w2[1] + w1[0], w1[1] = 0, 0 // arm64:"MOVD\tZR",-"MOVB",-"MOVH",-"MOVW" + w2[1], w2[0] = 0, 0 // arm64:"MOVD\tZR",-"MOVB",-"MOVH",-"MOVW" +} + +func zero_uint32_4(w1, w2 []uint32) { + _, _ = w1[3], w2[3] + w1[0], w1[1], w1[2], w1[3] = 0, 0, 0, 0 // arm64:"STP",-"MOVB",-"MOVH" + w2[2], w2[3], w2[1], w2[0] = 0, 0, 0, 0 // arm64:"STP",-"MOVB",-"MOVH" +} + +func zero_uint64_2(d1, d2 []uint64) { + _, _ = d1[1], d2[1] + d1[0], d1[1] = 0, 0 // arm64:"STP",-"MOVB",-"MOVH" + d2[1], d2[0] = 0, 0 // arm64:"STP",-"MOVB",-"MOVH" +} diff --git a/test/codegen/rotate.go b/test/codegen/rotate.go new file mode 100644 index 000000000..5812e1c0b --- /dev/null +++ b/test/codegen/rotate.go @@ -0,0 +1,160 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +// ------------------- // +// const rotates // +// ------------------- // + +func rot64(x uint64) uint64 { + var a uint64 + + // amd64:"ROLQ\t[$]7" + // arm64:"ROR\t[$]57" + // s390x:"RLLG\t[$]7" + // ppc64le:"ROTL\t[$]7" + a += x<<7 | x>>57 + + // amd64:"ROLQ\t[$]8" + // arm64:"ROR\t[$]56" + // s390x:"RLLG\t[$]8" + // ppc64le:"ROTL\t[$]8" + a += x<<8 + x>>56 + + // amd64:"ROLQ\t[$]9" + // arm64:"ROR\t[$]55" + // s390x:"RLLG\t[$]9" + // ppc64le:"ROTL\t[$]9" + a += x<<9 ^ x>>55 + + return a +} + +func rot32(x uint32) uint32 { + var a uint32 + + // amd64:"ROLL\t[$]7" + // arm:"MOVW\tR\\d+@>25" + // arm64:"RORW\t[$]25" + // s390x:"RLL\t[$]7" + // ppc64le:"ROTLW\t[$]7" + a += x<<7 | x>>25 + + // amd64:`ROLL\t[$]8` + // arm:"MOVW\tR\\d+@>24" + // arm64:"RORW\t[$]24" + // s390x:"RLL\t[$]8" + // ppc64le:"ROTLW\t[$]8" + a += x<<8 + x>>24 + + // amd64:"ROLL\t[$]9" + // arm:"MOVW\tR\\d+@>23" + // arm64:"RORW\t[$]23" + // s390x:"RLL\t[$]9" + // ppc64le:"ROTLW\t[$]9" + a += x<<9 ^ x>>23 + + return a +} + +func rot16(x uint16) uint16 { + var a uint16 + + // amd64:"ROLW\t[$]7" + a += x<<7 | x>>9 + + // amd64:`ROLW\t[$]8` + a += x<<8 + x>>8 + + // amd64:"ROLW\t[$]9" + a += x<<9 ^ x>>7 + + return a +} + +func rot8(x uint8) uint8 { + var a uint8 + + // amd64:"ROLB\t[$]5" + a += x<<5 | x>>3 + + // amd64:`ROLB\t[$]6` + a += x<<6 + x>>2 + + // amd64:"ROLB\t[$]7" + a += x<<7 ^ x>>1 + + return a +} + +// ----------------------- // +// non-const rotates // +// ----------------------- // + +func rot64nc(x uint64, z uint) uint64 { + var a uint64 + + z &= 63 + + // amd64:"ROLQ" + // ppc64le:"ROTL" + a += x<<z | x>>(64-z) + + // amd64:"RORQ" + a += x>>z | x<<(64-z) + + return a +} + +func rot32nc(x uint32, z uint) uint32 { + var a uint32 + + z &= 31 + + // amd64:"ROLL" + // ppc64le:"ROTLW" + a += x<<z | x>>(32-z) + + // amd64:"RORL" + a += x>>z | x<<(32-z) + + return a +} + +func rot16nc(x uint16, z uint) uint16 { + var a uint16 + + z &= 15 + + // amd64:"ROLW" + a += x<<z | x>>(16-z) + + // amd64:"RORW" + a += x>>z | x<<(16-z) + + return a +} + +func rot8nc(x uint8, z uint) uint8 { + var a uint8 + + z &= 7 + + // amd64:"ROLB" + a += x<<z | x>>(8-z) + + // amd64:"RORB" + a += x>>z | x<<(8-z) + + return a +} + +// Issue 18254: rotate after inlining +func f32(x uint32) uint32 { + // amd64:"ROLL\t[$]7" + return rot32nc(x, 7) +} diff --git a/test/codegen/shift.go b/test/codegen/shift.go new file mode 100644 index 000000000..93fa82886 --- /dev/null +++ b/test/codegen/shift.go @@ -0,0 +1,99 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +// ------------------ // +// masked shifts // +// ------------------ // + +func lshMask64x64(v int64, s uint64) int64 { + // s390x:-".*AND",-".*MOVDGE" + return v << (s&63) +} + +func rshMask64Ux64(v uint64, s uint64) uint64 { + // s390x:-".*AND",-".*MOVDGE" + return v >> (s&63) +} + +func rshMask64x64(v int64, s uint64) int64 { + // s390x:-".*AND",-".*MOVDGE" + return v >> (s&63) +} + +func lshMask32x64(v int32, s uint64) int32 { + // s390x:-".*AND",-".*MOVDGE" + return v << (s&63) +} + +func rshMask32Ux64(v uint32, s uint64) uint32 { + // s390x:-".*AND",-".*MOVDGE" + return v >> (s&63) +} + +func rshMask32x64(v int32, s uint64) int32 { + // s390x:-".*AND",-".*MOVDGE" + return v >> (s&63) +} + +func lshMask64x32(v int64, s uint32) int64 { + // s390x:-".*AND",-".*MOVDGE" + return v << (s&63) +} + +func rshMask64Ux32(v uint64, s uint32) uint64 { + // s390x:-".*AND",-".*MOVDGE" + return v >> (s&63) +} + +func rshMask64x32(v int64, s uint32) int64 { + // s390x:-".*AND",-".*MOVDGE" + return v >> (s&63) +} + +func lshMask64x32Ext(v int64, s int32) int64 { + // s390x:-".*AND",-".*MOVDGE" + return v << uint(s&63) +} + +func rshMask64Ux32Ext(v uint64, s int32) uint64 { + // s390x:-".*AND",-".*MOVDGE" + return v >> uint(s&63) +} + +func rshMask64x32Ext(v int64, s int32) int64 { + // s390x:-".*AND",-".*MOVDGE" + return v >> uint(s&63) +} + +// ------------------ // +// bounded shifts // +// ------------------ // + +func lshGuarded64(v int64, s uint) int64 { + if s < 64 { + // s390x:-".*AND",-".*MOVDGE" + return v >> s + } + panic("shift too large") +} + +func rshGuarded64U(v uint64, s uint) uint64 { + if s < 64 { + // s390x:-".*AND",-".*MOVDGE" + return v >> s + } + panic("shift too large") +} + +func rshGuarded64(v int64, s uint) int64 { + if s < 64 { + // s390x:-".*AND",-".*MOVDGE" + return v << s + } + panic("shift too large") +} diff --git a/test/codegen/slices.go b/test/codegen/slices.go new file mode 100644 index 000000000..15dbcee73 --- /dev/null +++ b/test/codegen/slices.go @@ -0,0 +1,63 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +// This file contains code generation tests related to the handling of +// slice types. + +// ------------------ // +// Clear // +// ------------------ // + +// Issue #5373 optimize memset idiom + +func SliceClear(s []int) []int { + // amd64:`.*memclrNoHeapPointers` + for i := range s { + s[i] = 0 + } + return s +} + +func SliceClearPointers(s []*int) []*int { + // amd64:`.*memclrHasPointers` + for i := range s { + s[i] = nil + } + return s +} + +// ------------------ // +// Extension // +// ------------------ // + +// Issue #21266 - avoid makeslice in append(x, make([]T, y)...) + +func SliceExtensionConst(s []int) []int { + // amd64:`.*runtime\.memclrNoHeapPointers` + // amd64:-`.*runtime\.makeslice` + // amd64:-`.*runtime\.panicmakeslicelen` + return append(s, make([]int, 1<<2)...) +} + +func SliceExtensionPointer(s []*int, l int) []*int { + // amd64:`.*runtime\.memclrHasPointers` + // amd64:-`.*runtime\.makeslice` + return append(s, make([]*int, l)...) +} + +func SliceExtensionVar(s []byte, l int) []byte { + // amd64:`.*runtime\.memclrNoHeapPointers` + // amd64:-`.*runtime\.makeslice` + return append(s, make([]byte, l)...) +} + +func SliceExtensionInt64(s []int, l64 int64) []int { + // 386:`.*runtime\.makeslice` + // 386:-`.*runtime\.memclr` + return append(s, make([]int, l64)...) +} diff --git a/test/codegen/stack.go b/test/codegen/stack.go new file mode 100644 index 000000000..7e12dbc0e --- /dev/null +++ b/test/codegen/stack.go @@ -0,0 +1,95 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +import "runtime" + +// This file contains code generation tests related to the use of the +// stack. + +// Check that stack stores are optimized away. + +// 386:"TEXT\t.*, [$]0-" +// amd64:"TEXT\t.*, [$]0-" +// arm:"TEXT\t.*, [$]-4-" +// arm64:"TEXT\t.*, [$]-8-" +// mips:"TEXT\t.*, [$]-4-" +// ppc64le:"TEXT\t.*, [$]0-" +// s390x:"TEXT\t.*, [$]0-" +func StackStore() int { + var x int + return *(&x) +} + +type T struct { + A, B, C, D int // keep exported fields + x, y, z int // reset unexported fields +} + +// Check that large structs are cleared directly (issue #24416). + +// 386:"TEXT\t.*, [$]0-" +// amd64:"TEXT\t.*, [$]0-" +// arm:"TEXT\t.*, [$]0-" (spills return address) +// arm64:"TEXT\t.*, [$]-8-" +// mips:"TEXT\t.*, [$]-4-" +// ppc64le:"TEXT\t.*, [$]0-" +// s390x:"TEXT\t.*, [$]0-" +func ZeroLargeStruct(x *T) { + t := T{} + *x = t +} + +// Check that structs are partially initialised directly (issue #24386). + +// Notes: +// - 386 fails due to spilling a register +// amd64:"TEXT\t.*, [$]0-" +// arm:"TEXT\t.*, [$]0-" (spills return address) +// arm64:"TEXT\t.*, [$]-8-" +// ppc64le:"TEXT\t.*, [$]0-" +// s390x:"TEXT\t.*, [$]0-" +// Note: that 386 currently has to spill a register. +func KeepWanted(t *T) { + *t = T{A: t.A, B: t.B, C: t.C, D: t.D} +} + +// Check that small array operations avoid using the stack (issue #15925). + +// Notes: +// - 386 fails due to spilling a register +// - arm & mips fail due to softfloat calls +// amd64:"TEXT\t.*, [$]0-" +// arm64:"TEXT\t.*, [$]-8-" +// ppc64le:"TEXT\t.*, [$]0-" +// s390x:"TEXT\t.*, [$]0-" +func ArrayAdd64(a, b [4]float64) [4]float64 { + return [4]float64{a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]} +} + +// Check that small array initialization avoids using the stack. + +// 386:"TEXT\t.*, [$]0-" +// amd64:"TEXT\t.*, [$]0-" +// arm:"TEXT\t.*, [$]0-" (spills return address) +// arm64:"TEXT\t.*, [$]-8-" +// mips:"TEXT\t.*, [$]-4-" +// ppc64le:"TEXT\t.*, [$]0-" +// s390x:"TEXT\t.*, [$]0-" +func ArrayInit(i, j int) [4]int { + return [4]int{i, 0, j, 0} +} + +// Check that assembly output has matching offset and base register +// (issue #21064). + +func check_asmout(a, b int) int { + runtime.GC() // use some frame + // amd64:`.*b\+24\(SP\)` + // arm:`.*b\+4\(FP\)` + return b +} diff --git a/test/codegen/strings.go b/test/codegen/strings.go new file mode 100644 index 000000000..ccb6bd427 --- /dev/null +++ b/test/codegen/strings.go @@ -0,0 +1,15 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +// This file contains code generation tests related to the handling of +// string types. + +func CountRunes(s string) int { // Issue #24923 + // amd64:`.*countrunes` + return len([]rune(s)) +} diff --git a/test/codegen/structs.go b/test/codegen/structs.go new file mode 100644 index 000000000..b81ad67c4 --- /dev/null +++ b/test/codegen/structs.go @@ -0,0 +1,46 @@ +// asmcheck + +// Copyright 2018 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. + +package codegen + +// This file contains code generation tests related to the handling of +// struct types. + +// ------------- // +// Zeroing // +// ------------- // + +type Z1 struct { + a, b, c int +} + +func Zero1(t *Z1) { // Issue #18370 + // amd64:`XORPS\tX., X`,`MOVUPS\tX., \(.*\)`,`MOVQ\t\$0, 16\(.*\)` + *t = Z1{} +} + +type Z2 struct { + a, b, c *int +} + +func Zero2(t *Z2) { + // amd64:`XORPS\tX., X`,`MOVUPS\tX., \(.*\)`,`MOVQ\t\$0, 16\(.*\)` + // amd64:`.*runtime[.]gcWriteBarrier\(SB\)` + *t = Z2{} +} + +// ------------------ // +// Initializing // +// ------------------ // + +type I1 struct { + a, b, c, d int +} + +func Init1(p *I1) { // Issue #18872 + // amd64:`MOVQ\t[$]1`,`MOVQ\t[$]2`,`MOVQ\t[$]3`,`MOVQ\t[$]4` + *p = I1{1, 2, 3, 4} +} diff --git a/test/const1.go b/test/const1.go index 58bddee7e..62abe4145 100644 --- a/test/const1.go +++ b/test/const1.go @@ -90,5 +90,5 @@ func main() { const ptr = nil // ERROR "const.*nil" const _ = string([]byte(nil)) // ERROR "is not a? ?constant" const _ = uintptr(unsafe.Pointer((*int)(nil))) // ERROR "is not a? ?constant" -const _ = unsafe.Pointer((*int)(nil)) // ERROR "cannot be nil|invalid constant type" -const _ = (*int)(nil) // ERROR "cannot be nil|invalid constant type" +const _ = unsafe.Pointer((*int)(nil)) // ERROR "cannot be nil|invalid constant type|is not a constant" +const _ = (*int)(nil) // ERROR "cannot be nil|invalid constant type|is not a constant" diff --git a/test/deferprint.go b/test/deferprint.go index 3dc08542c..b74677ac5 100644 --- a/test/deferprint.go +++ b/test/deferprint.go @@ -1,4 +1,4 @@ -// cmpout +// run // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/escape4.go b/test/escape4.go index 22a37c1d0..0fe330539 100644 --- a/test/escape4.go +++ b/test/escape4.go @@ -34,8 +34,8 @@ func f1() { func f2() {} // ERROR "can inline f2" -// No inline for panic, recover. -func f3() { panic(1) } +// No inline for recover; panic now allowed to inline. +func f3() { panic(1) } // ERROR "can inline f3" func f4() { recover() } func f5() *byte { diff --git a/test/escape5.go b/test/escape5.go index 7d6ef554a..03283a37f 100644 --- a/test/escape5.go +++ b/test/escape5.go @@ -163,3 +163,68 @@ func f13() { f12(&x) // ERROR "&x does not escape" runtime.KeepAlive(&x) // ERROR "&x does not escape" } + +// Test for issue 24305 (passing to unnamed receivers does not escape). +type U int + +func (*U) M() {} +func (_ *U) N() {} + +func _() { + var u U + u.M() // ERROR "u does not escape" + u.N() // ERROR "u does not escape" +} + +// Issue 24730: taking address in a loop causes unnecessary escape +type T24730 struct { + x [64]byte +} + +func (t *T24730) g() { // ERROR "t does not escape" + y := t.x[:] // ERROR "t\.x does not escape" + for i := range t.x[:] { // ERROR "t\.x does not escape" + y = t.x[:] // ERROR "t\.x does not escape" + y[i] = 1 + } + + var z *byte + for i := range t.x[:] { // ERROR "t\.x does not escape" + z = &t.x[i] // ERROR "t\.x\[i\] does not escape" + *z = 2 + } +} + +// Issue 15730: copy causes unnecessary escape + +var sink []byte +var sink2 []int +var sink3 []*int + +func f15730a(args ...interface{}) { // ERROR "args does not escape" + for _, arg := range args { + switch a := arg.(type) { + case string: + copy(sink, a) + } + } +} + +func f15730b(args ...interface{}) { // ERROR "args does not escape" + for _, arg := range args { + switch a := arg.(type) { + case []int: + copy(sink2, a) + } + } +} + +func f15730c(args ...interface{}) { // ERROR "leaking param content: args" + for _, arg := range args { + switch a := arg.(type) { + case []*int: + // copy pointerful data should cause escape + copy(sink3, a) + } + } +} diff --git a/test/escape_array.go b/test/escape_array.go index 0204c690c..c2c3e2c85 100644 --- a/test/escape_array.go +++ b/test/escape_array.go @@ -120,3 +120,10 @@ func doesMakeSlice(x *string, y *string) { // ERROR "leaking param: x" "leaking b := make([]*string, 65537) // ERROR "make\(\[\]\*string, 65537\) escapes to heap" b[0] = y } + +func nonconstArray() { + n := 32 + s1 := make([]int, n) // ERROR "make\(\[\]int, n\) escapes to heap" + s2 := make([]int, 0, n) // ERROR "make\(\[\]int, 0, n\) escapes to heap" + _, _ = s1, s2 +} diff --git a/test/escape_because.go b/test/escape_because.go index 7d349b7a1..c7548fc67 100644 --- a/test/escape_because.go +++ b/test/escape_because.go @@ -118,6 +118,13 @@ func transmit(b []byte) []byte { // ERROR "from ~r1 \(return\) at escape_because return b } +func f14() { + n := 32 + s1 := make([]int, n) // ERROR "make\(\[\]int, n\) escapes to heap" "from make\(\[\]int, n\) \(non-constant size\)" + s2 := make([]int, 0, n) // ERROR "make\(\[\]int, 0, n\) escapes to heap" "from make\(\[\]int, 0, n\) \(non-constant size\)" + _, _ = s1, s2 +} + // The list below is all of the why-escapes messages seen building the escape analysis tests. /* for i in escape*go ; do echo compile $i; go build -gcflags '-l -m -m' $i >& `basename $i .go`.log ; done @@ -134,51 +141,40 @@ appended to slice appendee slice arg to ... arg to recursive call -array literal element array-element-equals -assign-pair -assign-pair-dot-type -assign-pair-func-call +array literal element assigned assigned to top level variable -call part +assign-pair-dot-type +assign-pair-func-call captured by a closure -closure-var -converted -copied slice -defer func -defer func ... -defer func arg +captured by called closure dot -dot of pointer dot-equals +dot of pointer fixed-array-index-of -go func -go func ... go func arg indirection interface-converted key of map put map literal key map literal value +non-constant size +panic parameter to indirect call +passed-to-and-returned-from-call passed to call[argument content escapes] passed to call[argument escapes] -passed-to-and-returned-from-call pointer literal -range range-deref receiver in indirect call return returned from recursive function -send -slice slice-element-equals slice-literal-element star-dot-equals star-equals struct literal element -switch case too large for stack value of map put */ @@ -191,7 +187,6 @@ assign-pair-mapr assign-pair-receive call receiver map index -panic pointer literal [assign] slice literal element */ diff --git a/test/fixedbugs/bug206.go b/test/fixedbugs/bug206.go index c2382acf1..91efa3ff7 100644 --- a/test/fixedbugs/bug206.go +++ b/test/fixedbugs/bug206.go @@ -1,4 +1,4 @@ -// cmpout +// run // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/fixedbugs/bug248.dir/bug1.go b/test/fixedbugs/bug248.dir/bug1.go index 78433f504..f1db77d2f 100644 --- a/test/fixedbugs/bug248.dir/bug1.go +++ b/test/fixedbugs/bug248.dir/bug1.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file -package p +package q type T struct { X, Y int diff --git a/test/fixedbugs/bug248.dir/bug2.go b/test/fixedbugs/bug248.dir/bug2.go index ba547d64a..c0fdecfdb 100644 --- a/test/fixedbugs/bug248.dir/bug2.go +++ b/test/fixedbugs/bug248.dir/bug2.go @@ -2,19 +2,20 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file -package main +package s import ( p0 "./bug0" p1 "./bug1" - - "reflect" - "strings" ) +// both p0.T and p1.T are struct { X, Y int }. + var v0 p0.T var v1 p1.T +// interfaces involving the two + type I0 interface { M(p0.T) } @@ -23,83 +24,50 @@ type I1 interface { M(p1.T) } +// t0 satisfies I0 and p0.I type t0 int func (t0) M(p0.T) {} +// t1 satisfies I1 and p1.I type t1 float64 func (t1) M(p1.T) {} +// check static interface assignments var i0 I0 = t0(0) // ok var i1 I1 = t1(0) // ok +var i2 I0 = t1(0) // ERROR "does not implement|incompatible" +var i3 I1 = t0(0) // ERROR "does not implement|incompatible" + var p0i p0.I = t0(0) // ok var p1i p1.I = t1(0) // ok -func main() { - // check that reflect paths are correct, - // meaning that reflect data for v0, v1 didn't get confused. - - // path is full (rooted) path name. check suffix for gc, prefix for gccgo - if s := reflect.TypeOf(v0).PkgPath(); !strings.HasSuffix(s, "/bug0") && !strings.HasPrefix(s, "bug0") { - println("bad v0 path", len(s), s) - panic("fail") - } - if s := reflect.TypeOf(v1).PkgPath(); !strings.HasSuffix(s, "/bug1") && !strings.HasPrefix(s, "bug1") { - println("bad v1 path", s) - panic("fail") - } - - // check that dynamic interface check doesn't get confused - var i interface{} = t0(0) - if _, ok := i.(I1); ok { - println("used t0 as i1") - panic("fail") - } - if _, ok := i.(p1.I); ok { - println("used t0 as p1.I") - panic("fail") - } - - i = t1(1) - if _, ok := i.(I0); ok { - println("used t1 as i0") - panic("fail") - } - if _, ok := i.(p0.I); ok { - println("used t1 as p0.I") - panic("fail") - } - - // check that type switch works. - // the worry is that if p0.T and p1.T have the same hash, - // the binary search will handle one of them incorrectly. - for j := 0; j < 3; j++ { - switch j { - case 0: - i = p0.T{} - case 1: - i = p1.T{} - case 2: - i = 3.14 - } - switch i.(type) { - case p0.T: - if j != 0 { - println("type switch p0.T") - panic("fail") - } - case p1.T: - if j != 1 { - println("type switch p1.T") - panic("fail") - } - default: - if j != 2 { - println("type switch default", j) - panic("fail") - } - } - } +var p0i1 p0.I = t1(0) // ERROR "does not implement|incompatible" +var p0i2 p1.I = t0(0) // ERROR "does not implement|incompatible" + +func foobar() { + // check that cannot assign one to the other, + // but can convert. + v0 = v1 // ERROR "assign" + v1 = v0 // ERROR "assign" + + v0 = p0.T(v1) + v1 = p1.T(v0) + + i0 = i1 // ERROR "cannot use|incompatible" + i1 = i0 // ERROR "cannot use|incompatible" + p0i = i1 // ERROR "cannot use|incompatible" + p1i = i0 // ERROR "cannot use|incompatible" + i0 = p1i // ERROR "cannot use|incompatible" + i1 = p0i // ERROR "cannot use|incompatible" + p0i = p1i // ERROR "cannot use|incompatible" + p1i = p0i // ERROR "cannot use|incompatible" + + i0 = p0i + p0i = i0 + + i1 = p1i + p1i = i1 } diff --git a/test/fixedbugs/bug248.dir/bug3.go b/test/fixedbugs/bug248.dir/bug3.go index 4a56c5cc8..ba547d64a 100644 --- a/test/fixedbugs/bug248.dir/bug3.go +++ b/test/fixedbugs/bug248.dir/bug3.go @@ -7,15 +7,14 @@ package main import ( p0 "./bug0" p1 "./bug1" -) -// both p0.T and p1.T are struct { X, Y int }. + "reflect" + "strings" +) var v0 p0.T var v1 p1.T -// interfaces involving the two - type I0 interface { M(p0.T) } @@ -24,50 +23,83 @@ type I1 interface { M(p1.T) } -// t0 satisfies I0 and p0.I type t0 int func (t0) M(p0.T) {} -// t1 satisfies I1 and p1.I type t1 float64 func (t1) M(p1.T) {} -// check static interface assignments var i0 I0 = t0(0) // ok var i1 I1 = t1(0) // ok -var i2 I0 = t1(0) // ERROR "does not implement|incompatible" -var i3 I1 = t0(0) // ERROR "does not implement|incompatible" - var p0i p0.I = t0(0) // ok var p1i p1.I = t1(0) // ok -var p0i1 p0.I = t1(0) // ERROR "does not implement|incompatible" -var p0i2 p1.I = t0(0) // ERROR "does not implement|incompatible" - func main() { - // check that cannot assign one to the other, - // but can convert. - v0 = v1 // ERROR "assign" - v1 = v0 // ERROR "assign" - - v0 = p0.T(v1) - v1 = p1.T(v0) - - i0 = i1 // ERROR "cannot use|incompatible" - i1 = i0 // ERROR "cannot use|incompatible" - p0i = i1 // ERROR "cannot use|incompatible" - p1i = i0 // ERROR "cannot use|incompatible" - i0 = p1i // ERROR "cannot use|incompatible" - i1 = p0i // ERROR "cannot use|incompatible" - p0i = p1i // ERROR "cannot use|incompatible" - p1i = p0i // ERROR "cannot use|incompatible" - - i0 = p0i - p0i = i0 - - i1 = p1i - p1i = i1 + // check that reflect paths are correct, + // meaning that reflect data for v0, v1 didn't get confused. + + // path is full (rooted) path name. check suffix for gc, prefix for gccgo + if s := reflect.TypeOf(v0).PkgPath(); !strings.HasSuffix(s, "/bug0") && !strings.HasPrefix(s, "bug0") { + println("bad v0 path", len(s), s) + panic("fail") + } + if s := reflect.TypeOf(v1).PkgPath(); !strings.HasSuffix(s, "/bug1") && !strings.HasPrefix(s, "bug1") { + println("bad v1 path", s) + panic("fail") + } + + // check that dynamic interface check doesn't get confused + var i interface{} = t0(0) + if _, ok := i.(I1); ok { + println("used t0 as i1") + panic("fail") + } + if _, ok := i.(p1.I); ok { + println("used t0 as p1.I") + panic("fail") + } + + i = t1(1) + if _, ok := i.(I0); ok { + println("used t1 as i0") + panic("fail") + } + if _, ok := i.(p0.I); ok { + println("used t1 as p0.I") + panic("fail") + } + + // check that type switch works. + // the worry is that if p0.T and p1.T have the same hash, + // the binary search will handle one of them incorrectly. + for j := 0; j < 3; j++ { + switch j { + case 0: + i = p0.T{} + case 1: + i = p1.T{} + case 2: + i = 3.14 + } + switch i.(type) { + case p0.T: + if j != 0 { + println("type switch p0.T") + panic("fail") + } + case p1.T: + if j != 1 { + println("type switch p1.T") + panic("fail") + } + default: + if j != 2 { + println("type switch default", j) + panic("fail") + } + } + } } diff --git a/test/fixedbugs/bug248.go b/test/fixedbugs/bug248.go index 173b46fd8..93d2fdb67 100644 --- a/test/fixedbugs/bug248.go +++ b/test/fixedbugs/bug248.go @@ -1,53 +1,12 @@ -// +build !nacl,!plan9,!windows -// run +// +build !nacl,!js,!plan9 +// errorcheckandrundir -1 // Copyright 2009 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. -package main +package ignored -import ( - "fmt" - "os" - "os/exec" - "path/filepath" -) - -func main() { - // TODO: If we get rid of errchk, re-enable this test on Windows. - errchk, err := filepath.Abs("errchk") - check(err) - - err = os.Chdir(filepath.Join("fixedbugs", "bug248.dir")) - check(err) - - run("go", "tool", "compile", "bug0.go") - run("go", "tool", "compile", "bug1.go") - run("go", "tool", "compile", "bug2.go") - run(errchk, "go", "tool", "compile", "-e", "bug3.go") - run("go", "tool", "link", "bug2.o") - run(fmt.Sprintf(".%ca.out", filepath.Separator)) - - os.Remove("bug0.o") - os.Remove("bug1.o") - os.Remove("bug2.o") - os.Remove("a.out") -} - -func run(name string, args ...string) { - cmd := exec.Command(name, args...) - out, err := cmd.CombinedOutput() - if err != nil { - fmt.Println(string(out)) - fmt.Println(err) - os.Exit(1) - } -} - -func check(err error) { - if err != nil { - fmt.Println(err) - os.Exit(1) - } -} +// Compile: bug0.go, bug1.go +// Compile and errorCheck: bug2.go +// Link and run: bug3.go diff --git a/test/fixedbugs/bug273.go b/test/fixedbugs/bug273.go index c04f2116c..7305c6063 100644 --- a/test/fixedbugs/bug273.go +++ b/test/fixedbugs/bug273.go @@ -8,13 +8,15 @@ package main +import "unsafe" + var bug = false var minus1 = -1 var five = 5 -var big int64 = 10 | 1<<32 +var big int64 = 10 | 1<<40 -type block [1<<19]byte +type block [1 << 19]byte var g1 []block @@ -48,9 +50,10 @@ func bigcap() { g1 = make([]block, 10, big) } -type cblock [1<<16-1]byte +type cblock [1<<16 - 1]byte var g4 chan cblock + func badchancap() { g4 = make(chan cblock, minus1) } @@ -60,7 +63,8 @@ func bigchancap() { } func overflowchan() { - g4 = make(chan cblock, 1<<30) + const ptrSize = unsafe.Sizeof(uintptr(0)) + g4 = make(chan cblock, 1<<(30*(ptrSize/4))) } func main() { diff --git a/test/fixedbugs/bug302.go b/test/fixedbugs/bug302.go index e4de25d5d..c763b8778 100644 --- a/test/fixedbugs/bug302.go +++ b/test/fixedbugs/bug302.go @@ -1,4 +1,4 @@ -// +build !nacl +// +build !nacl,!js // run // Copyright 2010 The Go Authors. All rights reserved. diff --git a/test/fixedbugs/bug328.go b/test/fixedbugs/bug328.go index 180af05fd..57043f30a 100644 --- a/test/fixedbugs/bug328.go +++ b/test/fixedbugs/bug328.go @@ -1,4 +1,4 @@ -// cmpout +// run // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/fixedbugs/bug345.dir/main.go b/test/fixedbugs/bug345.dir/main.go index 6e4fdf4e2..b77a2fad5 100644 --- a/test/fixedbugs/bug345.dir/main.go +++ b/test/fixedbugs/bug345.dir/main.go @@ -6,8 +6,9 @@ package main import ( "bufio" - "./io" goio "io" + + "./io" ) func main() { @@ -22,7 +23,7 @@ func main() { // main.go:27: cannot use &x (type *"io".SectionReader) as type *"/Users/rsc/g/go/test/fixedbugs/bug345.dir/io".SectionReader in function argument var w io.Writer - bufio.NewWriter(w) // ERROR "test/io|has incompatible type" + bufio.NewWriter(w) // ERROR "[\w.]+[^.]/io|has incompatible type" var x goio.SectionReader - io.SR(&x) // ERROR "test/io|has incompatible type" + io.SR(&x) // ERROR "[\w.]+[^.]/io|has incompatible type" } diff --git a/test/fixedbugs/bug345.go b/test/fixedbugs/bug345.go index dcf62f059..b974a61ff 100644 --- a/test/fixedbugs/bug345.go +++ b/test/fixedbugs/bug345.go @@ -1,45 +1,10 @@ -// +build !nacl,!plan9,!windows -// run +// +build !windows +// errorcheckdir -n // Copyright 2011 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. -package main +package ignored -import ( - "fmt" - "os" - "os/exec" - "path/filepath" -) - -func main() { - // TODO: If we get rid of errchk, re-enable this test on Plan 9 and Windows. - errchk, err := filepath.Abs("errchk") - check(err) - - err = os.Chdir(filepath.Join(".", "fixedbugs", "bug345.dir")) - check(err) - - run("go", "tool", "compile", "io.go") - run(errchk, "go", "tool", "compile", "-e", "main.go") - os.Remove("io.o") -} - -func run(name string, args ...string) { - cmd := exec.Command(name, args...) - out, err := cmd.CombinedOutput() - if err != nil { - fmt.Println(string(out)) - fmt.Println(err) - os.Exit(1) - } -} - -func check(err error) { - if err != nil { - fmt.Println(err) - os.Exit(1) - } -} +// TODO(ysmolsky): Fix golang.org/issue/25693 to enable on Windows. diff --git a/test/fixedbugs/bug369.go b/test/fixedbugs/bug369.go index 60162ab1c..e2a114773 100644 --- a/test/fixedbugs/bug369.go +++ b/test/fixedbugs/bug369.go @@ -1,4 +1,4 @@ -// +build !nacl,!windows +// +build !nacl,!js,!windows // run // Copyright 2011 The Go Authors. All rights reserved. diff --git a/test/fixedbugs/bug409.go b/test/fixedbugs/bug409.go index 9e08a8e67..e8546361a 100644 --- a/test/fixedbugs/bug409.go +++ b/test/fixedbugs/bug409.go @@ -1,4 +1,4 @@ -// cmpout +// run // Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/fixedbugs/bug429_run.go b/test/fixedbugs/bug429_run.go index 284033d1f..c6a02aae5 100644 --- a/test/fixedbugs/bug429_run.go +++ b/test/fixedbugs/bug429_run.go @@ -1,4 +1,4 @@ -// +build !nacl +// +build !nacl,!js // run // Copyright 2014 The Go Authors. All rights reserved. diff --git a/test/fixedbugs/issue10958.go b/test/fixedbugs/issue10958.go index 2b7669413..52487fb9b 100644 --- a/test/fixedbugs/issue10958.go +++ b/test/fixedbugs/issue10958.go @@ -1,4 +1,4 @@ -// +build !nacl,disabled_see_issue_18589 +// +build !nacl,!js,disabled_see_issue_18589 // buildrun -t 10 -gcflags=-d=ssa/insert_resched_checks/on,ssa/check/on // Copyright 2016 The Go Authors. All rights reserved. diff --git a/test/fixedbugs/issue11656.go b/test/fixedbugs/issue11656.go index c04a66202..451ae6348 100644 --- a/test/fixedbugs/issue11656.go +++ b/test/fixedbugs/issue11656.go @@ -9,6 +9,9 @@ // doesn't go into the Go runtime. // +build !windows +// wasm does not work, because the linear memory is not executable. +// +build !wasm + package main import ( diff --git a/test/fixedbugs/issue11771.go b/test/fixedbugs/issue11771.go index d91fc5d96..99d7060d4 100644 --- a/test/fixedbugs/issue11771.go +++ b/test/fixedbugs/issue11771.go @@ -1,4 +1,4 @@ -// +build !nacl +// +build !nacl,!js // run // Copyright 2015 The Go Authors. All rights reserved. diff --git a/test/fixedbugs/issue12621.go b/test/fixedbugs/issue12621.go new file mode 100644 index 000000000..379a362d7 --- /dev/null +++ b/test/fixedbugs/issue12621.go @@ -0,0 +1,20 @@ +// run + +// Copyright 2018 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. + +// Issues 12576 and 12621: Negative untyped floating point constants +// with small magnitude round to 0, not negative zero. + +package main + +import "math" + +var m = -1e-10000 + +func main() { + if math.Signbit(m) { + panic(m) + } +} diff --git a/test/fixedbugs/issue14321.go b/test/fixedbugs/issue14321.go new file mode 100644 index 000000000..058008c38 --- /dev/null +++ b/test/fixedbugs/issue14321.go @@ -0,0 +1,33 @@ +// errorcheck + +// Copyright 2018 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. + +// Verify that error message reports _ambiguous_ method. + +package p + +type A struct{ + H int +} + +func (A) F() {} +func (A) G() {} + +type B struct{ + G int + H int +} + +func (B) F() {} + +type C struct { + A + B +} + +var _ = C.F // ERROR "ambiguous selector" +var _ = C.G // ERROR "ambiguous selector" +var _ = C.H // ERROR "ambiguous selector" +var _ = C.I // ERROR "no method I" diff --git a/test/fixedbugs/issue14636.go b/test/fixedbugs/issue14636.go index 7d1b60624..6b342e402 100644 --- a/test/fixedbugs/issue14636.go +++ b/test/fixedbugs/issue14636.go @@ -1,4 +1,4 @@ -// +build !nacl,!android,!darwin darwin,!arm +// +build !nacl,!js,!android,!darwin darwin,!arm // run // Copyright 2016 The Go Authors. All rights reserved. diff --git a/test/fixedbugs/issue15747.go b/test/fixedbugs/issue15747.go index decabc754..c7ef96d58 100644 --- a/test/fixedbugs/issue15747.go +++ b/test/fixedbugs/issue15747.go @@ -1,4 +1,4 @@ -// errorcheck -0 -live -d=eagerwb +// errorcheck -0 -live // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -7,10 +7,6 @@ // Issue 15747: liveness analysis was marking heap-escaped params live too much, // and worse was using the wrong bitmap bits to do so. -// TODO(austin): This expects function calls to the write barrier, so -// we enable the legacy eager write barrier. Fix this once the -// buffered write barrier works on all arches. - package p var global *[]byte @@ -21,14 +17,14 @@ type T struct{ M string } var b bool -func f1(q *Q, xx []byte) interface{} { // ERROR "live at call to newobject: xx$" "live at call to writebarrierptr: &xx$" "live at entry to f1: xx$" +func f1(q *Q, xx []byte) interface{} { // ERROR "live at call to newobject: xx$" "live at entry to f1: xx$" // xx was copied from the stack to the heap on the previous line: // xx was live for the first two prints but then it switched to &xx // being live. We should not see plain xx again. if b { - global = &xx // ERROR "live at call to writebarrierptr: &xx$" + global = &xx } - xx, _, err := f2(xx, 5) // ERROR "live at call to f2: &xx$" "live at call to writebarrierptr: err.data err.type$" + xx, _, err := f2(xx, 5) // ERROR "live at call to f2: &xx$" if err != nil { return err } @@ -38,7 +34,7 @@ func f1(q *Q, xx []byte) interface{} { // ERROR "live at call to newobject: xx$" //go:noinline func f2(d []byte, n int) (odata, res []byte, e interface{}) { // ERROR "live at entry to f2: d$" if n > len(d) { - return d, nil, &T{M: "hello"} // ERROR "live at call to newobject: d" "live at call to writebarrierptr: d" + return d, nil, &T{M: "hello"} // ERROR "live at call to newobject: d" } res = d[:n] odata = d[n:] diff --git a/test/fixedbugs/issue16037_run.go b/test/fixedbugs/issue16037_run.go index 23fff5925..d05e3f7f3 100644 --- a/test/fixedbugs/issue16037_run.go +++ b/test/fixedbugs/issue16037_run.go @@ -1,4 +1,4 @@ -// +build !nacl,!android +// +build !nacl,!js,!android // run // Copyright 2016 The Go Authors. All rights reserved. diff --git a/test/fixedbugs/issue18640.go b/test/fixedbugs/issue18640.go index c4f948b70..60abd31f7 100644 --- a/test/fixedbugs/issue18640.go +++ b/test/fixedbugs/issue18640.go @@ -11,12 +11,20 @@ type ( b struct { *a } +) +type ( c struct { *d } d = c +) +// The compiler reports an incorrect (non-alias related) +// type cycle here (via dowith()). Disabled for now. +// See issue #25838. +/* +type ( e = f f = g g = []h @@ -24,3 +32,16 @@ type ( i = j j = e ) +*/ + +type ( + a1 struct{ *b1 } + b1 = c1 + c1 struct{ *b1 } +) + +type ( + a2 struct{ b2 } + b2 = c2 + c2 struct{ *b2 } +) diff --git a/test/fixedbugs/issue18902.go b/test/fixedbugs/issue18902.go deleted file mode 100644 index 78c92187e..000000000 --- a/test/fixedbugs/issue18902.go +++ /dev/null @@ -1,141 +0,0 @@ -// run -// +build !nacl - -// Copyright 2016 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. - -// Runs a build -S to capture the assembly language -// output, checks that the line numbers associated with -// the stream of instructions do not change "too much". -// The changes that fixes this (that reduces the amount -// of change) does so by treating register spill, reload, -// copy, and rematerializations as being "unimportant" and -// just assigns them the line numbers of whatever "real" -// instructions preceded them. - -// nacl is excluded because this runs a compiler. - -package main - -import ( - "bufio" - "bytes" - "fmt" - "os" - "os/exec" - "strconv" - "strings" -) - -// updateEnv modifies env to ensure that key=val -func updateEnv(env *[]string, key, val string) { - if val != "" { - var found bool - key = key + "=" - for i, kv := range *env { - if strings.HasPrefix(kv, key) { - (*env)[i] = key + val - found = true - break - } - } - if !found { - *env = append(*env, key+val) - } - } -} - -func main() { - testarch := os.Getenv("TESTARCH") // Targets other platform in test compilation. - debug := os.Getenv("TESTDEBUG") != "" // Output the relevant assembly language. - - cmd := exec.Command("go", "tool", "compile", "-S", "fixedbugs/issue18902b.go") - var buf bytes.Buffer - cmd.Stdout = &buf - cmd.Stderr = &buf - cmd.Env = os.Environ() - - if testarch != "" { - updateEnv(&cmd.Env, "GOARCH", testarch) - updateEnv(&cmd.Env, "GOOS", "linux") // Simplify multi-arch testing - } - - err := cmd.Run() - if err != nil { - fmt.Printf("%s\n%s", err, buf.Bytes()) - return - } - begin := "\"\".(*gcSortBuf).flush" // Text at beginning of relevant dissassembly. - s := buf.String() - i := strings.Index(s, begin) - if i < 0 { - fmt.Printf("Failed to find expected symbol %s in output\n%s\n", begin, s) - return - } - s = s[i:] - r := strings.NewReader(s) - scanner := bufio.NewScanner(r) - first := true // The first line after the begin text will be skipped - beforeLineNumber := "issue18902b.go:" // Text preceding line number in each line. - lbln := len(beforeLineNumber) - - var scannedCount, changes, sumdiffs float64 - - prevVal := 0 - for scanner.Scan() { - line := scanner.Text() - if first { - first = false - continue - } - i = strings.Index(line, beforeLineNumber) - if i < 0 { - // Done reading lines - const minLines = 150 - if scannedCount <= minLines { // When test was written, 251 lines observed on amd64; arm64 now obtains 184 - fmt.Printf("Scanned only %d lines, was expecting more than %d\n", int(scannedCount), minLines) - return - } - // Note: when test was written, before changes=92, after=50 (was 62 w/o rematerialization NoXPos in *Value.copyInto()) - // and before sumdiffs=784, after=180 (was 446 w/o rematerialization NoXPos in *Value.copyInto()) - // Set the dividing line between pass and fail at the midpoint. - // Normalize against instruction count in case we unroll loops, etc. - if changes/scannedCount >= (50+92)/(2*scannedCount) || sumdiffs/scannedCount >= (180+784)/(2*scannedCount) { - fmt.Printf("Line numbers change too much, # of changes=%.f, sumdiffs=%.f, # of instructions=%.f\n", changes, sumdiffs, scannedCount) - } - return - } - scannedCount++ - i += lbln - lineVal, err := strconv.Atoi(line[i : i+3]) - if err != nil { - fmt.Printf("Expected 3-digit line number after %s in %s\n", beforeLineNumber, line) - } - if prevVal == 0 { - prevVal = lineVal - } - diff := lineVal - prevVal - if diff < 0 { - diff = -diff - } - if diff != 0 { - changes++ - sumdiffs += float64(diff) - } - // If things change too much, set environment variable TESTDEBUG to help figure out what's up. - // The "before" behavior can be recreated in DebugFriendlySetPosFrom (currently in gc/ssa.go) - // by inserting unconditional - // s.SetPos(v.Pos) - // at the top of the function. - - if debug { - fmt.Printf("%d %.f %.f %s\n", lineVal, changes, sumdiffs, line) - } - prevVal = lineVal - } - if err := scanner.Err(); err != nil { - fmt.Println("Reading standard input:", err) - return - } -} diff --git a/test/fixedbugs/issue18902b.go b/test/fixedbugs/issue18902b.go deleted file mode 100644 index 2e43e9f32..000000000 --- a/test/fixedbugs/issue18902b.go +++ /dev/null @@ -1,161 +0,0 @@ -// skip - -// Copyright 2016 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. - -package foo - -import ( - "unsafe" -) - -type gcMaxTreeNodeVal uint64 - -var work struct { - full uint64 // lock-free list of full blocks workbuf - empty uint64 // lock-free list of empty blocks workbuf - pad0 [64]uint8 // prevents false-sharing between full/empty and nproc/nwait - bytesMarked uint64 - markrootNext uint32 // next markroot job - markrootJobs uint32 // number of markroot jobs - nproc uint32 - tstart int64 - nwait uint32 - ndone uint32 -} - -type gcShardQueue1 struct { - partial *workbuf - full *workbuf - n uintptr - maxTree gcMaxTreeNodeVal -} -type gcShardQueue struct { - gcShardQueue1 - pad [64 - unsafe.Sizeof(gcShardQueue1{})]byte -} - -const gcSortBufPointers = (64 << 10) / 8 - -type gcSortBuf struct { - buf *gcSortArray - tmp *gcSortArray - n uintptr -} - -//go:notinheap -type gcSortArray [gcSortBufPointers]uintptr - -const ( - _DebugGC = 0 - _ConcurrentSweep = true - _FinBlockSize = 4 * 1024 - sweepMinHeapDistance = 1024 * 1024 - gcShardShift = 2 + 20 - gcShardBytes = 1 << gcShardShift -) - -//go:notinheap -type mheap struct { - shardQueues []gcShardQueue - _ uint32 // align uint64 fields on 32-bit for atomics - pagesInUse uint64 // pages of spans in stats _MSpanInUse; R/W with mheap.lock - spanBytesAlloc uint64 // bytes of spans allocated this cycle; updated atomically - pagesSwept uint64 // pages swept this cycle; updated atomically - sweepPagesPerByte float64 // proportional sweep ratio; written with lock, read without - largefree uint64 // bytes freed for large objects (>maxsmallsize) - nlargefree uint64 // number of frees for large objects (>maxsmallsize) - nsmallfree [67]uint64 // number of frees for small objects (<=maxsmallsize) - bitmap uintptr // Points to one byte past the end of the bitmap - bitmap_mapped uintptr - arena_start uintptr - arena_used uintptr // always mHeap_Map{Bits,Spans} before updating - arena_end uintptr - arena_reserved bool -} - -var mheap_ mheap - -type lfnode struct { - next uint64 - pushcnt uintptr -} -type workbufhdr struct { - node lfnode // must be first - next *workbuf - nobj int -} - -//go:notinheap -type workbuf struct { - workbufhdr - obj [(2048 - unsafe.Sizeof(workbufhdr{})) / 8]uintptr -} - -//go:noinline -func (b *workbuf) checkempty() { - if b.nobj != 0 { - b.nobj = 0 - } -} -func putempty(b *workbuf) { - b.checkempty() - lfstackpush(&work.empty, &b.node) -} - -//go:noinline -func lfstackpush(head *uint64, node *lfnode) { -} - -//go:noinline -func (q *gcShardQueue) add(qidx uintptr, ptrs []uintptr, spare *workbuf) *workbuf { - return spare -} - -func (b *gcSortBuf) flush() { - if b.n == 0 { - return - } - const sortDigitBits = 11 - buf, tmp := b.buf[:b.n], b.tmp[:b.n] - moreBits := true - for shift := uint(gcShardShift); moreBits; shift += sortDigitBits { - const k = 1 << sortDigitBits - var pos [k]uint16 - nshift := shift + sortDigitBits - nbits := buf[0] >> nshift - moreBits = false - for _, v := range buf { - pos[(v>>shift)%k]++ - moreBits = moreBits || v>>nshift != nbits - } - var sum uint16 - for i, count := range &pos { - pos[i] = sum - sum += count - } - for _, v := range buf { - digit := (v >> shift) % k - tmp[pos[digit]] = v - pos[digit]++ - } - buf, tmp = tmp, buf - } - start := mheap_.arena_start - i0 := 0 - shard0 := (buf[0] - start) / gcShardBytes - var spare *workbuf - for i, p := range buf { - shard := (p - start) / gcShardBytes - if shard != shard0 { - spare = mheap_.shardQueues[shard0].add(shard0, buf[i0:i], spare) - i0, shard0 = i, shard - } - } - spare = mheap_.shardQueues[shard0].add(shard0, buf[i0:], spare) - b.n = 0 - if spare != nil { - putempty(spare) - } -} diff --git a/test/fixedbugs/issue18911.dir/a.go b/test/fixedbugs/issue18911.dir/a.go new file mode 100644 index 000000000..d2221e761 --- /dev/null +++ b/test/fixedbugs/issue18911.dir/a.go @@ -0,0 +1,7 @@ +// Copyright 2018 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. + +package a + +var X interface{} = struct{ x int }{} diff --git a/test/fixedbugs/issue18911.dir/b.go b/test/fixedbugs/issue18911.dir/b.go new file mode 100644 index 000000000..da2388b88 --- /dev/null +++ b/test/fixedbugs/issue18911.dir/b.go @@ -0,0 +1,21 @@ +// Copyright 2018 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. + +package main + +import "./a" +import "strings" + +func main() { + defer func() { + p, ok := recover().(error) + if ok && strings.Contains(p.Error(), "different packages") { + return + } + panic(p) + }() + + // expected to fail and report two identical looking (but different) types + _ = a.X.(struct{ x int }) +} diff --git a/test/fixedbugs/issue18911.go b/test/fixedbugs/issue18911.go new file mode 100644 index 000000000..8bf34a382 --- /dev/null +++ b/test/fixedbugs/issue18911.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2018 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. + +package ignore diff --git a/test/fixedbugs/issue19182.go b/test/fixedbugs/issue19182.go index 3a90ff4b2..e1f3ffb47 100644 --- a/test/fixedbugs/issue19182.go +++ b/test/fixedbugs/issue19182.go @@ -1,4 +1,5 @@ // run +// +build !js // Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/fixedbugs/issue19359.go b/test/fixedbugs/issue19359.go index 4717d1365..cc3ecc84f 100644 --- a/test/fixedbugs/issue19359.go +++ b/test/fixedbugs/issue19359.go @@ -28,10 +28,64 @@ func del(m map[interface{}]interface{}, key interface{}) (err error) { return nil } +func addInt(m map[interface{}]int, key interface{}) (err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("addInt failed: %v", r) + } + }() + m[key] += 2018 + return nil +} + +func addStr(m map[interface{}]string, key interface{}) (err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("addStr failed: %v", r) + } + }() + m[key] += "hello, go" + return nil +} + +func appendInt(m map[interface{}][]int, key interface{}) (err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("appendInt failed: %v", r) + } + }() + m[key] = append(m[key], 2018) + return nil +} + +func appendStr(m map[interface{}][]string, key interface{}) (err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("addStr failed: %v", r) + } + }() + m[key] = append(m[key], "hello, go") + return nil +} + func main() { m := make(map[interface{}]interface{}) set(m, []int{1, 2, 3}) set(m, "abc") // used to throw del(m, []int{1, 2, 3}) del(m, "abc") // used to throw + + mi := make(map[interface{}]int) + addInt(mi, []int{1, 2, 3}) + addInt(mi, "abc") // used to throw + + ms := make(map[interface{}]string) + addStr(ms, []int{1, 2, 3}) + addStr(ms, "abc") // used to throw + + mia := make(map[interface{}][]int) + appendInt(mia, []int{1, 2, 3}) + + msa := make(map[interface{}][]string) + appendStr(msa, "abc") // used to throw } diff --git a/test/fixedbugs/issue19658.go b/test/fixedbugs/issue19658.go index 91cb88658..b2539629d 100644 --- a/test/fixedbugs/issue19658.go +++ b/test/fixedbugs/issue19658.go @@ -1,4 +1,4 @@ -// +build !nacl +// +build !nacl,!js // run // Copyright 2017 The Go Authors. All rights reserved. diff --git a/test/fixedbugs/issue20250.go b/test/fixedbugs/issue20250.go index 525192a46..6fc861a8d 100644 --- a/test/fixedbugs/issue20250.go +++ b/test/fixedbugs/issue20250.go @@ -1,4 +1,4 @@ -// errorcheck -0 -live -l -d=compilelater,eagerwb +// errorcheck -0 -live -l -d=compilelater // Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -8,10 +8,6 @@ // due to propagation of addrtaken to outer variables for // closure variables. -// TODO(austin): This expects function calls to the write barrier, so -// we enable the legacy eager write barrier. Fix this once the -// buffered write barrier works on all arches. - package p type T struct { @@ -21,7 +17,7 @@ type T struct { func f(a T) { // ERROR "live at entry to f: a" var e interface{} func() { // ERROR "live at entry to f.func1: a &e" - e = a.s // ERROR "live at call to convT2Estring: a &e" "live at call to writebarrierptr: a" + e = a.s // ERROR "live at call to convT2Estring: a &e" }() // ERROR "live at call to f.func1: e$" // Before the fix, both a and e were live at the previous line. _ = e diff --git a/test/fixedbugs/issue20529.go b/test/fixedbugs/issue20529.go index cd0c23da0..669064c2e 100644 --- a/test/fixedbugs/issue20529.go +++ b/test/fixedbugs/issue20529.go @@ -13,6 +13,9 @@ package p +import "runtime" + func f() { // ERROR "stack frame too large" - _ = [][]int{1e9: []int{}} + x := [][]int{1e9: []int{}} + runtime.KeepAlive(x) } diff --git a/test/fixedbugs/issue20780.go b/test/fixedbugs/issue20780.go new file mode 100644 index 000000000..a31e031b7 --- /dev/null +++ b/test/fixedbugs/issue20780.go @@ -0,0 +1,20 @@ +// errorcheck + +// Copyright 2018 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. + +// We have a limit of 1GB for stack frames. +// Make sure we include the callee args section. +// (The dispatch wrapper which implements (*S).f +// copies the return value from f to a stack temp, then +// from that stack temp to the return value of (*S).f. +// It uses ~800MB for each section.) + +package main + +type S struct { + i interface { + f() [800e6]byte + } +} diff --git a/test/fixedbugs/issue20923.go b/test/fixedbugs/issue20923.go new file mode 100644 index 000000000..5fd1ad8a4 --- /dev/null +++ b/test/fixedbugs/issue20923.go @@ -0,0 +1,19 @@ +// compile + +// Copyright 2018 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. + +// Issue 20923: gccgo failed to compile parenthesized select case expressions. + +package p + +func F(c chan bool) { + select { + case (<-c): + case _ = (<-c): + case _, _ = (<-c): + case (c) <- true: + default: + } +} diff --git a/test/fixedbugs/issue21221.go b/test/fixedbugs/issue21221.go new file mode 100644 index 000000000..bd5a4b550 --- /dev/null +++ b/test/fixedbugs/issue21221.go @@ -0,0 +1,18 @@ +// run + +// Copyright 2018 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. + +package main + +import "unsafe" + +func main() { + if unsafe.Pointer(uintptr(0)) != unsafe.Pointer(nil) { + panic("fail") + } + if (*int)(unsafe.Pointer(uintptr(0))) != (*int)(nil) { + panic("fail") + } +} diff --git a/test/fixedbugs/issue21317.go b/test/fixedbugs/issue21317.go index ae0e0b55f..530694af1 100644 --- a/test/fixedbugs/issue21317.go +++ b/test/fixedbugs/issue21317.go @@ -21,7 +21,7 @@ import ( ) func main() { - if runtime.Compiler != "gc" || runtime.GOOS == "nacl" { + if runtime.Compiler != "gc" || runtime.GOOS == "nacl" || runtime.GOOS == "js" { return } diff --git a/test/fixedbugs/issue21887.go b/test/fixedbugs/issue21887.go index 9e3e91fcd..73c3f4359 100644 --- a/test/fixedbugs/issue21887.go +++ b/test/fixedbugs/issue21887.go @@ -1,4 +1,4 @@ -// cmpout +// run // Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/fixedbugs/issue22305.go b/test/fixedbugs/issue22305.go new file mode 100644 index 000000000..ec432f9ae --- /dev/null +++ b/test/fixedbugs/issue22305.go @@ -0,0 +1,15 @@ +// compile + +// Copyright 2017 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. + +// Issue 22305: gccgo failed to compile this file. + +package main + +var F func() [0]func() +var i = 2 +var B = F()[i] + +func main() {} diff --git a/test/fixedbugs/issue22660.go b/test/fixedbugs/issue22660.go index 48686fefe..b2282ea66 100644 --- a/test/fixedbugs/issue22660.go +++ b/test/fixedbugs/issue22660.go @@ -19,7 +19,7 @@ import ( ) func main() { - if runtime.GOOS == "nacl" { + if runtime.GOOS == "nacl" || runtime.GOOS == "js" { return // no file system available on builders } diff --git a/test/fixedbugs/issue22662.go b/test/fixedbugs/issue22662.go new file mode 100644 index 000000000..a1f00bfac --- /dev/null +++ b/test/fixedbugs/issue22662.go @@ -0,0 +1,46 @@ +// run + +// Copyright 2018 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. + +// Verify effect of various line directives. +// TODO: check columns + +package main + +import ( + "fmt" + "runtime" +) + +func check(file string, line int) { + _, f, l, ok := runtime.Caller(1) + if !ok { + panic("runtime.Caller(1) failed") + } + if f != file || l != line { + panic(fmt.Sprintf("got %s:%d; want %s:%d", f, l, file, line)) + } +} + +func main() { +//-style line directives +//line :1 + check("??", 1) // no file specified +//line foo.go:1 + check("foo.go", 1) +//line bar.go:10:20 + check("bar.go", 10) +//line :11:22 + check("bar.go", 11) // no file, but column specified => keep old filename + +/*-style line directives */ +/*line :1*/ check("??", 1) // no file specified +/*line foo.go:1*/ check("foo.go", 1) +/*line bar.go:10:20*/ check("bar.go", 10) +/*line :11:22*/ check("bar.go", 11) // no file, but column specified => keep old filename + + /*line :10*/ check("??", 10); /*line foo.go:20*/ check("foo.go", 20); /*line :30:1*/ check("foo.go", 30) + check("foo.go", 31) +} diff --git a/test/fixedbugs/issue22662b.go b/test/fixedbugs/issue22662b.go new file mode 100644 index 000000000..3594c0f4e --- /dev/null +++ b/test/fixedbugs/issue22662b.go @@ -0,0 +1,65 @@ +// run + +// Copyright 2018 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. + +// Verify the impact of line directives on error positions and position formatting. + +package main + +import ( + "io/ioutil" + "log" + "os" + "os/exec" + "runtime" + "strings" +) + +// Each of these tests is expected to fail (missing package clause) +// at the position determined by the preceeding line directive. +var tests = []struct { + src, pos string +}{ + {"//line :10\n", ":10:"}, // no filename means no filename + {"//line :10:4\n", "filename:10:4"}, // no filename means use existing filename + {"//line foo.go:10\n", "foo.go:10:"}, // no column means don't print a column + {"//line foo.go:10:4\n", "foo.go:10:4:"}, // column means print a column + {"//line foo.go:10:4\n\n", "foo.go:11:1:"}, // relative columns start at 1 after newline + + {"/*line :10*/", ":10:"}, + {"/*line :10:4*/", "filename:10:4"}, + {"/*line foo.go:10*/", "foo.go:10:"}, + {"/*line foo.go:10:4*/", "foo.go:10:4:"}, + {"/*line foo.go:10:4*/\n", "foo.go:11:1:"}, +} + +func main() { + if runtime.GOOS == "nacl" || runtime.GOOS == "js" { + return // can not exec go tool + } + + f, err := ioutil.TempFile("", "issue22662b.go") + if err != nil { + log.Fatal(err) + } + f.Close() + defer os.Remove(f.Name()) + + for _, test := range tests { + if err := ioutil.WriteFile(f.Name(), []byte(test.src), 0660); err != nil { + log.Fatal(err) + } + + out, err := exec.Command("go", "tool", "compile", f.Name()).CombinedOutput() + if err == nil { + log.Fatalf("expected compiling\n---\n%s\n---\nto fail", test.src) + } + + errmsg := strings.Replace(string(out), f.Name(), "filename", -1) // use "filename" instead of actual (long) filename + if !strings.HasPrefix(errmsg, test.pos) { + log.Fatalf("%q: got %q; want position %q", test.src, errmsg, test.pos) + } + } +} diff --git a/test/fixedbugs/issue22683.go b/test/fixedbugs/issue22683.go index a59a0edaf..47c7f6513 100644 --- a/test/fixedbugs/issue22683.go +++ b/test/fixedbugs/issue22683.go @@ -1,4 +1,4 @@ -// cmpout +// run // Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/fixedbugs/issue22822.go b/test/fixedbugs/issue22822.go new file mode 100644 index 000000000..e449ddb18 --- /dev/null +++ b/test/fixedbugs/issue22822.go @@ -0,0 +1,16 @@ +// errorcheck + +// Copyright 2018 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. + +// Check that calling a function shadowing a built-in provides a good +// error message. + +package main + +func F() { + slice := []int{1, 2, 3} + len := int(2) + println(len(slice)) // ERROR "cannot call non-function len .type int., declared at" +} diff --git a/test/fixedbugs/issue22881.go b/test/fixedbugs/issue22881.go index 61e99a288..645f2d4b8 100644 --- a/test/fixedbugs/issue22881.go +++ b/test/fixedbugs/issue22881.go @@ -13,7 +13,7 @@ import "fmt" func main() { for i, f := range []func(map[int]int){ - f0, f1, f2, f3, f4, f5, f6, f7, + f0, f1, f2, f3, f4, f5, f6, f7, f8, } { m := map[int]int{} func() { // wrapper to scope the defer. @@ -27,6 +27,23 @@ func main() { fmt.Printf("map insert happened, case f%d\n", i) } } + + // Append slice. + for i, f := range []func(map[int][]int){ + fa0, fa1, fa2, fa3, + } { + m := map[int][]int{} + func() { // wrapper to scope the defer. + defer func() { + recover() + }() + f(m) // Will panic. Shouldn't modify m. + fmt.Printf("RHS didn't panic, case fa%d\n", i) + }() + if len(m) != 0 { + fmt.Printf("map insert happened, case fa%d\n", i) + } + } } func f0(m map[int]int) { @@ -69,4 +86,32 @@ func f7(m map[int]int) { m[0] = a[0] } +func f8(m map[int]int) { + var z int + m[0] %= z +} + +func fa0(m map[int][]int) { + var p *int + m[0] = append(m[0], *p) +} + +func fa1(m map[int][]int) { + var p *int + sink, m[0] = !sink, append(m[0], *p) +} + +func fa2(m map[int][]int) { + var p *int + m[0], _ = append(m[0], 0), *p +} + +func fa3(m map[int][]int) { + // OSLICE has similar in-place-reassignment + // optimizations as OAPPEND, but we need to make sure + // to *not* optimize them, because we can't guarantee + // the slice indices are within bounds. + m[0] = m[0][:1] +} + var sink bool diff --git a/test/fixedbugs/issue23094.go b/test/fixedbugs/issue23094.go new file mode 100644 index 000000000..415556f30 --- /dev/null +++ b/test/fixedbugs/issue23094.go @@ -0,0 +1,11 @@ +// errorcheck + +// Copyright 2018 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. + +// Verify that the array is reported in correct notation. + +package p + +var a [len(a)]int // ERROR "\[len\(a\)\]int" diff --git a/test/fixedbugs/issue23116.go b/test/fixedbugs/issue23116.go new file mode 100644 index 000000000..1737fee2c --- /dev/null +++ b/test/fixedbugs/issue23116.go @@ -0,0 +1,15 @@ +// errorcheck + +// Copyright 2018 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. + +package p + +func f(x interface{}) { + switch x.(type) { + } + + switch t := x.(type) { // ERROR "declared and not used" + } +} diff --git a/test/fixedbugs/issue23188.go b/test/fixedbugs/issue23188.go new file mode 100644 index 000000000..c22434069 --- /dev/null +++ b/test/fixedbugs/issue23188.go @@ -0,0 +1,32 @@ +// run + +// Copyright 2018 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 order of evaluation of index operations. + +package main + +func main() { + arr := []int{1, 2} + + // The spec says that in an assignment statement the operands + // of all index expressions and pointer indirections on the + // left, and the expressions on the right, are evaluated in + // the usual order. The usual order means function calls and + // channel operations are done first. Then the assignments are + // carried out one at a time. The operands of an index + // expression include both the array and the index. So this + // evaluates as + // tmp1 := arr + // tmp2 := len(arr) - 1 + // tmp3 := len(arr) + // arr = arr[:tmp3-1] + // tmp1[tmp2] = 3 + arr, arr[len(arr)-1] = arr[:len(arr)-1], 3 + + if len(arr) != 1 || arr[0] != 1 || arr[:2][1] != 3 { + panic(arr) + } +} diff --git a/test/fixedbugs/issue23298.go b/test/fixedbugs/issue23298.go new file mode 100644 index 000000000..be00a8ec6 --- /dev/null +++ b/test/fixedbugs/issue23298.go @@ -0,0 +1,14 @@ +// compile + +// Copyright 2018 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. + +package p + +type T string + +var ( + t = T("T") + r = []rune(t) +) diff --git a/test/fixedbugs/issue23414.go b/test/fixedbugs/issue23414.go new file mode 100644 index 000000000..7ef3d831f --- /dev/null +++ b/test/fixedbugs/issue23414.go @@ -0,0 +1,13 @@ +// compile + +// Copyright 2018 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. + +package p + +var x struct{} + +func f() bool { + return x == x && x == x +} diff --git a/test/fixedbugs/issue23489.go b/test/fixedbugs/issue23489.go new file mode 100644 index 000000000..1e64af190 --- /dev/null +++ b/test/fixedbugs/issue23489.go @@ -0,0 +1,20 @@ +// run + +// Copyright 2017 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. + +// Caused gccgo to issue a spurious compilation error. + +package main + +type T struct{} + +func (*T) Foo() {} + +type P = *T + +func main() { + var p P + p.Foo() +} diff --git a/test/fixedbugs/issue23504.go b/test/fixedbugs/issue23504.go new file mode 100644 index 000000000..77f318414 --- /dev/null +++ b/test/fixedbugs/issue23504.go @@ -0,0 +1,15 @@ +// compile + +// Copyright 2018 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. + +package p + +func f() { + var B bool + B2 := (B || B && !B) && !B + B3 := B2 || B + for (B3 || B2) && !B2 && B { + } +} diff --git a/test/fixedbugs/issue23521.go b/test/fixedbugs/issue23521.go new file mode 100644 index 000000000..159e03238 --- /dev/null +++ b/test/fixedbugs/issue23521.go @@ -0,0 +1,43 @@ +// errorcheck -0 -m + +// Copyright 2018 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. + +// Issue 23521: improve early DCE for if without explicit else. + +package p + +//go:noinline +func nonleaf() {} + +const truth = true + +func f() int { // ERROR "can inline f" + if truth { + return 0 + } + // If everything below is removed, as it should, + // function f should be inlineable. + nonleaf() + for { + panic("") + } +} + +func g() int { // ERROR "can inline g" + return f() // ERROR "inlining call to f" +} + +func f2() int { // ERROR "can inline f2" + if !truth { + nonleaf() + } else { + return 0 + } + panic("") +} + +func g2() int { // ERROR "can inline g2" + return f2() // ERROR "inlining call to f2" +} diff --git a/test/fixedbugs/issue23545.go b/test/fixedbugs/issue23545.go index 24485c11c..d97f64af5 100644 --- a/test/fixedbugs/issue23545.go +++ b/test/fixedbugs/issue23545.go @@ -4,12 +4,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build gccgo - // Issue 23545: gccgo didn't lower array comparison to // proper equality function in some case. -// TODO: build only on gccgo for now, as it hits issue -// #23546. package main diff --git a/test/fixedbugs/issue23546.go b/test/fixedbugs/issue23546.go new file mode 100644 index 000000000..818f0cd6b --- /dev/null +++ b/test/fixedbugs/issue23546.go @@ -0,0 +1,22 @@ +// run + +// Copyright 2018 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. + +// Issue 23546: type..eq function not generated when +// DWARF is disabled. + +package main + +func main() { + use(f() == f()) +} + +func f() [2]interface{} { + var out [2]interface{} + return out +} + +//go:noinline +func use(bool) {} diff --git a/test/fixedbugs/issue23586.go b/test/fixedbugs/issue23586.go new file mode 100644 index 000000000..c2d4c9ffb --- /dev/null +++ b/test/fixedbugs/issue23586.go @@ -0,0 +1,24 @@ +// errorcheck + +// Copyright 2018 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 we type-check deferred/go functions even +// if they are not called (a common error). Specifically, +// we don't want to see errors such as import or variable +// declared but not used. + +package p + +import ( + "fmt" + "math" +) + +func f() { + var i int + defer func() { fmt.Println() } // ERROR "must be function call" + go func() { _ = math.Sin(0) } // ERROR "must be function call" + go func() { _ = i} // ERROR "must be function call" +} diff --git a/test/fixedbugs/issue23587.go b/test/fixedbugs/issue23587.go new file mode 100644 index 000000000..bd5df2775 --- /dev/null +++ b/test/fixedbugs/issue23587.go @@ -0,0 +1,12 @@ +// errorcheck + +// Copyright 2018 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. + +package p + +func f(x int) { + _ = ~x // ERROR "invalid character" + _ = x ~ x // ERROR "invalid character" "unexpected x at end of statement" +} diff --git a/test/fixedbugs/issue23609.go b/test/fixedbugs/issue23609.go new file mode 100644 index 000000000..7c17a98d3 --- /dev/null +++ b/test/fixedbugs/issue23609.go @@ -0,0 +1,27 @@ +// errorcheck + +// Copyright 2018 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. + +package main + +type t1 struct { + t1f1 int + t1f2 int +} +type t2 struct { + t2f1 int + t2f2 int + t1 +} +type t3 struct { + t3f1 int + t2 +} + +var ( + _ = t2{t1f1: 600} // ERROR "cannot use promoted field t1.t1f1 in struct literal of type t2" + _ = t3{t1f2: 800} // ERROR "cannot use promoted field t2.t1.t1f2 in struct literal of type t3" + _ = t3{t2f1: 900} // ERROR "cannot use promoted field t2.t2f1 in struct literal of type t3" +) diff --git a/test/fixedbugs/issue23664.go b/test/fixedbugs/issue23664.go new file mode 100644 index 000000000..1925ebffe --- /dev/null +++ b/test/fixedbugs/issue23664.go @@ -0,0 +1,17 @@ +// errorcheck + +// Copyright 2018 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. + +// Verify error messages for incorrect if/switch headers. + +package p + +func f() { + if f() true { // ERROR "unexpected true, expecting {" + } + + switch f() true { // ERROR "unexpected true, expecting {" + } +} diff --git a/test/fixedbugs/issue23732.go b/test/fixedbugs/issue23732.go new file mode 100644 index 000000000..be17bf4f6 --- /dev/null +++ b/test/fixedbugs/issue23732.go @@ -0,0 +1,42 @@ +// errorcheck + +// Copyright 2018 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. + +// Issue 23732: Give better details about which struct +// initializer has the wrong number of values. + +package main + +type Foo struct { + A int + B int + C interface{} + Bar +} + +type Bar struct { + A string +} + +func main() { + _ = Foo{ + 1, + 2, + 3, // ERROR "too few values in Foo literal" + } + + _ = Foo{ + 1, + 2, + 3, + Bar{"A", "B"}, // ERROR "too many values in Bar literal" + } + + _ = Foo{ + 1, + 2, + Bar{"A", "B"}, // ERROR "too many values in Bar literal" "too few values in Foo literal" + } +} diff --git a/test/fixedbugs/issue23823.go b/test/fixedbugs/issue23823.go new file mode 100644 index 000000000..2f802d098 --- /dev/null +++ b/test/fixedbugs/issue23823.go @@ -0,0 +1,15 @@ +// errorcheck + +// Copyright 2018 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. + +package p + +type I1 = interface { + I2 +} + +type I2 interface { // ERROR "invalid recursive type" + I1 +} diff --git a/test/fixedbugs/issue23912.go b/test/fixedbugs/issue23912.go new file mode 100644 index 000000000..05ffd6be2 --- /dev/null +++ b/test/fixedbugs/issue23912.go @@ -0,0 +1,30 @@ +// compile + +// Copyright 2018 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. + +// A couple of aliases cases that gccgo incorrectly gave errors for. + +package p + +func F1() { + type E = struct{} + type X struct{} + var x X + var y E = x + _ = y +} + +func F2() { + type E = struct{} + type S []E + type T []struct{} + type X struct{} + var x X + s := S{E{}} + t := T{struct{}{}} + _ = append(s, x) + _ = append(s, t[0]) + _ = append(s, t...) +} diff --git a/test/fixedbugs/issue24120.go b/test/fixedbugs/issue24120.go new file mode 100644 index 000000000..6c7d871b7 --- /dev/null +++ b/test/fixedbugs/issue24120.go @@ -0,0 +1,14 @@ +// compile + +// Copyright 2018 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. + +package p + +var F func(int) + +func G() { + if F(func() int { return 1 }()); false { + } +} diff --git a/test/fixedbugs/issue24159.go b/test/fixedbugs/issue24159.go new file mode 100644 index 000000000..9397bdc84 --- /dev/null +++ b/test/fixedbugs/issue24159.go @@ -0,0 +1,20 @@ +// errorcheck + +// Copyright 2018 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. + +package p + +type intAlias = int + +func f() { + switch interface{}(nil) { + case uint8(0): + case byte(0): // ERROR "duplicate case" + case int32(0): + case rune(0): // ERROR "duplicate case" + case int(0): + case intAlias(0): // ERROR "duplicate case" + } +} diff --git a/test/fixedbugs/issue24173.go b/test/fixedbugs/issue24173.go new file mode 100644 index 000000000..4c19e05ef --- /dev/null +++ b/test/fixedbugs/issue24173.go @@ -0,0 +1,19 @@ +// compile + +// Copyright 2018 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. + +package p + +type arrayAlias = [10]int +type mapAlias = map[int]int +type sliceAlias = []int +type structAlias = struct{} + +func Exported() { + _ = arrayAlias{} + _ = mapAlias{} + _ = sliceAlias{} + _ = structAlias{} +} diff --git a/test/fixedbugs/issue24187.go b/test/fixedbugs/issue24187.go new file mode 100644 index 000000000..45fc92971 --- /dev/null +++ b/test/fixedbugs/issue24187.go @@ -0,0 +1,33 @@ +// +build amd64p32 +// run + +// Copyright 2018 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. + +package main + +import ( + "bytes" + "fmt" + "unsafe" +) + +func main() { + b := make([]byte, 128) + for i := range b { + b[i] = 1 + } + if bytes.IndexByte(b, 0) != -1 { + panic("found 0") + } + for i := range b { + b[i] = 0 + c := b + *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&c)) + unsafe.Sizeof(uintptr(0)))) = 1<<31 - 1 + if bytes.IndexByte(c, 0) != i { + panic(fmt.Sprintf("missing 0 at %d\n", i)) + } + b[i] = 1 + } +} diff --git a/test/fixedbugs/issue24339.go b/test/fixedbugs/issue24339.go new file mode 100644 index 000000000..0670becdf --- /dev/null +++ b/test/fixedbugs/issue24339.go @@ -0,0 +1,20 @@ +// errorcheck + +// Copyright 2018 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. + +package p + +// Use a diffent line number for each token so we can +// check that the error message appears at the correct +// position. +var _ = struct{}{ /*line :20:1*/foo /*line :21:1*/: /*line :22:1*/0 } + + + + + + + +// ERROR "unknown field 'foo'"
\ No newline at end of file diff --git a/test/fixedbugs/issue24419.go b/test/fixedbugs/issue24419.go new file mode 100644 index 000000000..73b7783e0 --- /dev/null +++ b/test/fixedbugs/issue24419.go @@ -0,0 +1,51 @@ +// run + +// Copyright 2018 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. + +package main + +import ( + "bytes" + "strings" +) + +func growstack(n int) { + if n > 0 { + growstack(n - 1) + } +} + +func main() { + c := make(chan struct{}) + go compare(c) + go equal(c) + go indexByte(c) + go indexByteString(c) + <-c + <-c + <-c + <-c +} + +func compare(c chan struct{}) { + defer bytes.Compare(nil, nil) + growstack(10000) + c <- struct{}{} +} +func equal(c chan struct{}) { + defer bytes.Equal(nil, nil) + growstack(10000) + c <- struct{}{} +} +func indexByte(c chan struct{}) { + defer bytes.IndexByte(nil, 0) + growstack(10000) + c <- struct{}{} +} +func indexByteString(c chan struct{}) { + defer strings.IndexByte("", 0) + growstack(10000) + c <- struct{}{} +} diff --git a/test/fixedbugs/issue24449.go b/test/fixedbugs/issue24449.go new file mode 100644 index 000000000..b23664510 --- /dev/null +++ b/test/fixedbugs/issue24449.go @@ -0,0 +1,62 @@ +// run + +// Copyright 2018 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. + +package main + +import ( + "sync/atomic" +) + +var cnt32 int32 + +//go:noinline +func test32(a, b []int) bool { + // Try to generate flag value, issue atomic + // adds and then re-use the flag value to see if + // the atomic add has clobbered them. + atomic.AddInt32(&cnt32, 1) + if len(a) == len(b) { + atomic.AddInt32(&cnt32, 2) + } + atomic.AddInt32(&cnt32, 4) + if len(a) >= len(b) { + atomic.AddInt32(&cnt32, 8) + } + if len(a) <= len(b) { + atomic.AddInt32(&cnt32, 16) + } + return atomic.LoadInt32(&cnt32) == 31 +} + +var cnt64 int64 + +//go:noinline +func test64(a, b []int) bool { + // Try to generate flag value, issue atomic + // adds and then re-use the flag value to see if + // the atomic add has clobbered them. + atomic.AddInt64(&cnt64, 1) + if len(a) == len(b) { + atomic.AddInt64(&cnt64, 2) + } + atomic.AddInt64(&cnt64, 4) + if len(a) >= len(b) { + atomic.AddInt64(&cnt64, 8) + } + if len(a) <= len(b) { + atomic.AddInt64(&cnt64, 16) + } + return atomic.LoadInt64(&cnt64) == 31 +} + +func main() { + if !test32([]int{}, []int{}) { + panic("test32") + } + if !test64([]int{}, []int{}) { + panic("test64") + } +} diff --git a/test/fixedbugs/issue24470.go b/test/fixedbugs/issue24470.go new file mode 100644 index 000000000..d0e5e23fa --- /dev/null +++ b/test/fixedbugs/issue24470.go @@ -0,0 +1,15 @@ +// errorcheck + +// Copyright 2018 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. + +// Verify that we get "use of .(type) outside type switch" +// before any other (misleading) errors. Test case from issue. + +package p + +func f(i interface{}) { + if x, ok := i.(type); ok { // ERROR "outside type switch" + } +} diff --git a/test/fixedbugs/issue24503.go b/test/fixedbugs/issue24503.go new file mode 100644 index 000000000..933ce70db --- /dev/null +++ b/test/fixedbugs/issue24503.go @@ -0,0 +1,28 @@ +// run + +// Copyright 2018 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. + +// Issue 24503: Handle == and != of addresses taken of symbols consistently. + +package main + +func test() string { + type test struct{} + o1 := test{} + o2 := test{} + if &o1 == &o2 { + return "equal" + } + if &o1 != &o2 { + return "unequal" + } + return "failed" +} + +func main() { + if test() == "failed" { + panic("expected either 'equal' or 'unequal'") + } +} diff --git a/test/fixedbugs/issue24547.go b/test/fixedbugs/issue24547.go new file mode 100644 index 000000000..47d94a9f9 --- /dev/null +++ b/test/fixedbugs/issue24547.go @@ -0,0 +1,46 @@ +// run + +// Copyright 2018 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. + +// When computing method sets with shadowed methods, make sure we +// compute whether a method promotion involved a pointer traversal +// based on the promoted method, not the shadowed method. + +package main + +import ( + "bytes" + "fmt" +) + +type mystruct struct { + f int +} + +func (t mystruct) String() string { + return "FAIL" +} + +func main() { + type deep struct { + mystruct + } + s := struct { + deep + *bytes.Buffer + }{ + deep{}, + bytes.NewBufferString("ok"), + } + + if got := s.String(); got != "ok" { + panic(got) + } + + var i fmt.Stringer = s + if got := i.String(); got != "ok" { + panic(got) + } +} diff --git a/test/fixedbugs/issue24651a.go b/test/fixedbugs/issue24651a.go new file mode 100644 index 000000000..5f63635a2 --- /dev/null +++ b/test/fixedbugs/issue24651a.go @@ -0,0 +1,24 @@ +//errorcheck -0 -race -m -m + +// Copyright 2018 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. + +package main + +//go:norace +func Foo(x int) int { // ERROR "cannot inline Foo: marked go:norace with -race compilation$" + return x * (x + 1) * (x + 2) +} + +func Bar(x int) int { // ERROR "can inline Bar as: func\(int\) int { return x \* \(x \+ 1\) \* \(x \+ 2\) }$" + return x * (x + 1) * (x + 2) +} + +var x = 5 + +//go:noinline Provide a clean, constant reason for not inlining main +func main() { // ERROR "cannot inline main: marked go:noinline$" + println("Foo(", x, ")=", Foo(x)) + println("Bar(", x, ")=", Bar(x)) // ERROR "inlining call to Bar func\(int\) int { return x \* \(x \+ 1\) \* \(x \+ 2\) }$" +} diff --git a/test/fixedbugs/issue24651b.go b/test/fixedbugs/issue24651b.go new file mode 100644 index 000000000..2420f61fa --- /dev/null +++ b/test/fixedbugs/issue24651b.go @@ -0,0 +1,24 @@ +//errorcheck -0 -m -m + +// Copyright 2018 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. + +package main + +//go:norace +func Foo(x int) int { // ERROR "can inline Foo as: func\(int\) int { return x \* \(x \+ 1\) \* \(x \+ 2\) }$" + return x * (x + 1) * (x + 2) +} + +func Bar(x int) int { // ERROR "can inline Bar as: func\(int\) int { return x \* \(x \+ 1\) \* \(x \+ 2\) }$" + return x * (x + 1) * (x + 2) +} + +var x = 5 + +//go:noinline Provide a clean, constant reason for not inlining main +func main() { // ERROR "cannot inline main: marked go:noinline$" + println("Foo(", x, ")=", Foo(x)) // ERROR "inlining call to Foo func\(int\) int { return x \* \(x \+ 1\) \* \(x \+ 2\) }$" + println("Bar(", x, ")=", Bar(x)) // ERROR "inlining call to Bar func\(int\) int { return x \* \(x \+ 1\) \* \(x \+ 2\) }$" +} diff --git a/test/fixedbugs/issue24693.dir/a.go b/test/fixedbugs/issue24693.dir/a.go new file mode 100644 index 000000000..8a845ed86 --- /dev/null +++ b/test/fixedbugs/issue24693.dir/a.go @@ -0,0 +1,11 @@ +// Copyright 2018 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. + +package a + +type T struct{} + +func (T) m() { println("FAIL") } + +type I interface{ m() } diff --git a/test/fixedbugs/issue24693.dir/b.go b/test/fixedbugs/issue24693.dir/b.go new file mode 100644 index 000000000..15ffa4f7c --- /dev/null +++ b/test/fixedbugs/issue24693.dir/b.go @@ -0,0 +1,38 @@ +// Copyright 2018 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. + +package b + +import "./a" + +type T struct{ a.T } + +func (T) m() { println("ok") } + +// The compiler used to not pay attention to package for non-exported +// methods when statically constructing itabs. The consequence of this +// was that the call to b.F1(b.T{}) in c.go would create an itab using +// a.T.m instead of b.T.m. +func F1(i interface{ m() }) { i.m() } + +// The interface method calling convention depends on interface method +// sets being sorted in the same order across compilation units. In +// the test case below, at the call to b.F2(b.T{}) in c.go, the +// interface method set is sorted as { a.m(); b.m() }. +// +// However, while compiling package b, its package path is set to "", +// so the code produced for F2 uses { b.m(); a.m() } as the method set +// order. So again, it ends up calling the wrong method. +// +// Also, this function is marked noinline because it's critical to the +// test that the interface method call happen in this compilation +// unit, and the itab construction happens in c.go. +// +//go:noinline +func F2(i interface { + m() + a.I // embeds m() from package a +}) { + i.m() +} diff --git a/test/fixedbugs/issue24693.dir/c.go b/test/fixedbugs/issue24693.dir/c.go new file mode 100644 index 000000000..8c6e27b14 --- /dev/null +++ b/test/fixedbugs/issue24693.dir/c.go @@ -0,0 +1,12 @@ +// Copyright 2018 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. + +package main + +import "./b" + +func main() { + b.F1(b.T{}) + b.F2(b.T{}) +} diff --git a/test/fixedbugs/issue24693.go b/test/fixedbugs/issue24693.go new file mode 100644 index 000000000..3da6a81af --- /dev/null +++ b/test/fixedbugs/issue24693.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2018 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. + +package ignored diff --git a/test/fixedbugs/issue24693.out b/test/fixedbugs/issue24693.out new file mode 100644 index 000000000..79ebd0860 --- /dev/null +++ b/test/fixedbugs/issue24693.out @@ -0,0 +1,2 @@ +ok +ok diff --git a/test/fixedbugs/issue24761.dir/a.go b/test/fixedbugs/issue24761.dir/a.go new file mode 100644 index 000000000..1aa2317bd --- /dev/null +++ b/test/fixedbugs/issue24761.dir/a.go @@ -0,0 +1,15 @@ +// Copyright 2018 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. + +package a + +type T2 struct{} + +func (t *T2) M2(a, b float64) { + variadic(a, b) +} + +func variadic(points ...float64) { + println(points) +} diff --git a/test/fixedbugs/issue24761.dir/b.go b/test/fixedbugs/issue24761.dir/b.go new file mode 100644 index 000000000..dd3d543a0 --- /dev/null +++ b/test/fixedbugs/issue24761.dir/b.go @@ -0,0 +1,11 @@ +// Copyright 2018 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. + +package b + +import "./a" + +type T1 struct { + *a.T2 +} diff --git a/test/fixedbugs/issue24761.go b/test/fixedbugs/issue24761.go new file mode 100644 index 000000000..4b97663c3 --- /dev/null +++ b/test/fixedbugs/issue24761.go @@ -0,0 +1,7 @@ +// compiledir -c=4 + +// Copyright 2018 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. + +package ignored diff --git a/test/fixedbugs/issue24763.go b/test/fixedbugs/issue24763.go new file mode 100644 index 000000000..623ab489e --- /dev/null +++ b/test/fixedbugs/issue24763.go @@ -0,0 +1,21 @@ +// run + +// Copyright 2018 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. + +package main + +func main() { + var s uint + var x = interface{}(1<<s + 1<<s) // compiler must not crash here + if x.(int) != 2 { + panic("x not int or not 2") + } + + var y interface{} + y = 1<<s + 1 // compiler must not crash here + if y.(int) != 2 { + panic("y not int or not 2") + } +} diff --git a/test/fixedbugs/issue24799.go b/test/fixedbugs/issue24799.go new file mode 100644 index 000000000..c805c86dd --- /dev/null +++ b/test/fixedbugs/issue24799.go @@ -0,0 +1,58 @@ +// run + +// Copyright 2018 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. + +// Loads of 8 byte go.strings cannot use DS relocation +// in case the alignment is not a multiple of 4. + +package main + +import ( + "fmt" +) + +type Level string + +// The following are all go.strings. A link time error can +// occur if an 8 byte load is used to load a go.string that is +// not aligned to 4 bytes due to the type of relocation that +// is generated for the instruction. A fix was made to avoid +// generating an instruction with DS relocation for go.strings +// since their alignment is not known until link time. + +// This problem only affects go.string since other types have +// correct alignment. + +const ( + LevelBad Level = "badvals" + LevelNone Level = "No" + LevelMetadata Level = "Metadata" + LevelRequest Level = "Request" + LevelRequestResponse Level = "RequestResponse" +) + +func ordLevel(l Level) int { + switch l { + case LevelMetadata: + return 1 + case LevelRequest: + return 2 + case LevelRequestResponse: + return 3 + default: + return 0 + } +} + +//go:noinline +func test(l Level) { + if ordLevel(l) < ordLevel(LevelMetadata) { + fmt.Printf("OK\n") + } +} + +func main() { + test(LevelMetadata) +} diff --git a/test/fixedbugs/issue24801.dir/a.go b/test/fixedbugs/issue24801.dir/a.go new file mode 100644 index 000000000..58e6240d8 --- /dev/null +++ b/test/fixedbugs/issue24801.dir/a.go @@ -0,0 +1,9 @@ +// Copyright 2018 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. + +package a + +type main int + +var X main diff --git a/test/fixedbugs/issue24801.dir/main.go b/test/fixedbugs/issue24801.dir/main.go new file mode 100644 index 000000000..5c7db7b4d --- /dev/null +++ b/test/fixedbugs/issue24801.dir/main.go @@ -0,0 +1,11 @@ +// Copyright 2018 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. + +package main + +import "./a" + +func main() { + a.X = 1 +} diff --git a/test/fixedbugs/issue24801.go b/test/fixedbugs/issue24801.go new file mode 100644 index 000000000..9b05db8ea --- /dev/null +++ b/test/fixedbugs/issue24801.go @@ -0,0 +1,5 @@ +// compiledir + +// Copyright 2018 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. diff --git a/test/fixedbugs/issue24817.go b/test/fixedbugs/issue24817.go new file mode 100644 index 000000000..ba2a138ed --- /dev/null +++ b/test/fixedbugs/issue24817.go @@ -0,0 +1,64 @@ +// run + +// Copyright 2018 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. + +// Check all ways to compare a non-constant string to the empty string. + +package main + +import ( + "fmt" + "os" +) + +var ( + s = "abc" + e = "" + failed bool +) + +func main() { + want(true, "" < s, `"" < s`) + want(false, s < "", `s < ""`) + want(false, "" < e, `"" < e`) + want(false, e < "", `e < ""`) + + want(true, "" <= s, `"" <= s`) + want(false, s <= "", `s <= ""`) + want(true, "" <= e, `"" <= e`) + want(true, e <= "", `e <= ""`) + + want(false, "" > s, `"" > s`) + want(true, s > "", `s > ""`) + want(false, "" > e, `"" > e`) + want(false, e > "", `e > ""`) + + want(false, "" >= s, `"" >= s`) + want(true, s >= "", `s >= ""`) + want(true, "" >= e, `"" >= e`) + want(true, e >= "", `e >= ""`) + + want(false, "" == s, `"" == s`) + want(false, s == "", `s == ""`) + want(true, "" == e, `"" == e`) + want(true, e == "", `e == ""`) + + want(true, "" != s, `"" != s`) + want(true, s != "", `s != ""`) + want(false, "" != e, `"" != e`) + want(false, e != "", `e != ""`) + + if failed { + os.Exit(1) + } +} + +//go:noinline +func want(b bool, have bool, msg string) { + if b != have { + fmt.Println(msg) + failed = true + } +} diff --git a/test/fixedbugs/issue24937.go b/test/fixedbugs/issue24937.go new file mode 100644 index 000000000..7d8460f61 --- /dev/null +++ b/test/fixedbugs/issue24937.go @@ -0,0 +1,15 @@ +// run + +// Copyright 2018 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. + +package main + +func main() { + x := []byte{'a'} + switch string(x) { + case func() string { x[0] = 'b'; return "b" }(): + panic("FAIL") + } +} diff --git a/test/fixedbugs/issue24939.go b/test/fixedbugs/issue24939.go new file mode 100644 index 000000000..26530e95b --- /dev/null +++ b/test/fixedbugs/issue24939.go @@ -0,0 +1,21 @@ +// compile + +// Copyright 2018 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. + +package main + +type T interface { + M(P) +} + +type M interface { + F() P +} + +type P = interface { + I() M +} + +func main() {} diff --git a/test/fixedbugs/issue25006.go b/test/fixedbugs/issue25006.go new file mode 100644 index 000000000..570fdca5c --- /dev/null +++ b/test/fixedbugs/issue25006.go @@ -0,0 +1,30 @@ +// compile + +// Copyright 2018 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. + +package p + +func spin() { + var i int + var b bool + + switch 1 { + case 0: + i = 1 + } + switch 1 { + case i: + default: + i = 1 + b = !b && (b && !b) && b + } + switch false { + case false: + i = 3 + -i + switch 0 { + case 1 - i: + } + } +} diff --git a/test/fixedbugs/issue25055.dir/a.go b/test/fixedbugs/issue25055.dir/a.go new file mode 100644 index 000000000..7fea195e2 --- /dev/null +++ b/test/fixedbugs/issue25055.dir/a.go @@ -0,0 +1,7 @@ +// Copyright 2018 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. + +package a + +var A chan *interface{} diff --git a/test/fixedbugs/issue25055.dir/b.go b/test/fixedbugs/issue25055.dir/b.go new file mode 100644 index 000000000..01efeae3e --- /dev/null +++ b/test/fixedbugs/issue25055.dir/b.go @@ -0,0 +1,9 @@ +// Copyright 2018 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. + +package b + +import "./a" + +var _ = <-a.A diff --git a/test/fixedbugs/issue25055.go b/test/fixedbugs/issue25055.go new file mode 100644 index 000000000..0e15a8e71 --- /dev/null +++ b/test/fixedbugs/issue25055.go @@ -0,0 +1,7 @@ +// compiledir -c=2 + +// Copyright 2018 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. + +package ignored diff --git a/test/fixedbugs/issue25101.go b/test/fixedbugs/issue25101.go new file mode 100644 index 000000000..4fd6bed92 --- /dev/null +++ b/test/fixedbugs/issue25101.go @@ -0,0 +1,16 @@ +// compile + +// Copyright 2018 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. + +// Indexed export format must not crash when writing +// the anonymous parameter for m. + +package p + +var x interface { + m(int) +} + +var M = x.m diff --git a/test/fixedbugs/issue25322.go b/test/fixedbugs/issue25322.go new file mode 100644 index 000000000..ee4ff53e2 --- /dev/null +++ b/test/fixedbugs/issue25322.go @@ -0,0 +1,23 @@ +// run + +// Copyright 2018 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. + +// Missing zero extension when converting a float32 +// to a uint64. + +package main + +import ( + "fmt" + "math" +) + +func Foo(v float32) { + fmt.Printf("%x\n", uint64(math.Float32bits(v))) +} + +func main() { + Foo(2.0) +} diff --git a/test/fixedbugs/issue25322.out b/test/fixedbugs/issue25322.out new file mode 100644 index 000000000..52f3f6a74 --- /dev/null +++ b/test/fixedbugs/issue25322.out @@ -0,0 +1 @@ +40000000 diff --git a/test/fixedbugs/issue25507.go b/test/fixedbugs/issue25507.go new file mode 100644 index 000000000..8dcbae16a --- /dev/null +++ b/test/fixedbugs/issue25507.go @@ -0,0 +1,29 @@ +// errorcheck + +// Copyright 2018 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. + +// We have a limit of 1GB for stack frames. +// Test that we extend that limit to include large argument/return areas. +// Argument/return areas are part of the parent frame, not the frame itself, +// so they need to be handled separately. + +package main + +// >1GB to trigger failure, <2GB to work on 32-bit platforms. +type large struct { + b [1500000000]byte +} + +func (x large) f1() int { // ERROR "stack frame too large" + return 5 +} + +func f2(x large) int { // ERROR "stack frame too large" + return 5 +} + +func f3() (x large, i int) { // ERROR "stack frame too large" + return +} diff --git a/test/fixedbugs/issue25516.go b/test/fixedbugs/issue25516.go new file mode 100644 index 000000000..8326ef953 --- /dev/null +++ b/test/fixedbugs/issue25516.go @@ -0,0 +1,26 @@ +// compile + +// Copyright 2018 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. + +// Make sure dead write barriers are handled correctly. + +package main + +func f(p **int) { + // The trick here is to eliminate the block containing the write barrier, + // but only after the write barrier branches are inserted. + // This requires some delicate code. + i := 0 + var b []bool + var s string + for true { + if b[i] { + var a []string + s = a[len(s)] + i = 0 + } + *p = nil + } +} diff --git a/test/fixedbugs/issue25727.go b/test/fixedbugs/issue25727.go new file mode 100644 index 000000000..9b7c804a0 --- /dev/null +++ b/test/fixedbugs/issue25727.go @@ -0,0 +1,21 @@ +// errorcheck + +// Copyright 2018 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. + +package main + +import "net/http" + +var s = http.Server{} +var _ = s.doneChan // ERROR "s.doneChan undefined .cannot refer to unexported field or method doneChan.$" +var _ = s.DoneChan // ERROR "s.DoneChan undefined .type http.Server has no field or method DoneChan.$" +var _ = http.Server{tlsConfig: nil} // ERROR "unknown field 'tlsConfig' in struct literal.+ .but does have TLSConfig.$" +var _ = http.Server{DoneChan: nil} // ERROR "unknown field 'DoneChan' in struct literal of type http.Server$" + +type foo struct { + bar int +} + +var _ = &foo{bAr: 10} // ERROR "unknown field 'bAr' in struct literal.+ .but does have bar.$" diff --git a/test/fixedbugs/issue25741.go b/test/fixedbugs/issue25741.go new file mode 100644 index 000000000..c76e97594 --- /dev/null +++ b/test/fixedbugs/issue25741.go @@ -0,0 +1,14 @@ +// compile + +// Copyright 2018 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. + +package main + +var s []int + +func main() { + i := -1 + s[i] = 0 +} diff --git a/test/fixedbugs/issue25776.go b/test/fixedbugs/issue25776.go new file mode 100644 index 000000000..e05c0bce4 --- /dev/null +++ b/test/fixedbugs/issue25776.go @@ -0,0 +1,99 @@ +// run + +// Copyright 2018 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. + +package main + +const ( + Upper = true + blas_Upper = 121 + badTriangle = "bad triangle" +) + +// Triangular represents a triangular matrix. Triangular matrices are always square. +type Triangular interface { + // Triangular returns the number of rows/columns in the matrix and its + // orientation. + Tryangle() (mmmm int, kynd bool) + Triangle() (mmmm int, kynd bool) +} + +// blas64_Triangular represents a triangular matrix using the conventional storage scheme. +type blas64_Triangular struct { + Stride int + Uplo int +} + +// TriDense represents an upper or lower triangular matrix in dense storage +// format. +type TriDense struct { + mat blas64_Triangular +} + +func NewTriDense() *TriDense { + return &TriDense{ + mat: blas64_Triangular{ + Stride: 3, + Uplo: blas_Upper, + }, + } +} + +func (t *TriDense) isUpper() bool { + return isUpperUplo(t.mat.Uplo) +} + +func (t *TriDense) triKind() bool { + return isUpperUplo(t.mat.Uplo) +} + +func isUpperUplo(u int) bool { + switch u { + case blas_Upper: + return true + default: + panic(badTriangle) + } +} + +func (t *TriDense) IsZero() bool { + return t.mat.Stride == 0 +} + +//go:noinline +func (t *TriDense) ScaleTri(f float64, a Triangular) { + n, kind := a.Triangle() + if kind == false { + println("ScaleTri n, kind=", n, ", ", kind, " (FAIL, expected true)") + } +} + +//go:noinline +func (t *TriDense) ScaleTry(f float64, a Triangular) { + n, kind := a.Tryangle() + if kind == false { + println("ScaleTry n, kind=", n, ", ", kind, " (FAIL, expected true)") + } +} + +// Triangle failed (before fix) +func (t *TriDense) Triangle() (nnnn int, kind bool) { + return 3, !t.IsZero() && t.triKind() +} + +// Tryangle works -- difference is not-named output parameters. +func (t *TriDense) Tryangle() (int, bool) { + return 3, !t.IsZero() && t.triKind() +} + +func main() { + ta := NewTriDense() + n, kind := ta.Triangle() + if kind == false { + println(" main n, kind=", n, ", ", kind, " (FAIL, expected true)") + } + ta.ScaleTri(1, ta) + ta.ScaleTry(1, ta) +} diff --git a/test/fixedbugs/issue25958.go b/test/fixedbugs/issue25958.go new file mode 100644 index 000000000..ba7ee8223 --- /dev/null +++ b/test/fixedbugs/issue25958.go @@ -0,0 +1,17 @@ +// errorcheck + +// Copyright 2018 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. + +package p + +// Verify that the "must be receive" error for "case done:" appears +// on the line of the case clause, not the line of the done declaration. + +func f(done chan struct{}) { + select { + case done: // ERROR "must be receive", "not used" + case (chan struct{})(done): // ERROR "must be receive" + } +} diff --git a/test/fixedbugs/issue25966.go b/test/fixedbugs/issue25966.go new file mode 100644 index 000000000..7b5f2a7e4 --- /dev/null +++ b/test/fixedbugs/issue25966.go @@ -0,0 +1,24 @@ +// compile -N + +// Copyright 2018 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. + +// Issue 25966: liveness code complains autotmp live on +// function entry. + +package p + +var F = []func(){ + func() func() { return (func())(nil) }(), +} + +var A = []int{} + +type ss struct { + string + float64 + i int +} + +var V = A[ss{}.i] diff --git a/test/fixedbugs/issue25984.dir/p.go b/test/fixedbugs/issue25984.dir/p.go new file mode 100644 index 000000000..306d6a489 --- /dev/null +++ b/test/fixedbugs/issue25984.dir/p.go @@ -0,0 +1,15 @@ +// Copyright 2018 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. + +package p + +type m struct { + link *m +} + +var head *m + +func F(m *int) bool { + return head != nil +} diff --git a/test/fixedbugs/issue25984.dir/q.go b/test/fixedbugs/issue25984.dir/q.go new file mode 100644 index 000000000..64d25870b --- /dev/null +++ b/test/fixedbugs/issue25984.dir/q.go @@ -0,0 +1,11 @@ +// Copyright 2018 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. + +package q + +import "./p" + +func G() { + p.F(nil) +} diff --git a/test/fixedbugs/issue25984.go b/test/fixedbugs/issue25984.go new file mode 100644 index 000000000..128cf9d06 --- /dev/null +++ b/test/fixedbugs/issue25984.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2018 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. + +package ignored diff --git a/test/fixedbugs/issue25993.go b/test/fixedbugs/issue25993.go new file mode 100644 index 000000000..3253cd82f --- /dev/null +++ b/test/fixedbugs/issue25993.go @@ -0,0 +1,21 @@ +// compile -d=ssa/check/on + +// Copyright 2018 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. + +// Issue 25993: SSA check fails on ARM. + +package p + +func f() { + var x int + var B0 bool + B0 = !B0 || B0 + if B0 && B0 { + x = -1 + } + var AI []int + var AB []bool + _ = AI[x] > 0 && AB[x] +} diff --git a/test/fixedbugs/issue26024.go b/test/fixedbugs/issue26024.go new file mode 100644 index 000000000..78877b21f --- /dev/null +++ b/test/fixedbugs/issue26024.go @@ -0,0 +1,28 @@ +// compile +// Copyright 2018 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. + +package r + +// f compiles into code where no stores remain in the two successors +// of a write barrier block; i.e., they are empty. Pre-fix, this +// results in an unexpected input to markUnsafePoints, that expects to +// see a pair of non-empty plain blocks. +func f() { + var i int + var s string + for len(s) < len(s) { + i++ + s = "a" + } + var b bool + var sa []string + for true { + sa = []string{""} + for b || i == 0 { + } + b = !b + _ = sa + } +} diff --git a/test/fixedbugs/issue26043.go b/test/fixedbugs/issue26043.go new file mode 100644 index 000000000..fe32947b0 --- /dev/null +++ b/test/fixedbugs/issue26043.go @@ -0,0 +1,32 @@ +// compile + +// Copyright 2018 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. + +// This program results in a loop inferred to increment +// j by 0, causing bounds check elimination to attempt +// something%0, which panics (in the bug). + +package q + +func f() { + var s1 string + var b bool + if b { + b = !b + s1 += "a" + } + + var s2 string + var i, j int + if (s1 <= "") || (s2 >= "") { + j = len(s1[:6]) + } else { + i = len("b") + } + + for j < 0 { + j += i + } +} diff --git a/test/fixedbugs/issue26094.go b/test/fixedbugs/issue26094.go new file mode 100644 index 000000000..7af8fac6b --- /dev/null +++ b/test/fixedbugs/issue26094.go @@ -0,0 +1,49 @@ +// run + +// Copyright 2018 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. + +package main + +import "strings" + +var X interface{} + +type T struct{} + +func scopes() { + p, ok := recover().(error) + if ok && strings.Contains(p.Error(), "different scopes") { + return + } + panic(p) +} + +func F1() { + type T struct{} + X = T{} +} + +func F2() { + type T struct{} + defer scopes() + _ = X.(T) +} + +func F3() { + defer scopes() + _ = X.(T) +} + +func F4() { + X = T{} +} + +func main() { + F1() // set X to F1's T + F2() // check that X is not F2's T + F3() // check that X is not package T + F4() // set X to package T + F2() // check that X is not F2's T +} diff --git a/test/fixedbugs/issue26097.go b/test/fixedbugs/issue26097.go new file mode 100644 index 000000000..eedd6bacd --- /dev/null +++ b/test/fixedbugs/issue26097.go @@ -0,0 +1,47 @@ +// run + +// Copyright 2018 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. + +package main + +//go:noinline +func cmovClobberAX64(v1, v2 int64, x1, x2 float64) int64 { + r := v1 + if x1 == x2 { + r = v2 + } + return r +} + +//go:noinline +func cmovClobberAX32(v1, v2 int32, x1, x2 float64) int32 { + r := v1 + if x1 == x2 { + r = v2 + } + return r +} + +//go:noinline +func cmovClobberAX16(v1, v2 int16, x1, x2 float64) int16 { + r := v1 + if x1 == x2 { + r = v2 + } + return r +} + +func main() { + if cmovClobberAX16(1, 2, 4.0, 5.0) != 1 { + panic("CMOVQEQF causes incorrect code") + } + if cmovClobberAX32(1, 2, 4.0, 5.0) != 1 { + panic("CMOVQEQF causes incorrect code") + } + if cmovClobberAX64(1, 2, 4.0, 5.0) != 1 { + panic("CMOVQEQF causes incorrect code") + } + +} diff --git a/test/fixedbugs/issue26105.go b/test/fixedbugs/issue26105.go new file mode 100644 index 000000000..88a5f162f --- /dev/null +++ b/test/fixedbugs/issue26105.go @@ -0,0 +1,25 @@ +// compile + +// Copyright 2018 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. + +// Triggers a bug in writebarrier, which inserts one +// between (first block) OpAddr x and (second block) a VarDef x, +// which are then in the wrong order and unable to be +// properly scheduled. + +package q + +var S interface{} + +func F(n int) { + fun := func(x int) int { + S = 1 + return n + } + i := fun(([]int{})[n]) + + var fc [2]chan int + S = (([1][2]chan int{fc})[i][i]) +} diff --git a/test/fixedbugs/issue26116.go b/test/fixedbugs/issue26116.go new file mode 100644 index 000000000..8a6348181 --- /dev/null +++ b/test/fixedbugs/issue26116.go @@ -0,0 +1,18 @@ +// run + +// Copyright 2018 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. + +package main + +func main() { + s := []int{0, 1, 2} + i := 1 + for i > 0 && s[i] != 2 { + i++ + } + if i != 2 { + panic("loop didn't run") + } +} diff --git a/test/fixedbugs/issue26120.go b/test/fixedbugs/issue26120.go new file mode 100644 index 000000000..94bf7d9cb --- /dev/null +++ b/test/fixedbugs/issue26120.go @@ -0,0 +1,23 @@ +// compile + +// Copyright 2018 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. + +// Issue 26120: INDEX of 1-element but non-SSAable array +// is mishandled when building SSA. + +package p + +type T [1]struct { + f []int + i, j int +} + +func F() { + var v T + f := func() T { + return v + } + _ = []int{}[f()[0].i] +} diff --git a/test/fixedbugs/issue26153.go b/test/fixedbugs/issue26153.go new file mode 100644 index 000000000..53f53cf8a --- /dev/null +++ b/test/fixedbugs/issue26153.go @@ -0,0 +1,29 @@ +// run + +// Copyright 2018 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. + +// Issue 26153. The write to ps was incorrectly +// removed by the dead auto elimination pass. + +package main + +const hello = "hello world" + +func main() { + var s string + mangle(&s) + if s != hello { + panic("write incorrectly elided") + } +} + +//go:noinline +func mangle(ps *string) { + if ps == nil { + var s string + ps = &s + } + *ps = hello +} diff --git a/test/fixedbugs/issue26163.go b/test/fixedbugs/issue26163.go new file mode 100644 index 000000000..3f3d77859 --- /dev/null +++ b/test/fixedbugs/issue26163.go @@ -0,0 +1,17 @@ +// compile -N -d=softfloat + +// Copyright 2018 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. + +// Issue 26163: dead store generated in late opt messes +// up store chain calculation. + +package p + +var i int +var A = ([]*int{})[i] + +var F func(float64, complex128) int +var C chan complex128 +var B = F(1, 1+(<-C)) diff --git a/test/fixedbugs/issue26248.go b/test/fixedbugs/issue26248.go new file mode 100644 index 000000000..2f04e4822 --- /dev/null +++ b/test/fixedbugs/issue26248.go @@ -0,0 +1,39 @@ +// run + +// Copyright 2018 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. + +// Issue 26248: gccgo miscompiles interface field expression. +// In G().M where G returns an interface, G() is evaluated twice. + +package main + +type I interface { + M() +} + +type T struct{} + +func (T) M() {} + +var g = 0 + +//go:noinline +func G() I { + g++ + return T{} +} + +//go:noinline +func Use(interface{}) {} + +func main() { + x := G().M + Use(x) + + if g != 1 { + println("want 1, got", g) + panic("FAIL") + } +} diff --git a/test/fixedbugs/issue26335.go b/test/fixedbugs/issue26335.go new file mode 100644 index 000000000..a97b4b6c7 --- /dev/null +++ b/test/fixedbugs/issue26335.go @@ -0,0 +1,32 @@ +// run + +// Copyright 2018 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. + +// gccgo mishandled passing a struct with an empty field through +// reflect.Value.Call. + +package main + +import ( + "reflect" +) + +type Empty struct { + f1, f2 *byte + empty struct{} +} + +func F(e Empty, s []string) { + if len(s) != 1 || s[0] != "hi" { + panic("bad slice") + } +} + +func main() { + reflect.ValueOf(F).Call([]reflect.Value{ + reflect.ValueOf(Empty{}), + reflect.ValueOf([]string{"hi"}), + }) +} diff --git a/test/fixedbugs/issue26340.go b/test/fixedbugs/issue26340.go new file mode 100644 index 000000000..f973ce223 --- /dev/null +++ b/test/fixedbugs/issue26340.go @@ -0,0 +1,21 @@ +// compile + +// Copyright 2018 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. + +// gccgo did not permit omitting the type of a composite literal +// element when one of the middle omitted types was a pointer type. + +package p + +type S []T +type T struct { x int } + +var _ = map[string]*S{ + "a": { + { 1 }, + }, +} + +var _ = [1]*S{ { {1}, } } diff --git a/test/fixedbugs/issue26341.dir/a.go b/test/fixedbugs/issue26341.dir/a.go new file mode 100644 index 000000000..4fd23c796 --- /dev/null +++ b/test/fixedbugs/issue26341.dir/a.go @@ -0,0 +1,11 @@ +// Copyright 2018 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. + +package a + +type k int + +func (k) F() {} + +type M map[k]int diff --git a/test/fixedbugs/issue26341.dir/b.go b/test/fixedbugs/issue26341.dir/b.go new file mode 100644 index 000000000..30b8c25a5 --- /dev/null +++ b/test/fixedbugs/issue26341.dir/b.go @@ -0,0 +1,13 @@ +// Copyright 2018 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. + +package b + +import "./a" + +func f() { + for k := range (a.M{}) { + k.F() + } +} diff --git a/test/fixedbugs/issue26341.go b/test/fixedbugs/issue26341.go new file mode 100644 index 000000000..128cf9d06 --- /dev/null +++ b/test/fixedbugs/issue26341.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2018 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. + +package ignored diff --git a/test/fixedbugs/issue26407.go b/test/fixedbugs/issue26407.go new file mode 100644 index 000000000..aa8e79c1e --- /dev/null +++ b/test/fixedbugs/issue26407.go @@ -0,0 +1,58 @@ +// run + +// Copyright 2018 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. + +// Issue 26407: ensure that stack variables which have +// had their address taken and then used in a comparison, +// but are otherwise unused, are cleared. + +package main + +func main() { + poison() + test() +} + +//go:noinline +func poison() { + // initialise the stack with invalid pointers + var large [256]uintptr + for i := range large { + large[i] = 1 + } + use(large[:]) +} + +//go:noinline +func test() { + a := 2 + x := &a + if x != compare(&x) { + panic("not possible") + } +} + +//go:noinline +func compare(x **int) *int { + var y *int + if x == &y { + panic("not possible") + } + // grow the stack to trigger a check for invalid pointers + grow() + if x == &y { + panic("not possible") + } + return *x +} + +//go:noinline +func grow() { + var large [1 << 16]uintptr + use(large[:]) +} + +//go:noinline +func use(_ []uintptr) { } diff --git a/test/fixedbugs/issue26416.go b/test/fixedbugs/issue26416.go new file mode 100644 index 000000000..bc37fd9d3 --- /dev/null +++ b/test/fixedbugs/issue26416.go @@ -0,0 +1,27 @@ +// errorcheck + +// Copyright 2018 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. + +package main + +type t1 struct { + t1f1 int + t1f2 int +} +type t2 struct { + t2f1 int + t2f2 int + *t1 +} +type t3 struct { + t3f1 int + *t2 +} + +var ( + _ = t2{t1f1: 600} // ERROR "cannot use promoted field t1.t1f1 in struct literal of type t2" + _ = t3{t1f2: 800} // ERROR "cannot use promoted field t2.t1.t1f2 in struct literal of type t3" + _ = t3{t2f1: 900} // ERROR "cannot use promoted field t2.t2f1 in struct literal of type t3" +) diff --git a/test/fixedbugs/issue26426.go b/test/fixedbugs/issue26426.go new file mode 100644 index 000000000..4f2a452fa --- /dev/null +++ b/test/fixedbugs/issue26426.go @@ -0,0 +1,29 @@ +//run + +// Copyright 2018 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. + +package main + +//go:noinline +func f(p *int, v int, q1, q2 *int, r *bool) { + x := *r + if x { + *q1 = 1 + } + *p = *p + v // This must clobber flags. Otherwise we keep x in a flags register. + if x { + *q2 = 1 + } +} + +func main() { + var p int + var q1, q2 int + var b bool + f(&p, 1, &q1, &q2, &b) + if q1 != 0 || q2 != 0 { + panic("bad") + } +} diff --git a/test/fixedbugs/issue26438.go b/test/fixedbugs/issue26438.go new file mode 100644 index 000000000..d44da21eb --- /dev/null +++ b/test/fixedbugs/issue26438.go @@ -0,0 +1,26 @@ +// run + +// Copyright 2018 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. + +// Issue 26438: arm64 backend may use 64-bit TST for +// "if uint32(a)&uint32(b) == 0", which should be +// 32-bit TSTW + +package main + +//go:noinline +func tstw(a, b uint64) uint64 { + if uint32(a)&uint32(b) == 0 { + return 100 + } else { + return 200 + } +} + +func main() { + if tstw(0xff00000000, 0xaa00000000) == 200 { + panic("impossible") + } +} diff --git a/test/fixedbugs/issue26495.go b/test/fixedbugs/issue26495.go new file mode 100644 index 000000000..11ea36017 --- /dev/null +++ b/test/fixedbugs/issue26495.go @@ -0,0 +1,36 @@ +// run + +// Copyright 2018 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. + +// Issue 26495: gccgo produces incorrect order of evaluation +// for expressions involving &&, || subexpressions. + +package main + +var i int + +func checkorder(order int) { + if i != order { + panic("FAIL: wrong evaluation order") + } + i++ +} + +func A() bool { checkorder(1); return true } +func B() bool { checkorder(2); return true } +func C() bool { checkorder(5); return false } +func D() bool { panic("FAIL: D should not be called") } +func E() int { checkorder(3); return 0 } +func F() int { checkorder(0); return 0 } +func G(bool) int { checkorder(9); return 0 } +func H(int, bool, int) int { checkorder(7); return 0 } +func I(int) bool { checkorder(8); return true } +func J() int { checkorder(4); return 0 } +func K() int { checkorder(6); return 0 } +func L() int { checkorder(10); return 0 } + +func main() { + _ = F() + G(A() && B() && I(E()+H(J(), C() && D(), K()))) + L() +} diff --git a/test/fixedbugs/issue4085b.go b/test/fixedbugs/issue4085b.go index b91bbd748..6bf315fcc 100644 --- a/test/fixedbugs/issue4085b.go +++ b/test/fixedbugs/issue4085b.go @@ -21,13 +21,12 @@ func main() { shouldPanic("cap out of range", func() { _ = make(T, 0, int64(n)) }) var t *byte if unsafe.Sizeof(t) == 8 { - n = 1 << 20 - n <<= 20 - shouldPanic("len out of range", func() { _ = make(T, n) }) - shouldPanic("cap out of range", func() { _ = make(T, 0, n) }) - n <<= 20 - shouldPanic("len out of range", func() { _ = make(T, n) }) - shouldPanic("cap out of range", func() { _ = make(T, 0, n) }) + var n2 int64 = 1 << 50 + shouldPanic("len out of range", func() { _ = make(T, int(n2)) }) + shouldPanic("cap out of range", func() { _ = make(T, 0, int(n2)) }) + n2 = 1<<63 - 1 + shouldPanic("len out of range", func() { _ = make(T, int(n2)) }) + shouldPanic("cap out of range", func() { _ = make(T, 0, int(n2)) }) } else { n = 1<<31 - 1 shouldPanic("len out of range", func() { _ = make(T, n) }) @@ -35,6 +34,12 @@ func main() { shouldPanic("len out of range", func() { _ = make(T, int64(n)) }) shouldPanic("cap out of range", func() { _ = make(T, 0, int64(n)) }) } + + // Test make in append panics since the gc compiler optimizes makes in appends. + shouldPanic("len out of range", func() { _ = append(T{}, make(T, n)...) }) + shouldPanic("cap out of range", func() { _ = append(T{}, make(T, 0, n)...) }) + shouldPanic("len out of range", func() { _ = append(T{}, make(T, int64(n))...) }) + shouldPanic("cap out of range", func() { _ = append(T{}, make(T, 0, int64(n))...) }) } func shouldPanic(str string, f func()) { diff --git a/test/fixedbugs/issue6899.go b/test/fixedbugs/issue6899.go index f98f551b3..d7f857802 100644 --- a/test/fixedbugs/issue6899.go +++ b/test/fixedbugs/issue6899.go @@ -1,4 +1,4 @@ -// cmpout +// run // Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/fixedbugs/issue9355.go b/test/fixedbugs/issue9355.go index 10f8c7306..9657e6449 100644 --- a/test/fixedbugs/issue9355.go +++ b/test/fixedbugs/issue9355.go @@ -16,7 +16,7 @@ import ( ) func main() { - if runtime.Compiler != "gc" || runtime.GOOS == "nacl" { + if runtime.Compiler != "gc" || runtime.GOOS == "nacl" || runtime.GOOS == "js" { return } diff --git a/test/fixedbugs/issue9604b.go b/test/fixedbugs/issue9604b.go index ebbd205ba..d32116b85 100644 --- a/test/fixedbugs/issue9604b.go +++ b/test/fixedbugs/issue9604b.go @@ -4,6 +4,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// terribly slow on wasm +// +build !wasm + package main import ( diff --git a/test/fixedbugs/issue9862_run.go b/test/fixedbugs/issue9862_run.go index be22f4058..299e80954 100644 --- a/test/fixedbugs/issue9862_run.go +++ b/test/fixedbugs/issue9862_run.go @@ -1,4 +1,4 @@ -// +build !nacl +// +build !nacl,!js // run // Copyright 2015 The Go Authors. All rights reserved. diff --git a/test/gc2.go b/test/gc2.go index 31b36d8a0..2f8eb9b70 100644 --- a/test/gc2.go +++ b/test/gc2.go @@ -1,4 +1,4 @@ -// +build !nacl +// +build !nacl,!js // run // Copyright 2011 The Go Authors. All rights reserved. diff --git a/test/gcgort.go b/test/gcgort.go new file mode 100644 index 000000000..973e79659 --- /dev/null +++ b/test/gcgort.go @@ -0,0 +1,1850 @@ +// run + +// Copyright 2018 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 independent goroutines modifying a comprehensive +// variety of vars during aggressive garbage collection. + +// The point is to catch GC regressions like fixedbugs/issue22781.go + +package main + +import ( + "errors" + "runtime" + "runtime/debug" + "sync" +) + +const ( + goroutines = 8 + allocs = 8 + mods = 8 + + length = 9 +) + +func main() { + debug.SetGCPercent(1) + var wg sync.WaitGroup + for i := 0; i < goroutines; i++ { + for _, t := range types { + err := t.valid() + if err != nil { + panic(err) + } + wg.Add(1) + go func(f modifier) { + var wg2 sync.WaitGroup + for j := 0; j < allocs; j++ { + wg2.Add(1) + go func() { + f.t() + wg2.Done() + }() + wg2.Add(1) + go func() { + f.pointerT() + wg2.Done() + }() + wg2.Add(1) + go func() { + f.arrayT() + wg2.Done() + }() + wg2.Add(1) + go func() { + f.sliceT() + wg2.Done() + }() + wg2.Add(1) + go func() { + f.mapT() + wg2.Done() + }() + wg2.Add(1) + go func() { + f.mapPointerKeyT() + wg2.Done() + }() + wg2.Add(1) + go func() { + f.chanT() + wg2.Done() + }() + wg2.Add(1) + go func() { + f.interfaceT() + wg2.Done() + }() + } + wg2.Wait() + wg.Done() + }(t) + } + } + wg.Wait() +} + +type modifier struct { + name string + t func() + pointerT func() + arrayT func() + sliceT func() + mapT func() + mapPointerKeyT func() + chanT func() + interfaceT func() +} + +func (a modifier) valid() error { + switch { + case a.name == "": + return errors.New("modifier without name") + case a.t == nil: + return errors.New(a.name + " missing t") + case a.pointerT == nil: + return errors.New(a.name + " missing pointerT") + case a.arrayT == nil: + return errors.New(a.name + " missing arrayT") + case a.sliceT == nil: + return errors.New(a.name + " missing sliceT") + case a.mapT == nil: + return errors.New(a.name + " missing mapT") + case a.mapPointerKeyT == nil: + return errors.New(a.name + " missing mapPointerKeyT") + case a.chanT == nil: + return errors.New(a.name + " missing chanT") + case a.interfaceT == nil: + return errors.New(a.name + " missing interfaceT") + default: + return nil + } +} + +var types = []modifier{ + modifier{ + name: "bool", + t: func() { + var a bool + for i := 0; i < mods; i++ { + a = !a + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *bool { return new(bool) }() + for i := 0; i < mods; i++ { + *a = !*a + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]bool{} + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j] = !a[j] + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]bool, length) + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j] = !a[j] + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[bool]bool) + for i := 0; i < mods; i++ { + a[false] = !a[false] + a[true] = !a[true] + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*bool]bool) + for i := 0; i < length; i++ { + a[new(bool)] = false + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, v := range a { + a[k] = !v + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan bool) + for i := 0; i < mods; i++ { + go func() { a <- false }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(bool(false)) + for i := 0; i < mods; i++ { + a = !a.(bool) + runtime.Gosched() + } + }, + }, + modifier{ + name: "uint8", + t: func() { + var u uint8 + for i := 0; i < mods; i++ { + u++ + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *uint8 { return new(uint8) }() + for i := 0; i < mods; i++ { + *a++ + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]uint8{} + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]uint8, length) + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[uint8]uint8) + for i := 0; i < length; i++ { + a[uint8(i)] = uint8(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*uint8]uint8) + for i := 0; i < length; i++ { + a[new(uint8)] = uint8(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan uint8) + for i := 0; i < mods; i++ { + go func() { a <- uint8(i) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(uint8(0)) + for i := 0; i < mods; i++ { + a = a.(uint8) + 1 + runtime.Gosched() + } + }, + }, + modifier{ + name: "uint16", + t: func() { + var u uint16 + for i := 0; i < mods; i++ { + u++ + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *uint16 { return new(uint16) }() + for i := 0; i < mods; i++ { + *a++ + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]uint16{} + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]uint16, length) + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[uint16]uint16) + for i := 0; i < length; i++ { + a[uint16(i)] = uint16(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*uint16]uint16) + for i := 0; i < length; i++ { + a[new(uint16)] = uint16(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan uint16) + for i := 0; i < mods; i++ { + go func() { a <- uint16(i) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(uint16(0)) + for i := 0; i < mods; i++ { + a = a.(uint16) + 1 + runtime.Gosched() + } + }, + }, + modifier{ + name: "uint32", + t: func() { + var u uint32 + for i := 0; i < mods; i++ { + u++ + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *uint32 { return new(uint32) }() + for i := 0; i < mods; i++ { + *a++ + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]uint32{} + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]uint32, length) + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[uint32]uint32) + for i := 0; i < length; i++ { + a[uint32(i)] = uint32(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*uint32]uint32) + for i := 0; i < length; i++ { + a[new(uint32)] = uint32(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan uint32) + for i := 0; i < mods; i++ { + go func() { a <- uint32(i) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(uint32(0)) + for i := 0; i < mods; i++ { + a = a.(uint32) + 1 + runtime.Gosched() + } + }, + }, + modifier{ + name: "uint64", + t: func() { + var u uint64 + for i := 0; i < mods; i++ { + u++ + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *uint64 { return new(uint64) }() + for i := 0; i < mods; i++ { + *a++ + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]uint64{} + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]uint64, length) + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[uint64]uint64) + for i := 0; i < length; i++ { + a[uint64(i)] = uint64(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*uint64]uint64) + for i := 0; i < length; i++ { + a[new(uint64)] = uint64(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan uint64) + for i := 0; i < mods; i++ { + go func() { a <- uint64(i) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(uint64(0)) + for i := 0; i < mods; i++ { + a = a.(uint64) + 1 + runtime.Gosched() + } + }, + }, + modifier{ + name: "int8", + t: func() { + var u int8 + for i := 0; i < mods; i++ { + u++ + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *int8 { return new(int8) }() + for i := 0; i < mods; i++ { + *a++ + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]int8{} + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]int8, length) + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[int8]int8) + for i := 0; i < length; i++ { + a[int8(i)] = int8(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*int8]int8) + for i := 0; i < length; i++ { + a[new(int8)] = int8(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan int8) + for i := 0; i < mods; i++ { + go func() { a <- int8(i) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(int8(0)) + for i := 0; i < mods; i++ { + a = a.(int8) + 1 + runtime.Gosched() + } + }, + }, + modifier{ + name: "int16", + t: func() { + var u int16 + for i := 0; i < mods; i++ { + u++ + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *int16 { return new(int16) }() + for i := 0; i < mods; i++ { + *a++ + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]int16{} + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]int16, length) + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[int16]int16) + for i := 0; i < length; i++ { + a[int16(i)] = int16(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*int16]int16) + for i := 0; i < length; i++ { + a[new(int16)] = int16(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan int16) + for i := 0; i < mods; i++ { + go func() { a <- int16(i) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(int16(0)) + for i := 0; i < mods; i++ { + a = a.(int16) + 1 + runtime.Gosched() + } + }, + }, + modifier{ + name: "int32", + t: func() { + var u int32 + for i := 0; i < mods; i++ { + u++ + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *int32 { return new(int32) }() + for i := 0; i < mods; i++ { + *a++ + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]int32{} + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]int32, length) + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[int32]int32) + for i := 0; i < length; i++ { + a[int32(i)] = int32(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*int32]int32) + for i := 0; i < length; i++ { + a[new(int32)] = int32(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan int32) + for i := 0; i < mods; i++ { + go func() { a <- int32(i) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(int32(0)) + for i := 0; i < mods; i++ { + a = a.(int32) + 1 + runtime.Gosched() + } + }, + }, + modifier{ + name: "int64", + t: func() { + var u int64 + for i := 0; i < mods; i++ { + u++ + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *int64 { return new(int64) }() + for i := 0; i < mods; i++ { + *a++ + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]int64{} + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]int64, length) + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[int64]int64) + for i := 0; i < length; i++ { + a[int64(i)] = int64(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*int64]int64) + for i := 0; i < length; i++ { + a[new(int64)] = int64(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan int64) + for i := 0; i < mods; i++ { + go func() { a <- int64(i) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(int64(0)) + for i := 0; i < mods; i++ { + a = a.(int64) + 1 + runtime.Gosched() + } + }, + }, + modifier{ + name: "float32", + t: func() { + u := float32(1.01) + for i := 0; i < mods; i++ { + u *= 1.01 + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *float32 { return new(float32) }() + *a = 1.01 + for i := 0; i < mods; i++ { + *a *= 1.01 + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]float32{} + for i := 0; i < length; i++ { + a[i] = float32(1.01) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j] *= 1.01 + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]float32, length) + for i := 0; i < length; i++ { + a[i] = float32(1.01) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j] *= 1.01 + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[float32]float32) + for i := 0; i < length; i++ { + a[float32(i)] = float32(i) + 0.01 + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k] *= 1.01 + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*float32]float32) + for i := 0; i < length; i++ { + a[new(float32)] = float32(i) + 0.01 + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k] *= 1.01 + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan float32) + for i := 0; i < mods; i++ { + go func() { a <- float32(i) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(float32(0)) + for i := 0; i < mods; i++ { + a = a.(float32) * 1.01 + runtime.Gosched() + } + }, + }, + modifier{ + name: "float64", + t: func() { + u := float64(1.01) + for i := 0; i < mods; i++ { + u *= 1.01 + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *float64 { return new(float64) }() + *a = 1.01 + for i := 0; i < mods; i++ { + *a *= 1.01 + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]float64{} + for i := 0; i < length; i++ { + a[i] = float64(1.01) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j] *= 1.01 + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]float64, length) + for i := 0; i < length; i++ { + a[i] = float64(1.01) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j] *= 1.01 + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[float64]float64) + for i := 0; i < length; i++ { + a[float64(i)] = float64(i) + 0.01 + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k] *= 1.01 + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*float64]float64) + for i := 0; i < length; i++ { + a[new(float64)] = float64(i) + 0.01 + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k] *= 1.01 + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan float64) + for i := 0; i < mods; i++ { + go func() { a <- float64(i) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(float64(0)) + for i := 0; i < mods; i++ { + a = a.(float64) * 1.01 + runtime.Gosched() + } + }, + }, + modifier{ + name: "complex64", + t: func() { + c := complex64(complex(float32(1.01), float32(1.01))) + for i := 0; i < mods; i++ { + c = complex(real(c)*1.01, imag(c)*1.01) + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *complex64 { return new(complex64) }() + *a = complex64(complex(float32(1.01), float32(1.01))) + for i := 0; i < mods; i++ { + *a *= complex(real(*a)*1.01, imag(*a)*1.01) + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]complex64{} + for i := 0; i < length; i++ { + a[i] = complex64(complex(float32(1.01), float32(1.01))) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j] *= complex(real(a[j])*1.01, imag(a[j])*1.01) + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]complex64, length) + for i := 0; i < length; i++ { + a[i] = complex64(complex(float32(1.01), float32(1.01))) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j] *= complex(real(a[j])*1.01, imag(a[j])*1.01) + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[complex64]complex64) + for i := 0; i < length; i++ { + a[complex64(complex(float32(i), float32(i)))] = complex64(complex(float32(i), float32(i))) + 0.01 + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k] *= complex(real(a[k])*1.01, imag(a[k])*1.01) + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*complex64]complex64) + for i := 0; i < length; i++ { + a[new(complex64)] = complex64(complex(float32(i), float32(i))) + 0.01 + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k] *= complex(real(a[k])*1.01, imag(a[k])*1.01) + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan complex64) + for i := 0; i < mods; i++ { + go func() { a <- complex64(complex(float32(i), float32(i))) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(complex64(complex(float32(1.01), float32(1.01)))) + for i := 0; i < mods; i++ { + a = a.(complex64) * complex(real(a.(complex64))*1.01, imag(a.(complex64))*1.01) + runtime.Gosched() + } + }, + }, + modifier{ + name: "complex128", + t: func() { + c := complex128(complex(float64(1.01), float64(1.01))) + for i := 0; i < mods; i++ { + c = complex(real(c)*1.01, imag(c)*1.01) + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *complex128 { return new(complex128) }() + *a = complex128(complex(float64(1.01), float64(1.01))) + for i := 0; i < mods; i++ { + *a *= complex(real(*a)*1.01, imag(*a)*1.01) + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]complex128{} + for i := 0; i < length; i++ { + a[i] = complex128(complex(float64(1.01), float64(1.01))) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j] *= complex(real(a[j])*1.01, imag(a[j])*1.01) + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]complex128, length) + for i := 0; i < length; i++ { + a[i] = complex128(complex(float64(1.01), float64(1.01))) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j] *= complex(real(a[j])*1.01, imag(a[j])*1.01) + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[complex128]complex128) + for i := 0; i < length; i++ { + a[complex128(complex(float64(i), float64(i)))] = complex128(complex(float64(i), float64(i))) + 0.01 + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k] *= complex(real(a[k])*1.01, imag(a[k])*1.01) + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*complex128]complex128) + for i := 0; i < length; i++ { + a[new(complex128)] = complex128(complex(float64(i), float64(i))) + 0.01 + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k] *= complex(real(a[k])*1.01, imag(a[k])*1.01) + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan complex128) + for i := 0; i < mods; i++ { + go func() { a <- complex128(complex(float64(i), float64(i))) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(complex128(complex(float64(1.01), float64(1.01)))) + for i := 0; i < mods; i++ { + a = a.(complex128) * complex(real(a.(complex128))*1.01, imag(a.(complex128))*1.01) + runtime.Gosched() + } + }, + }, + modifier{ + name: "byte", + t: func() { + var a byte + for i := 0; i < mods; i++ { + a++ + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *byte { return new(byte) }() + for i := 0; i < mods; i++ { + *a++ + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]byte{} + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]byte, length) + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[byte]byte) + for i := 0; i < length; i++ { + a[byte(i)] = byte(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*byte]byte) + for i := 0; i < length; i++ { + a[new(byte)] = byte(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan byte) + for i := 0; i < mods; i++ { + go func() { a <- byte(i) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(byte(0)) + for i := 0; i < mods; i++ { + a = a.(byte) + 1 + runtime.Gosched() + } + }, + }, + modifier{ + name: "rune", + t: func() { + var a rune + for i := 0; i < mods; i++ { + a++ + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *rune { return new(rune) }() + for i := 0; i < mods; i++ { + *a++ + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]rune{} + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]rune, length) + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[rune]rune) + for i := 0; i < length; i++ { + a[rune(i)] = rune(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*rune]rune) + for i := 0; i < length; i++ { + a[new(rune)] = rune(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan rune) + for i := 0; i < mods; i++ { + go func() { a <- rune(i) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(rune(0)) + for i := 0; i < mods; i++ { + a = a.(rune) + 1 + runtime.Gosched() + } + }, + }, + modifier{ + name: "uint", + t: func() { + var a uint + for i := 0; i < mods; i++ { + a++ + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *uint { return new(uint) }() + for i := 0; i < mods; i++ { + *a++ + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]uint{} + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]uint, length) + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[uint]uint) + for i := 0; i < length; i++ { + a[uint(i)] = uint(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*uint]uint) + for i := 0; i < length; i++ { + a[new(uint)] = uint(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan uint) + for i := 0; i < mods; i++ { + go func() { a <- uint(i) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(uint(0)) + for i := 0; i < mods; i++ { + a = a.(uint) + 1 + runtime.Gosched() + } + }, + }, + modifier{ + name: "int", + t: func() { + var a int + for i := 0; i < mods; i++ { + a++ + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *int { return new(int) }() + for i := 0; i < mods; i++ { + *a++ + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]int{} + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]int, length) + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[int]int) + for i := 0; i < length; i++ { + a[int(i)] = int(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*int]int) + for i := 0; i < length; i++ { + a[new(int)] = int(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan int) + for i := 0; i < mods; i++ { + go func() { a <- int(i) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(int(0)) + for i := 0; i < mods; i++ { + a = a.(int) + 1 + runtime.Gosched() + } + }, + }, + modifier{ + name: "uintptr", + t: func() { + var a uintptr + for i := 0; i < mods; i++ { + a++ + runtime.Gosched() + } + }, + pointerT: func() { + a := func() *uintptr { return new(uintptr) }() + for i := 0; i < mods; i++ { + *a++ + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]uintptr{} + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]uintptr, length) + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j]++ + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[uintptr]uintptr) + for i := 0; i < length; i++ { + a[uintptr(i)] = uintptr(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*uintptr]uintptr) + for i := 0; i < length; i++ { + a[new(uintptr)] = uintptr(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k]++ + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan uintptr) + for i := 0; i < mods; i++ { + go func() { a <- uintptr(i) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(uintptr(0)) + for i := 0; i < mods; i++ { + a = a.(uintptr) + 1 + runtime.Gosched() + } + }, + }, + modifier{ + name: "string", + t: func() { + var s string + f := func(a string) string { return a } + for i := 0; i < mods; i++ { + s = str(i) + s = f(s) + } + }, + pointerT: func() { + a := func() *string { return new(string) }() + for i := 0; i < mods; i++ { + *a = str(i) + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]string{} + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j] = str(i) + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]string, length) + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j] = str(i) + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[string]string) + for i := 0; i < length; i++ { + a[string(i)] = str(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k] = str(i) + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*string]string) + for i := 0; i < length; i++ { + a[new(string)] = str(i) + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for k, _ := range a { + a[k] = str(i) + runtime.Gosched() + } + } + }, + chanT: func() { + a := make(chan string) + for i := 0; i < mods; i++ { + go func() { a <- str(i) }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(str(0)) + f := func(a string) string { return a } + for i := 0; i < mods; i++ { + a = str(i) + a = f(a.(string)) + runtime.Gosched() + } + }, + }, + modifier{ + name: "structT", + t: func() { + s := newStructT() + for i := 0; i < mods; i++ { + s.u8++ + s.u16++ + s.u32++ + s.u64++ + s.i8++ + s.i16++ + s.i32++ + s.i64++ + s.f32 *= 1.01 + s.f64 *= 1.01 + s.c64 = complex(real(s.c64)*1.01, imag(s.c64)*1.01) + s.c128 = complex(real(s.c128)*1.01, imag(s.c128)*1.01) + s.b++ + s.r++ + s.u++ + s.in++ + s.uip++ + s.s = str(i) + runtime.Gosched() + } + }, + pointerT: func() { + s := func() *structT { + t := newStructT() + return &t + }() + for i := 0; i < mods; i++ { + s.u8++ + s.u16++ + s.u32++ + s.u64++ + s.i8++ + s.i16++ + s.i32++ + s.i64++ + s.f32 *= 1.01 + s.f64 *= 1.01 + s.c64 = complex(real(s.c64)*1.01, imag(s.c64)*1.01) + s.c128 = complex(real(s.c128)*1.01, imag(s.c128)*1.01) + s.b++ + s.r++ + s.u++ + s.in++ + s.uip++ + s.s = str(i) + runtime.Gosched() + } + }, + arrayT: func() { + a := [length]structT{} + for i := 0; i < len(a); i++ { + a[i] = newStructT() + } + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j].u8++ + a[j].u16++ + a[j].u32++ + a[j].u64++ + a[j].i8++ + a[j].i16++ + a[j].i32++ + a[j].i64++ + a[j].f32 *= 1.01 + a[j].f64 *= 1.01 + a[j].c64 = complex(real(a[j].c64)*1.01, imag(a[j].c64)*1.01) + a[j].c128 = complex(real(a[j].c128)*1.01, imag(a[j].c128)*1.01) + a[j].b++ + a[j].r++ + a[j].u++ + a[j].in++ + a[j].uip++ + a[j].s = str(i) + runtime.Gosched() + } + } + }, + sliceT: func() { + a := make([]structT, length) + for i := 0; i < len(a); i++ { + a[i] = newStructT() + } + for i := 0; i < mods; i++ { + for j := 0; j < len(a); j++ { + a[j].u8++ + a[j].u16++ + a[j].u32++ + a[j].u64++ + a[j].i8++ + a[j].i16++ + a[j].i32++ + a[j].i64++ + a[j].f32 *= 1.01 + a[j].f64 *= 1.01 + a[j].c64 = complex(real(a[j].c64)*1.01, imag(a[j].c64)*1.01) + a[j].c128 = complex(real(a[j].c128)*1.01, imag(a[j].c128)*1.01) + a[j].b++ + a[j].r++ + a[j].u++ + a[j].in++ + a[j].uip++ + a[j].s = str(i) + runtime.Gosched() + } + } + }, + mapT: func() { + a := make(map[structT]structT) + for i := 0; i < length; i++ { + m := newStructT() + m.in = i + a[m] = newStructT() + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for j, _ := range a { + m := a[j] + m.u8++ + m.u16++ + m.u32++ + m.u64++ + m.i8++ + m.i16++ + m.i32++ + m.i64++ + m.f32 *= 1.01 + m.f64 *= 1.01 + m.c64 = complex(real(a[j].c64)*1.01, imag(a[j].c64)*1.01) + m.c128 = complex(real(a[j].c128)*1.01, imag(a[j].c128)*1.01) + m.b++ + m.r++ + m.u++ + m.in++ + m.uip++ + m.s = str(i) + a[j] = m + runtime.Gosched() + } + runtime.Gosched() + } + }, + mapPointerKeyT: func() { + a := make(map[*structT]structT) + f := func() *structT { + m := newStructT() + return &m + } + for i := 0; i < length; i++ { + m := f() + m.in = i + a[m] = newStructT() + runtime.Gosched() + } + for i := 0; i < mods; i++ { + for j, _ := range a { + m := a[j] + m.u8++ + m.u16++ + m.u32++ + m.u64++ + m.i8++ + m.i16++ + m.i32++ + m.i64++ + m.f32 *= 1.01 + m.f64 *= 1.01 + m.c64 = complex(real(a[j].c64)*1.01, imag(a[j].c64)*1.01) + m.c128 = complex(real(a[j].c128)*1.01, imag(a[j].c128)*1.01) + m.b++ + m.r++ + m.u++ + m.in++ + m.uip++ + m.s = str(i) + a[j] = m + runtime.Gosched() + } + runtime.Gosched() + } + }, + chanT: func() { + a := make(chan structT) + for i := 0; i < mods; i++ { + go func() { a <- newStructT() }() + <-a + runtime.Gosched() + } + }, + interfaceT: func() { + a := interface{}(newStructT()) + for i := 0; i < mods; i++ { + a = a.(structT) + runtime.Gosched() + } + }, + }, +} + +type structT struct { + u8 uint8 + u16 uint16 + u32 uint32 + u64 uint64 + i8 int8 + i16 int16 + i32 int32 + i64 int64 + f32 float32 + f64 float64 + c64 complex64 + c128 complex128 + b byte + r rune + u uint + in int + uip uintptr + s string +} + +func newStructT() structT { + return structT{ + f32: 1.01, + f64: 1.01, + c64: complex(float32(1.01), float32(1.01)), + c128: complex(float64(1.01), float64(1.01)), + } +} + +func str(in int) string { + switch in % 3 { + case 0: + return "Hello" + case 1: + return "world" + case 2: + return "!" + } + return "?" +} diff --git a/test/goprint.go b/test/goprint.go index 0648c77e7..57eeac53a 100644 --- a/test/goprint.go +++ b/test/goprint.go @@ -1,4 +1,4 @@ -// cmpout +// run // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/helloworld.go b/test/helloworld.go index 5025ec9bb..06851d13b 100644 --- a/test/helloworld.go +++ b/test/helloworld.go @@ -1,4 +1,4 @@ -// cmpout +// run // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/inline_big.go b/test/inline_big.go new file mode 100644 index 000000000..c4af15b4e --- /dev/null +++ b/test/inline_big.go @@ -0,0 +1,1029 @@ +// errorcheck -0 -m=2 + +// Copyright 2018 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 we restrict inlining into very large functions. +// See issue #26546. + +package foo + +func small(a []int) int { // ERROR "can inline small as:.*" "small a does not escape" + // Cost 16 body (need cost < 20). + // See cmd/compile/internal/gc/inl.go:inlineBigFunction* + return a[0] + a[1] + a[2] + a[3] +} +func medium(a []int) int { // ERROR "can inline medium as:.*" "medium a does not escape" + // Cost 32 body (need cost > 20 and cost < 80). + // See cmd/compile/internal/gc/inl.go:inlineBigFunction* + return a[0] + a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] +} + +func f(a []int) int { // ERROR "cannot inline f:.*" "f a does not escape" + // Add lots of nodes to f's body. We need >5000. + // See cmd/compile/internal/gc/inl.go:inlineBigFunction* + a[0] = 0 + a[1] = 0 + a[2] = 0 + a[3] = 0 + a[4] = 0 + a[5] = 0 + a[6] = 0 + a[7] = 0 + a[8] = 0 + a[9] = 0 + a[10] = 0 + a[11] = 0 + a[12] = 0 + a[13] = 0 + a[14] = 0 + a[15] = 0 + a[16] = 0 + a[17] = 0 + a[18] = 0 + a[19] = 0 + a[20] = 0 + a[21] = 0 + a[22] = 0 + a[23] = 0 + a[24] = 0 + a[25] = 0 + a[26] = 0 + a[27] = 0 + a[28] = 0 + a[29] = 0 + a[30] = 0 + a[31] = 0 + a[32] = 0 + a[33] = 0 + a[34] = 0 + a[35] = 0 + a[36] = 0 + a[37] = 0 + a[38] = 0 + a[39] = 0 + a[40] = 0 + a[41] = 0 + a[42] = 0 + a[43] = 0 + a[44] = 0 + a[45] = 0 + a[46] = 0 + a[47] = 0 + a[48] = 0 + a[49] = 0 + a[50] = 0 + a[51] = 0 + a[52] = 0 + a[53] = 0 + a[54] = 0 + a[55] = 0 + a[56] = 0 + a[57] = 0 + a[58] = 0 + a[59] = 0 + a[60] = 0 + a[61] = 0 + a[62] = 0 + a[63] = 0 + a[64] = 0 + a[65] = 0 + a[66] = 0 + a[67] = 0 + a[68] = 0 + a[69] = 0 + a[70] = 0 + a[71] = 0 + a[72] = 0 + a[73] = 0 + a[74] = 0 + a[75] = 0 + a[76] = 0 + a[77] = 0 + a[78] = 0 + a[79] = 0 + a[80] = 0 + a[81] = 0 + a[82] = 0 + a[83] = 0 + a[84] = 0 + a[85] = 0 + a[86] = 0 + a[87] = 0 + a[88] = 0 + a[89] = 0 + a[90] = 0 + a[91] = 0 + a[92] = 0 + a[93] = 0 + a[94] = 0 + a[95] = 0 + a[96] = 0 + a[97] = 0 + a[98] = 0 + a[99] = 0 + a[100] = 0 + a[101] = 0 + a[102] = 0 + a[103] = 0 + a[104] = 0 + a[105] = 0 + a[106] = 0 + a[107] = 0 + a[108] = 0 + a[109] = 0 + a[110] = 0 + a[111] = 0 + a[112] = 0 + a[113] = 0 + a[114] = 0 + a[115] = 0 + a[116] = 0 + a[117] = 0 + a[118] = 0 + a[119] = 0 + a[120] = 0 + a[121] = 0 + a[122] = 0 + a[123] = 0 + a[124] = 0 + a[125] = 0 + a[126] = 0 + a[127] = 0 + a[128] = 0 + a[129] = 0 + a[130] = 0 + a[131] = 0 + a[132] = 0 + a[133] = 0 + a[134] = 0 + a[135] = 0 + a[136] = 0 + a[137] = 0 + a[138] = 0 + a[139] = 0 + a[140] = 0 + a[141] = 0 + a[142] = 0 + a[143] = 0 + a[144] = 0 + a[145] = 0 + a[146] = 0 + a[147] = 0 + a[148] = 0 + a[149] = 0 + a[150] = 0 + a[151] = 0 + a[152] = 0 + a[153] = 0 + a[154] = 0 + a[155] = 0 + a[156] = 0 + a[157] = 0 + a[158] = 0 + a[159] = 0 + a[160] = 0 + a[161] = 0 + a[162] = 0 + a[163] = 0 + a[164] = 0 + a[165] = 0 + a[166] = 0 + a[167] = 0 + a[168] = 0 + a[169] = 0 + a[170] = 0 + a[171] = 0 + a[172] = 0 + a[173] = 0 + a[174] = 0 + a[175] = 0 + a[176] = 0 + a[177] = 0 + a[178] = 0 + a[179] = 0 + a[180] = 0 + a[181] = 0 + a[182] = 0 + a[183] = 0 + a[184] = 0 + a[185] = 0 + a[186] = 0 + a[187] = 0 + a[188] = 0 + a[189] = 0 + a[190] = 0 + a[191] = 0 + a[192] = 0 + a[193] = 0 + a[194] = 0 + a[195] = 0 + a[196] = 0 + a[197] = 0 + a[198] = 0 + a[199] = 0 + a[200] = 0 + a[201] = 0 + a[202] = 0 + a[203] = 0 + a[204] = 0 + a[205] = 0 + a[206] = 0 + a[207] = 0 + a[208] = 0 + a[209] = 0 + a[210] = 0 + a[211] = 0 + a[212] = 0 + a[213] = 0 + a[214] = 0 + a[215] = 0 + a[216] = 0 + a[217] = 0 + a[218] = 0 + a[219] = 0 + a[220] = 0 + a[221] = 0 + a[222] = 0 + a[223] = 0 + a[224] = 0 + a[225] = 0 + a[226] = 0 + a[227] = 0 + a[228] = 0 + a[229] = 0 + a[230] = 0 + a[231] = 0 + a[232] = 0 + a[233] = 0 + a[234] = 0 + a[235] = 0 + a[236] = 0 + a[237] = 0 + a[238] = 0 + a[239] = 0 + a[240] = 0 + a[241] = 0 + a[242] = 0 + a[243] = 0 + a[244] = 0 + a[245] = 0 + a[246] = 0 + a[247] = 0 + a[248] = 0 + a[249] = 0 + a[250] = 0 + a[251] = 0 + a[252] = 0 + a[253] = 0 + a[254] = 0 + a[255] = 0 + a[256] = 0 + a[257] = 0 + a[258] = 0 + a[259] = 0 + a[260] = 0 + a[261] = 0 + a[262] = 0 + a[263] = 0 + a[264] = 0 + a[265] = 0 + a[266] = 0 + a[267] = 0 + a[268] = 0 + a[269] = 0 + a[270] = 0 + a[271] = 0 + a[272] = 0 + a[273] = 0 + a[274] = 0 + a[275] = 0 + a[276] = 0 + a[277] = 0 + a[278] = 0 + a[279] = 0 + a[280] = 0 + a[281] = 0 + a[282] = 0 + a[283] = 0 + a[284] = 0 + a[285] = 0 + a[286] = 0 + a[287] = 0 + a[288] = 0 + a[289] = 0 + a[290] = 0 + a[291] = 0 + a[292] = 0 + a[293] = 0 + a[294] = 0 + a[295] = 0 + a[296] = 0 + a[297] = 0 + a[298] = 0 + a[299] = 0 + a[300] = 0 + a[301] = 0 + a[302] = 0 + a[303] = 0 + a[304] = 0 + a[305] = 0 + a[306] = 0 + a[307] = 0 + a[308] = 0 + a[309] = 0 + a[310] = 0 + a[311] = 0 + a[312] = 0 + a[313] = 0 + a[314] = 0 + a[315] = 0 + a[316] = 0 + a[317] = 0 + a[318] = 0 + a[319] = 0 + a[320] = 0 + a[321] = 0 + a[322] = 0 + a[323] = 0 + a[324] = 0 + a[325] = 0 + a[326] = 0 + a[327] = 0 + a[328] = 0 + a[329] = 0 + a[330] = 0 + a[331] = 0 + a[332] = 0 + a[333] = 0 + a[334] = 0 + a[335] = 0 + a[336] = 0 + a[337] = 0 + a[338] = 0 + a[339] = 0 + a[340] = 0 + a[341] = 0 + a[342] = 0 + a[343] = 0 + a[344] = 0 + a[345] = 0 + a[346] = 0 + a[347] = 0 + a[348] = 0 + a[349] = 0 + a[350] = 0 + a[351] = 0 + a[352] = 0 + a[353] = 0 + a[354] = 0 + a[355] = 0 + a[356] = 0 + a[357] = 0 + a[358] = 0 + a[359] = 0 + a[360] = 0 + a[361] = 0 + a[362] = 0 + a[363] = 0 + a[364] = 0 + a[365] = 0 + a[366] = 0 + a[367] = 0 + a[368] = 0 + a[369] = 0 + a[370] = 0 + a[371] = 0 + a[372] = 0 + a[373] = 0 + a[374] = 0 + a[375] = 0 + a[376] = 0 + a[377] = 0 + a[378] = 0 + a[379] = 0 + a[380] = 0 + a[381] = 0 + a[382] = 0 + a[383] = 0 + a[384] = 0 + a[385] = 0 + a[386] = 0 + a[387] = 0 + a[388] = 0 + a[389] = 0 + a[390] = 0 + a[391] = 0 + a[392] = 0 + a[393] = 0 + a[394] = 0 + a[395] = 0 + a[396] = 0 + a[397] = 0 + a[398] = 0 + a[399] = 0 + a[400] = 0 + a[401] = 0 + a[402] = 0 + a[403] = 0 + a[404] = 0 + a[405] = 0 + a[406] = 0 + a[407] = 0 + a[408] = 0 + a[409] = 0 + a[410] = 0 + a[411] = 0 + a[412] = 0 + a[413] = 0 + a[414] = 0 + a[415] = 0 + a[416] = 0 + a[417] = 0 + a[418] = 0 + a[419] = 0 + a[420] = 0 + a[421] = 0 + a[422] = 0 + a[423] = 0 + a[424] = 0 + a[425] = 0 + a[426] = 0 + a[427] = 0 + a[428] = 0 + a[429] = 0 + a[430] = 0 + a[431] = 0 + a[432] = 0 + a[433] = 0 + a[434] = 0 + a[435] = 0 + a[436] = 0 + a[437] = 0 + a[438] = 0 + a[439] = 0 + a[440] = 0 + a[441] = 0 + a[442] = 0 + a[443] = 0 + a[444] = 0 + a[445] = 0 + a[446] = 0 + a[447] = 0 + a[448] = 0 + a[449] = 0 + a[450] = 0 + a[451] = 0 + a[452] = 0 + a[453] = 0 + a[454] = 0 + a[455] = 0 + a[456] = 0 + a[457] = 0 + a[458] = 0 + a[459] = 0 + a[460] = 0 + a[461] = 0 + a[462] = 0 + a[463] = 0 + a[464] = 0 + a[465] = 0 + a[466] = 0 + a[467] = 0 + a[468] = 0 + a[469] = 0 + a[470] = 0 + a[471] = 0 + a[472] = 0 + a[473] = 0 + a[474] = 0 + a[475] = 0 + a[476] = 0 + a[477] = 0 + a[478] = 0 + a[479] = 0 + a[480] = 0 + a[481] = 0 + a[482] = 0 + a[483] = 0 + a[484] = 0 + a[485] = 0 + a[486] = 0 + a[487] = 0 + a[488] = 0 + a[489] = 0 + a[490] = 0 + a[491] = 0 + a[492] = 0 + a[493] = 0 + a[494] = 0 + a[495] = 0 + a[496] = 0 + a[497] = 0 + a[498] = 0 + a[499] = 0 + a[500] = 0 + a[501] = 0 + a[502] = 0 + a[503] = 0 + a[504] = 0 + a[505] = 0 + a[506] = 0 + a[507] = 0 + a[508] = 0 + a[509] = 0 + a[510] = 0 + a[511] = 0 + a[512] = 0 + a[513] = 0 + a[514] = 0 + a[515] = 0 + a[516] = 0 + a[517] = 0 + a[518] = 0 + a[519] = 0 + a[520] = 0 + a[521] = 0 + a[522] = 0 + a[523] = 0 + a[524] = 0 + a[525] = 0 + a[526] = 0 + a[527] = 0 + a[528] = 0 + a[529] = 0 + a[530] = 0 + a[531] = 0 + a[532] = 0 + a[533] = 0 + a[534] = 0 + a[535] = 0 + a[536] = 0 + a[537] = 0 + a[538] = 0 + a[539] = 0 + a[540] = 0 + a[541] = 0 + a[542] = 0 + a[543] = 0 + a[544] = 0 + a[545] = 0 + a[546] = 0 + a[547] = 0 + a[548] = 0 + a[549] = 0 + a[550] = 0 + a[551] = 0 + a[552] = 0 + a[553] = 0 + a[554] = 0 + a[555] = 0 + a[556] = 0 + a[557] = 0 + a[558] = 0 + a[559] = 0 + a[560] = 0 + a[561] = 0 + a[562] = 0 + a[563] = 0 + a[564] = 0 + a[565] = 0 + a[566] = 0 + a[567] = 0 + a[568] = 0 + a[569] = 0 + a[570] = 0 + a[571] = 0 + a[572] = 0 + a[573] = 0 + a[574] = 0 + a[575] = 0 + a[576] = 0 + a[577] = 0 + a[578] = 0 + a[579] = 0 + a[580] = 0 + a[581] = 0 + a[582] = 0 + a[583] = 0 + a[584] = 0 + a[585] = 0 + a[586] = 0 + a[587] = 0 + a[588] = 0 + a[589] = 0 + a[590] = 0 + a[591] = 0 + a[592] = 0 + a[593] = 0 + a[594] = 0 + a[595] = 0 + a[596] = 0 + a[597] = 0 + a[598] = 0 + a[599] = 0 + a[600] = 0 + a[601] = 0 + a[602] = 0 + a[603] = 0 + a[604] = 0 + a[605] = 0 + a[606] = 0 + a[607] = 0 + a[608] = 0 + a[609] = 0 + a[610] = 0 + a[611] = 0 + a[612] = 0 + a[613] = 0 + a[614] = 0 + a[615] = 0 + a[616] = 0 + a[617] = 0 + a[618] = 0 + a[619] = 0 + a[620] = 0 + a[621] = 0 + a[622] = 0 + a[623] = 0 + a[624] = 0 + a[625] = 0 + a[626] = 0 + a[627] = 0 + a[628] = 0 + a[629] = 0 + a[630] = 0 + a[631] = 0 + a[632] = 0 + a[633] = 0 + a[634] = 0 + a[635] = 0 + a[636] = 0 + a[637] = 0 + a[638] = 0 + a[639] = 0 + a[640] = 0 + a[641] = 0 + a[642] = 0 + a[643] = 0 + a[644] = 0 + a[645] = 0 + a[646] = 0 + a[647] = 0 + a[648] = 0 + a[649] = 0 + a[650] = 0 + a[651] = 0 + a[652] = 0 + a[653] = 0 + a[654] = 0 + a[655] = 0 + a[656] = 0 + a[657] = 0 + a[658] = 0 + a[659] = 0 + a[660] = 0 + a[661] = 0 + a[662] = 0 + a[663] = 0 + a[664] = 0 + a[665] = 0 + a[666] = 0 + a[667] = 0 + a[668] = 0 + a[669] = 0 + a[670] = 0 + a[671] = 0 + a[672] = 0 + a[673] = 0 + a[674] = 0 + a[675] = 0 + a[676] = 0 + a[677] = 0 + a[678] = 0 + a[679] = 0 + a[680] = 0 + a[681] = 0 + a[682] = 0 + a[683] = 0 + a[684] = 0 + a[685] = 0 + a[686] = 0 + a[687] = 0 + a[688] = 0 + a[689] = 0 + a[690] = 0 + a[691] = 0 + a[692] = 0 + a[693] = 0 + a[694] = 0 + a[695] = 0 + a[696] = 0 + a[697] = 0 + a[698] = 0 + a[699] = 0 + a[700] = 0 + a[701] = 0 + a[702] = 0 + a[703] = 0 + a[704] = 0 + a[705] = 0 + a[706] = 0 + a[707] = 0 + a[708] = 0 + a[709] = 0 + a[710] = 0 + a[711] = 0 + a[712] = 0 + a[713] = 0 + a[714] = 0 + a[715] = 0 + a[716] = 0 + a[717] = 0 + a[718] = 0 + a[719] = 0 + a[720] = 0 + a[721] = 0 + a[722] = 0 + a[723] = 0 + a[724] = 0 + a[725] = 0 + a[726] = 0 + a[727] = 0 + a[728] = 0 + a[729] = 0 + a[730] = 0 + a[731] = 0 + a[732] = 0 + a[733] = 0 + a[734] = 0 + a[735] = 0 + a[736] = 0 + a[737] = 0 + a[738] = 0 + a[739] = 0 + a[740] = 0 + a[741] = 0 + a[742] = 0 + a[743] = 0 + a[744] = 0 + a[745] = 0 + a[746] = 0 + a[747] = 0 + a[748] = 0 + a[749] = 0 + a[750] = 0 + a[751] = 0 + a[752] = 0 + a[753] = 0 + a[754] = 0 + a[755] = 0 + a[756] = 0 + a[757] = 0 + a[758] = 0 + a[759] = 0 + a[760] = 0 + a[761] = 0 + a[762] = 0 + a[763] = 0 + a[764] = 0 + a[765] = 0 + a[766] = 0 + a[767] = 0 + a[768] = 0 + a[769] = 0 + a[770] = 0 + a[771] = 0 + a[772] = 0 + a[773] = 0 + a[774] = 0 + a[775] = 0 + a[776] = 0 + a[777] = 0 + a[778] = 0 + a[779] = 0 + a[780] = 0 + a[781] = 0 + a[782] = 0 + a[783] = 0 + a[784] = 0 + a[785] = 0 + a[786] = 0 + a[787] = 0 + a[788] = 0 + a[789] = 0 + a[790] = 0 + a[791] = 0 + a[792] = 0 + a[793] = 0 + a[794] = 0 + a[795] = 0 + a[796] = 0 + a[797] = 0 + a[798] = 0 + a[799] = 0 + a[800] = 0 + a[801] = 0 + a[802] = 0 + a[803] = 0 + a[804] = 0 + a[805] = 0 + a[806] = 0 + a[807] = 0 + a[808] = 0 + a[809] = 0 + a[810] = 0 + a[811] = 0 + a[812] = 0 + a[813] = 0 + a[814] = 0 + a[815] = 0 + a[816] = 0 + a[817] = 0 + a[818] = 0 + a[819] = 0 + a[820] = 0 + a[821] = 0 + a[822] = 0 + a[823] = 0 + a[824] = 0 + a[825] = 0 + a[826] = 0 + a[827] = 0 + a[828] = 0 + a[829] = 0 + a[830] = 0 + a[831] = 0 + a[832] = 0 + a[833] = 0 + a[834] = 0 + a[835] = 0 + a[836] = 0 + a[837] = 0 + a[838] = 0 + a[839] = 0 + a[840] = 0 + a[841] = 0 + a[842] = 0 + a[843] = 0 + a[844] = 0 + a[845] = 0 + a[846] = 0 + a[847] = 0 + a[848] = 0 + a[849] = 0 + a[850] = 0 + a[851] = 0 + a[852] = 0 + a[853] = 0 + a[854] = 0 + a[855] = 0 + a[856] = 0 + a[857] = 0 + a[858] = 0 + a[859] = 0 + a[860] = 0 + a[861] = 0 + a[862] = 0 + a[863] = 0 + a[864] = 0 + a[865] = 0 + a[866] = 0 + a[867] = 0 + a[868] = 0 + a[869] = 0 + a[870] = 0 + a[871] = 0 + a[872] = 0 + a[873] = 0 + a[874] = 0 + a[875] = 0 + a[876] = 0 + a[877] = 0 + a[878] = 0 + a[879] = 0 + a[880] = 0 + a[881] = 0 + a[882] = 0 + a[883] = 0 + a[884] = 0 + a[885] = 0 + a[886] = 0 + a[887] = 0 + a[888] = 0 + a[889] = 0 + a[890] = 0 + a[891] = 0 + a[892] = 0 + a[893] = 0 + a[894] = 0 + a[895] = 0 + a[896] = 0 + a[897] = 0 + a[898] = 0 + a[899] = 0 + a[900] = 0 + a[901] = 0 + a[902] = 0 + a[903] = 0 + a[904] = 0 + a[905] = 0 + a[906] = 0 + a[907] = 0 + a[908] = 0 + a[909] = 0 + a[910] = 0 + a[911] = 0 + a[912] = 0 + a[913] = 0 + a[914] = 0 + a[915] = 0 + a[916] = 0 + a[917] = 0 + a[918] = 0 + a[919] = 0 + a[920] = 0 + a[921] = 0 + a[922] = 0 + a[923] = 0 + a[924] = 0 + a[925] = 0 + a[926] = 0 + a[927] = 0 + a[928] = 0 + a[929] = 0 + a[930] = 0 + a[931] = 0 + a[932] = 0 + a[933] = 0 + a[934] = 0 + a[935] = 0 + a[936] = 0 + a[937] = 0 + a[938] = 0 + a[939] = 0 + a[940] = 0 + a[941] = 0 + a[942] = 0 + a[943] = 0 + a[944] = 0 + a[945] = 0 + a[946] = 0 + a[947] = 0 + a[948] = 0 + a[949] = 0 + a[950] = 0 + a[951] = 0 + a[952] = 0 + a[953] = 0 + a[954] = 0 + a[955] = 0 + a[956] = 0 + a[957] = 0 + a[958] = 0 + a[959] = 0 + a[960] = 0 + a[961] = 0 + a[962] = 0 + a[963] = 0 + a[964] = 0 + a[965] = 0 + a[966] = 0 + a[967] = 0 + a[968] = 0 + a[969] = 0 + a[970] = 0 + a[971] = 0 + a[972] = 0 + a[973] = 0 + a[974] = 0 + a[975] = 0 + a[976] = 0 + a[977] = 0 + a[978] = 0 + a[979] = 0 + a[980] = 0 + a[981] = 0 + a[982] = 0 + a[983] = 0 + a[984] = 0 + a[985] = 0 + a[986] = 0 + a[987] = 0 + a[988] = 0 + a[989] = 0 + a[990] = 0 + a[991] = 0 + a[992] = 0 + a[993] = 0 + a[994] = 0 + a[995] = 0 + a[996] = 0 + a[997] = 0 + a[998] = 0 + a[999] = 0 + x := small(a) // ERROR "inlining call to small .*" + y := medium(a) // The crux of this test: medium is not inlined. + return x + y +} diff --git a/test/inline_variadic.go b/test/inline_variadic.go index 6466c2b09..fcc1cff1e 100644 --- a/test/inline_variadic.go +++ b/test/inline_variadic.go @@ -1,10 +1,10 @@ -// errorcheck -0 -m -l=3 +// errorcheck -0 -m // Copyright 2016 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 more aggressive inlining (-l=3 allows variadic functions) +// Test inlining of variadic functions. // See issue #18116. package foo diff --git a/test/ken/cplx0.go b/test/ken/cplx0.go index 665e52a5f..5d78dc014 100644 --- a/test/ken/cplx0.go +++ b/test/ken/cplx0.go @@ -1,4 +1,4 @@ -// cmpout +// run // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/ken/string.go b/test/ken/string.go index 6df8dc4dd..7bb3cabbc 100644 --- a/test/ken/string.go +++ b/test/ken/string.go @@ -1,4 +1,4 @@ -// cmpout +// run // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/linkmain_run.go b/test/linkmain_run.go index 55de481a8..68d53e8ca 100644 --- a/test/linkmain_run.go +++ b/test/linkmain_run.go @@ -1,4 +1,4 @@ -// +build !nacl +// +build !nacl,!js // run // Copyright 2014 The Go Authors. All rights reserved. diff --git a/test/linkobj.go b/test/linkobj.go index 8a86aa872..2902d23f4 100644 --- a/test/linkobj.go +++ b/test/linkobj.go @@ -1,4 +1,4 @@ -// +build !nacl +// +build !nacl,!js // run // Copyright 2016 The Go Authors. All rights reserved. diff --git a/test/linkx_run.go b/test/linkx_run.go index cc249c9cf..ca9d31612 100644 --- a/test/linkx_run.go +++ b/test/linkx_run.go @@ -1,4 +1,4 @@ -// +build !nacl +// +build !nacl,!js // run // Copyright 2014 The Go Authors. All rights reserved. diff --git a/test/live.go b/test/live.go index e54336ead..18611f511 100644 --- a/test/live.go +++ b/test/live.go @@ -141,7 +141,7 @@ var i9 interface{} func f9() bool { g8() x := i9 - y := interface{}(str()) // ERROR "live at call to convT2Estring: .autotmp_[0-9]+ x.data x.type$" "live at call to str: x.data x.type$" + y := interface{}(str()) // ERROR "live at call to convT2Estring: .autotmp_[0-9]+ x.data$" "live at call to str: x.data$" i9 = y // make y escape so the line above has to call convT2E return x != y } @@ -163,10 +163,10 @@ var b bool // this used to have a spurious "live at entry to f11a: ~r0" func f11a() *int { - select { // ERROR "live at call to newselect: .autotmp_[0-9]+$" "live at call to selectgo: .autotmp_[0-9]+$" - case <-c: // ERROR "live at call to selectrecv: .autotmp_[0-9]+$" + select { // ERROR "live at call to selectgo: .autotmp_[0-9]+$" + case <-c: return nil - case <-c: // ERROR "live at call to selectrecv: .autotmp_[0-9]+$" + case <-c: return nil } } @@ -178,10 +178,10 @@ func f11b() *int { // get to the bottom of the function. // This used to have a spurious "live at call to printint: p". printint(1) // nothing live here! - select { // ERROR "live at call to newselect: .autotmp_[0-9]+$" "live at call to selectgo: .autotmp_[0-9]+$" - case <-c: // ERROR "live at call to selectrecv: .autotmp_[0-9]+$" + select { // ERROR "live at call to selectgo: .autotmp_[0-9]+$" + case <-c: return nil - case <-c: // ERROR "live at call to selectrecv: .autotmp_[0-9]+$" + case <-c: return nil } } @@ -198,9 +198,9 @@ func f11c() *int { // Unlike previous, the cases in this select fall through, // so we can get to the println, so p is not dead. printint(1) // ERROR "live at call to printint: p$" - select { // ERROR "live at call to newselect: .autotmp_[0-9]+ p$" "live at call to selectgo: .autotmp_[0-9]+ p$" - case <-c: // ERROR "live at call to selectrecv: .autotmp_[0-9]+ p$" - case <-c: // ERROR "live at call to selectrecv: .autotmp_[0-9]+ p$" + select { // ERROR "live at call to selectgo: .autotmp_[0-9]+ p$" + case <-c: + case <-c: } } println(*p) @@ -553,7 +553,7 @@ func f34() { } func f35() { - if m33[byteptr()] == 0 && m33[byteptr()] == 0 { // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$" + if m33[byteptr()] == 0 && m33[byteptr()] == 0 { // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$" "f35: .autotmp_[0-9]+ \(type interface \{\}\) is ambiguously live$" printnl() return } @@ -561,7 +561,7 @@ func f35() { } func f36() { - if m33[byteptr()] == 0 || m33[byteptr()] == 0 { // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$" + if m33[byteptr()] == 0 || m33[byteptr()] == 0 { // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$" "f36: .autotmp_[0-9]+ \(type interface \{\}\) is ambiguously live$" printnl() return } @@ -569,7 +569,7 @@ func f36() { } func f37() { - if (m33[byteptr()] == 0 || m33[byteptr()] == 0) && m33[byteptr()] == 0 { // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$" + if (m33[byteptr()] == 0 || m33[byteptr()] == 0) && m33[byteptr()] == 0 { // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$" "f37: .autotmp_[0-9]+ \(type interface \{\}\) is ambiguously live$" printnl() return } @@ -589,14 +589,14 @@ func f38(b bool) { // we care that the println lines have no live variables // and therefore no output. if b { - select { // ERROR "live at call to newselect:( .autotmp_[0-9]+)+$" "live at call to selectgo:( .autotmp_[0-9]+)+$" - case <-fc38(): // ERROR "live at call to selectrecv:( .autotmp_[0-9]+)+$" + select { // ERROR "live at call to selectgo:( .autotmp_[0-9]+)+$" + case <-fc38(): printnl() - case fc38() <- *fi38(1): // ERROR "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$" "live at call to selectsend:( .autotmp_[0-9]+)+$" + case fc38() <- *fi38(1): // ERROR "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$" printnl() - case *fi38(2) = <-fc38(): // ERROR "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$" "live at call to selectrecv:( .autotmp_[0-9]+)+$" + case *fi38(2) = <-fc38(): // ERROR "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$" printnl() - case *fi38(3), *fb38() = <-fc38(): // ERROR "live at call to fb38:( .autotmp_[0-9]+)+$" "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$" "live at call to selectrecv:( .autotmp_[0-9]+)+$" + case *fi38(3), *fb38() = <-fc38(): // ERROR "live at call to fb38:( .autotmp_[0-9]+)+$" "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$" printnl() } printnl() diff --git a/test/live_syscall.go b/test/live_syscall.go index 6d954653c..65a161c02 100644 --- a/test/live_syscall.go +++ b/test/live_syscall.go @@ -1,6 +1,6 @@ // errorcheck -0 -m -live -// +build !windows +// +build !windows,!js // Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/locklinear.go b/test/locklinear.go index 161912b65..54e40a543 100644 --- a/test/locklinear.go +++ b/test/locklinear.go @@ -125,6 +125,12 @@ func main() { } }) + if runtime.GOARCH == "arm" && os.Getenv("GOARM") == "5" { + // lockmany reliably fails on the linux-arm-arm5spacemonkey + // builder. See https://golang.org/issue/24221. + return + } + checkLinear("lockmany", 1000, func(n int) { locks := make([]sync.RWMutex, n*offset+1) diff --git a/test/loopbce.go b/test/loopbce.go index 857cf2442..b4bf79749 100644 --- a/test/loopbce.go +++ b/test/loopbce.go @@ -1,20 +1,20 @@ // +build amd64 -// errorcheck -0 -d=ssa/loopbce/debug=3 +// errorcheck -0 -d=ssa/prove/debug=1 package main func f0a(a []int) int { x := 0 - for i := range a { // ERROR "Induction variable with minimum 0 and increment 1$" - x += a[i] // ERROR "Found redundant IsInBounds$" + for i := range a { // ERROR "Induction variable: limits \[0,\?\), increment 1$" + x += a[i] // ERROR "Proved IsInBounds$" } return x } func f0b(a []int) int { x := 0 - for i := range a { // ERROR "Induction variable with minimum 0 and increment 1$" - b := a[i:] // ERROR "Found redundant IsSliceInBounds$" + for i := range a { // ERROR "Induction variable: limits \[0,\?\), increment 1$" + b := a[i:] // ERROR "Proved IsSliceInBounds$" x += b[0] } return x @@ -22,8 +22,8 @@ func f0b(a []int) int { func f0c(a []int) int { x := 0 - for i := range a { // ERROR "Induction variable with minimum 0 and increment 1$" - b := a[:i+1] // ERROR "Found redundant IsSliceInBounds \(len promoted to cap\)$" + for i := range a { // ERROR "Induction variable: limits \[0,\?\), increment 1$" + b := a[:i+1] // ERROR "Proved IsSliceInBounds$" x += b[0] } return x @@ -31,7 +31,7 @@ func f0c(a []int) int { func f1(a []int) int { x := 0 - for _, i := range a { // ERROR "Induction variable with minimum 0 and increment 1" + for _, i := range a { // ERROR "Induction variable: limits \[0,\?\), increment 1$" x += i } return x @@ -39,47 +39,79 @@ func f1(a []int) int { func f2(a []int) int { x := 0 - for i := 1; i < len(a); i++ { // ERROR "Induction variable with minimum 1 and increment 1$" - x += a[i] // ERROR "Found redundant IsInBounds$" + for i := 1; i < len(a); i++ { // ERROR "Induction variable: limits \[1,\?\), increment 1$" + x += a[i] // ERROR "Proved IsInBounds$" } return x } func f4(a [10]int) int { x := 0 - for i := 0; i < len(a); i += 2 { // ERROR "Induction variable with minimum 0 and increment 2$" - x += a[i] // ERROR "Found redundant IsInBounds$" + for i := 0; i < len(a); i += 2 { // ERROR "Induction variable: limits \[0,10\), increment 2$" + x += a[i] // ERROR "Proved IsInBounds$" } return x } func f5(a [10]int) int { x := 0 - for i := -10; i < len(a); i += 2 { // ERROR "Induction variable with minimum -10 and increment 2$" + for i := -10; i < len(a); i += 2 { // ERROR "Induction variable: limits \[-10,10\), increment 2$" x += a[i] } return x } func f6(a []int) { - for i := range a { // ERROR "Induction variable with minimum 0 and increment 1$" - b := a[0:i] // ERROR "Found redundant IsSliceInBounds \(len promoted to cap\)$" + for i := range a { // ERROR "Induction variable: limits \[0,\?\), increment 1$" + b := a[0:i] // ERROR "Proved IsSliceInBounds$" f6(b) } } func g0a(a string) int { x := 0 - for i := 0; i < len(a); i++ { // ERROR "Induction variable with minimum 0 and increment 1$" - x += int(a[i]) // ERROR "Found redundant IsInBounds$" + for i := 0; i < len(a); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" + x += int(a[i]) // ERROR "Proved IsInBounds$" } return x } func g0b(a string) int { x := 0 - for i := 0; len(a) > i; i++ { // ERROR "Induction variable with minimum 0 and increment 1$" - x += int(a[i]) // ERROR "Found redundant IsInBounds$" + for i := 0; len(a) > i; i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" + x += int(a[i]) // ERROR "Proved IsInBounds$" + } + return x +} + +func g0c(a string) int { + x := 0 + for i := len(a); i > 0; i-- { // ERROR "Induction variable: limits \(0,\?\], increment 1$" + x += int(a[i-1]) // ERROR "Proved IsInBounds$" + } + return x +} + +func g0d(a string) int { + x := 0 + for i := len(a); 0 < i; i-- { // ERROR "Induction variable: limits \(0,\?\], increment 1$" + x += int(a[i-1]) // ERROR "Proved IsInBounds$" + } + return x +} + +func g0e(a string) int { + x := 0 + for i := len(a) - 1; i >= 0; i-- { // ERROR "Induction variable: limits \[0,\?\], increment 1$" + x += int(a[i]) // ERROR "Proved IsInBounds$" + } + return x +} + +func g0f(a string) int { + x := 0 + for i := len(a) - 1; 0 <= i; i-- { // ERROR "Induction variable: limits \[0,\?\], increment 1$" + x += int(a[i]) // ERROR "Proved IsInBounds$" } return x } @@ -87,8 +119,8 @@ func g0b(a string) int { func g1() int { a := "evenlength" x := 0 - for i := 0; i < len(a); i += 2 { // ERROR "Induction variable with minimum 0 and increment 2$" - x += int(a[i]) // ERROR "Found redundant IsInBounds$" + for i := 0; i < len(a); i += 2 { // ERROR "Induction variable: limits \[0,10\), increment 2$" + x += int(a[i]) // ERROR "Proved IsInBounds$" } return x } @@ -96,9 +128,9 @@ func g1() int { func g2() int { a := "evenlength" x := 0 - for i := 0; i < len(a); i += 2 { // ERROR "Induction variable with minimum 0 and increment 2$" + for i := 0; i < len(a); i += 2 { // ERROR "Induction variable: limits \[0,10\), increment 2$" j := i - if a[i] == 'e' { // ERROR "Found redundant IsInBounds$" + if a[i] == 'e' { // ERROR "Proved IsInBounds$" j = j + 1 } x += int(a[j]) @@ -108,97 +140,119 @@ func g2() int { func g3a() { a := "this string has length 25" - for i := 0; i < len(a); i += 5 { // ERROR "Induction variable with minimum 0 and increment 5$" - useString(a[i:]) // ERROR "Found redundant IsSliceInBounds$" + for i := 0; i < len(a); i += 5 { // ERROR "Induction variable: limits \[0,25\), increment 5$" + useString(a[i:]) // ERROR "Proved IsSliceInBounds$" useString(a[:i+3]) } } func g3b(a string) { - for i := 0; i < len(a); i++ { // ERROR "Induction variable with minimum 0 and increment 1$" - useString(a[i+1:]) // ERROR "Found redundant IsSliceInBounds$" + for i := 0; i < len(a); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" + useString(a[i+1:]) // ERROR "Proved IsSliceInBounds$" } } func g3c(a string) { - for i := 0; i < len(a); i++ { // ERROR "Induction variable with minimum 0 and increment 1$" - useString(a[:i+1]) // ERROR "Found redundant IsSliceInBounds$" + for i := 0; i < len(a); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" + useString(a[:i+1]) // ERROR "Proved IsSliceInBounds$" } } func h1(a []byte) { c := a[:128] - for i := range c { // ERROR "Induction variable with minimum 0 and increment 1$" - c[i] = byte(i) // ERROR "Found redundant IsInBounds$" + for i := range c { // ERROR "Induction variable: limits \[0,128\), increment 1$" + c[i] = byte(i) // ERROR "Proved IsInBounds$" } } func h2(a []byte) { - for i := range a[:128] { // ERROR "Induction variable with minimum 0 and increment 1$" + for i := range a[:128] { // ERROR "Induction variable: limits \[0,128\), increment 1$" a[i] = byte(i) } } func k0(a [100]int) [100]int { - for i := 10; i < 90; i++ { // ERROR "Induction variable with minimum 10 and increment 1$" + for i := 10; i < 90; i++ { // ERROR "Induction variable: limits \[10,90\), increment 1$" a[i-11] = i - a[i-10] = i // ERROR "Found redundant \(IsInBounds ind 100\), ind < 80$" - a[i-5] = i // ERROR "Found redundant \(IsInBounds ind 100\), ind < 85$" - a[i] = i // ERROR "Found redundant \(IsInBounds ind 100\), ind < 90$" - a[i+5] = i // ERROR "Found redundant \(IsInBounds ind 100\), ind < 95$" - a[i+10] = i // ERROR "Found redundant \(IsInBounds ind 100\), ind < 100$" + a[i-10] = i // ERROR "Proved IsInBounds$" + a[i-5] = i // ERROR "Proved IsInBounds$" + a[i] = i // ERROR "Proved IsInBounds$" + a[i+5] = i // ERROR "Proved IsInBounds$" + a[i+10] = i // ERROR "Proved IsInBounds$" a[i+11] = i } return a } func k1(a [100]int) [100]int { - for i := 10; i < 90; i++ { // ERROR "Induction variable with minimum 10 and increment 1$" + for i := 10; i < 90; i++ { // ERROR "Induction variable: limits \[10,90\), increment 1$" useSlice(a[:i-11]) - useSlice(a[:i-10]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 80$" - useSlice(a[:i-5]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 85$" - useSlice(a[:i]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 90$" - useSlice(a[:i+5]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 95$" - useSlice(a[:i+10]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 100$" - useSlice(a[:i+11]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 101$" + useSlice(a[:i-10]) // ERROR "Proved IsSliceInBounds$" + useSlice(a[:i-5]) // ERROR "Proved IsSliceInBounds$" + useSlice(a[:i]) // ERROR "Proved IsSliceInBounds$" + useSlice(a[:i+5]) // ERROR "Proved IsSliceInBounds$" + useSlice(a[:i+10]) // ERROR "Proved IsSliceInBounds$" + useSlice(a[:i+11]) // ERROR "Proved IsSliceInBounds$" + useSlice(a[:i+12]) } return a } func k2(a [100]int) [100]int { - for i := 10; i < 90; i++ { // ERROR "Induction variable with minimum 10 and increment 1$" + for i := 10; i < 90; i++ { // ERROR "Induction variable: limits \[10,90\), increment 1$" useSlice(a[i-11:]) - useSlice(a[i-10:]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 80$" - useSlice(a[i-5:]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 85$" - useSlice(a[i:]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 90$" - useSlice(a[i+5:]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 95$" - useSlice(a[i+10:]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 100$" - useSlice(a[i+11:]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 101$" + useSlice(a[i-10:]) // ERROR "Proved IsSliceInBounds$" + useSlice(a[i-5:]) // ERROR "Proved IsSliceInBounds$" + useSlice(a[i:]) // ERROR "Proved IsSliceInBounds$" + useSlice(a[i+5:]) // ERROR "Proved IsSliceInBounds$" + useSlice(a[i+10:]) // ERROR "Proved IsSliceInBounds$" + useSlice(a[i+11:]) // ERROR "Proved IsSliceInBounds$" + useSlice(a[i+12:]) } return a } func k3(a [100]int) [100]int { - for i := -10; i < 90; i++ { // ERROR "Induction variable with minimum -10 and increment 1$" - a[i+10] = i // ERROR "Found redundant \(IsInBounds ind 100\), ind < 100$" + for i := -10; i < 90; i++ { // ERROR "Induction variable: limits \[-10,90\), increment 1$" + a[i+9] = i + a[i+10] = i // ERROR "Proved IsInBounds$" + a[i+11] = i + } + return a +} + +func k3neg(a [100]int) [100]int { + for i := 89; i > -11; i-- { // ERROR "Induction variable: limits \(-11,89\], increment 1$" + a[i+9] = i + a[i+10] = i // ERROR "Proved IsInBounds$" + a[i+11] = i + } + return a +} + +func k3neg2(a [100]int) [100]int { + for i := 89; i >= -10; i-- { // ERROR "Induction variable: limits \[-10,89\], increment 1$" + a[i+9] = i + a[i+10] = i // ERROR "Proved IsInBounds$" + a[i+11] = i } return a } func k4(a [100]int) [100]int { min := (-1) << 63 - for i := min; i < min+50; i++ { // ERROR "Induction variable with minimum -9223372036854775808 and increment 1$" - a[i-min] = i // ERROR "Found redundant \(IsInBounds ind 100\), ind < 50$" + for i := min; i < min+50; i++ { // ERROR "Induction variable: limits \[-9223372036854775808,-9223372036854775758\), increment 1$" + a[i-min] = i // ERROR "Proved IsInBounds$" } return a } func k5(a [100]int) [100]int { max := (1 << 63) - 1 - for i := max - 50; i < max; i++ { // ERROR "Induction variable with minimum 9223372036854775757 and increment 1$" - a[i-max+50] = i // ERROR "Found redundant \(IsInBounds ind 100\), ind < 50$" - a[i-(max-70)] = i // ERROR "Found redundant \(IsInBounds ind 100\), ind < 70$" + for i := max - 50; i < max; i++ { // ERROR "Induction variable: limits \[9223372036854775757,9223372036854775807\), increment 1" + a[i-max+50] = i // ERROR "Proved IsInBounds$" + a[i-(max-70)] = i // ERROR "Proved IsInBounds$" } return a } @@ -220,14 +274,21 @@ func nobce1() { } func nobce2(a string) { - for i := int64(0); i < int64(len(a)); i++ { // ERROR "Induction variable with minimum 0 and increment 1$" - useString(a[i:]) // ERROR "Found redundant IsSliceInBounds$" + for i := int64(0); i < int64(len(a)); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" + useString(a[i:]) // ERROR "Proved IsSliceInBounds$" + } + for i := int64(0); i < int64(len(a))-31337; i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" + useString(a[i:]) // ERROR "Proved IsSliceInBounds$" + } + for i := int64(0); i < int64(len(a))+int64(-1<<63); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" + useString(a[i:]) // ERROR "Proved IsSliceInBounds$" } - for i := int64(0); i < int64(len(a))-31337; i++ { // ERROR "Induction variable with minimum 0 and increment 1$" - useString(a[i:]) // ERROR "Found redundant IsSliceInBounds$" + j := int64(len(a)) - 123 + for i := int64(0); i < j+123+int64(-1<<63); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" + useString(a[i:]) // ERROR "Proved IsSliceInBounds$" } - for i := int64(0); i < int64(len(a))+int64(-1<<63); i++ { // ERROR "Induction variable with minimum 0 and increment 1$" - // tests an overflow of StringLen-MinInt64 + for i := int64(0); i < j+122+int64(-1<<63); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" + // len(a)-123+122+MinInt overflows when len(a) == 0, so a bound check is needed here useString(a[i:]) } } @@ -235,12 +296,22 @@ func nobce2(a string) { func nobce3(a [100]int64) [100]int64 { min := int64((-1) << 63) max := int64((1 << 63) - 1) - for i := min; i < max; i++ { // ERROR "Induction variable with minimum -9223372036854775808 and increment 1$" + for i := min; i < max; i++ { // ERROR "Induction variable: limits \[-9223372036854775808,9223372036854775807\), increment 1$" a[i] = i } return a } +func issue26116a(a []int) { + // There is no induction variable here. The comparison is in the wrong direction. + for i := 3; i > 6; i++ { + a[i] = 0 + } + for i := 7; i < 3; i-- { + a[i] = 1 + } +} + //go:noinline func useString(a string) { } diff --git a/test/makemap.go b/test/makemap.go index 60983c0d4..d19e6c344 100644 --- a/test/makemap.go +++ b/test/makemap.go @@ -18,6 +18,12 @@ func main() { sink = make(T, -1) // ERROR "negative size argument in make.*" sink = make(T, uint64(1<<63)) // ERROR "size argument too large in make.*" + // Test that errors are emitted at call sites, not const declarations + const x = -1 + sink = make(T, x) // ERROR "negative size argument in make.*" + const y = uint64(1 << 63) + sink = make(T, y) // ERROR "size argument too large in make.*" + sink = make(T, 0.5) // ERROR "constant 0.5 truncated to integer" sink = make(T, 1.0) sink = make(T, float32(1.0)) // ERROR "non-integer size argument in make.*" diff --git a/test/mapclear.go b/test/mapclear.go new file mode 100644 index 000000000..a29f30da7 --- /dev/null +++ b/test/mapclear.go @@ -0,0 +1,89 @@ +// run + +// Copyright 2018 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. + +// Ensure that range loops over maps with delete statements +// have the requisite side-effects. + +package main + +import ( + "fmt" + "os" +) + +func checkcleared() { + m := make(map[byte]int) + m[1] = 1 + m[2] = 2 + for k := range m { + delete(m, k) + } + l := len(m) + if want := 0; l != want { + fmt.Printf("len after map clear = %d want %d\n", l, want) + os.Exit(1) + } + + m[0] = 0 // To have non empty map and avoid internal map code fast paths. + n := 0 + for range m { + n++ + } + if want := 1; n != want { + fmt.Printf("number of keys found = %d want %d\n", n, want) + os.Exit(1) + } +} + +func checkloopvars() { + k := 0 + m := make(map[int]int) + m[42] = 0 + for k = range m { + delete(m, k) + } + if want := 42; k != want { + fmt.Printf("var after range with side-effect = %d want %d\n", k, want) + os.Exit(1) + } +} + +func checksideeffects() { + var x int + f := func() int { + x++ + return 0 + } + m := make(map[int]int) + m[0] = 0 + m[1] = 1 + for k := range m { + delete(m, k+f()) + } + if want := 2; x != want { + fmt.Printf("var after range with side-effect = %d want %d\n", x, want) + os.Exit(1) + } + + var n int + m = make(map[int]int) + m[0] = 0 + m[1] = 1 + for k := range m { + delete(m, k) + n++ + } + if want := 2; n != want { + fmt.Printf("counter for range with side-effect = %d want %d\n", n, want) + os.Exit(1) + } +} + +func main() { + checkcleared() + checkloopvars() + checksideeffects() +} diff --git a/test/method7.go b/test/method7.go index 72c88b377..15e123e85 100644 --- a/test/method7.go +++ b/test/method7.go @@ -45,10 +45,9 @@ func main() { interface{ m1(string) }.m1(x, "d") want += " m1(d)" - // cannot link the call below - see #22444 - // g := struct{ T }.m2 - // g(struct{T}{}) - // want += " m2()" + g := struct{ T }.m2 + g(struct{ T }{}) + want += " m2()" if got != want { panic("got" + got + ", want" + want) diff --git a/test/nilptr3.go b/test/nilptr3.go index 9a96bb538..a22e60ef1 100644 --- a/test/nilptr3.go +++ b/test/nilptr3.go @@ -1,5 +1,7 @@ // errorcheck -0 -d=nil +// +build !wasm + // 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. diff --git a/test/nilptr3_wasm.go b/test/nilptr3_wasm.go new file mode 100644 index 000000000..9376d4209 --- /dev/null +++ b/test/nilptr3_wasm.go @@ -0,0 +1,270 @@ +// errorcheck -0 -d=nil + +// +build wasm + +// 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 this check (but nilcheck and load are in different blocks) + + 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" +} + +func f5(p *float32, q *float64, r *float32, s *float64) float64 { + x := float64(*p) // ERROR "generated nil check" + y := *q // ERROR "generated nil check" + *r = 7 // ERROR "generated nil check" + *s = 9 // ERROR "generated nil check" + return x + y +} + +type T [29]byte + +func f6(p, q *T) { + x := *p // ERROR "generated nil check" + *q = x // ERROR "generated nil check" +} + +func m1(m map[int][80]byte) byte { + v := m[3] // ERROR "removed nil check" + return v[5] +} +func m2(m map[int][800]byte) byte { + v := m[3] // ERROR "removed nil check" + return v[5] +} +func m3(m map[int][80]byte) (byte, bool) { + v, ok := m[3] // ERROR "removed nil check" + return v[5], ok +} +func m4(m map[int][800]byte) (byte, bool) { + v, ok := m[3] // ERROR "removed nil check" + return v[5], ok +} +func p1() byte { + p := new([100]byte) + return p[5] // ERROR "removed nil check" +} + +// make sure not to do nil check for access of PAUTOHEAP +//go:noinline +func (p *Struct) m() {} +func c1() { + var x Struct + func() { x.m() }() // ERROR "removed nil check" +} + +type SS struct { + x byte +} + +type TT struct { + SS +} + +func f(t *TT) *byte { + // See issue 17242. + s := &t.SS // ERROR "removed nil check" + return &s.x // ERROR "generated nil check" +} + +// make sure not to do nil check for newobject +func f7() (*Struct, float64) { + t := new(Struct) + p := &t.Y // ERROR "removed nil check" + return t, *p // ERROR "removed nil check" +} + +// make sure to remove nil check for memory move (issue #18003) +func f8(t *[8]int) [8]int { + return *t // ERROR "generated nil check" +} + +func f9() []int { + x := new([1]int) + x[0] = 1 // ERROR "removed nil check" + y := x[:] // ERROR "removed nil check" + return y +} diff --git a/test/nosplit.go b/test/nosplit.go index e6cecebde..e6cd04e56 100644 --- a/test/nosplit.go +++ b/test/nosplit.go @@ -1,4 +1,4 @@ -// +build !nacl +// +build !nacl,!js // run // Copyright 2014 The Go Authors. All rights reserved. diff --git a/test/peano.go b/test/peano.go index 745f5153f..1102a9724 100644 --- a/test/peano.go +++ b/test/peano.go @@ -9,6 +9,8 @@ package main +import "runtime" + type Number *Number // ------------------------------------- @@ -116,7 +118,11 @@ var results = [...]int{ } func main() { - for i := 0; i <= 9; i++ { + max := 9 + if runtime.GOARCH == "wasm" { + max = 7 // stack size is limited + } + for i := 0; i <= max; i++ { if f := count(fact(gen(i))); f != results[i] { println("FAIL:", i, "!:", f, "!=", results[i]) panic(0) diff --git a/test/print.go b/test/print.go index b7f3db0a4..7718c735e 100644 --- a/test/print.go +++ b/test/print.go @@ -1,4 +1,4 @@ -// cmpout +// run // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/printbig.go b/test/printbig.go index 5693c58d4..9e08c39ad 100644 --- a/test/printbig.go +++ b/test/printbig.go @@ -1,4 +1,4 @@ -// cmpout +// run // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/prove.go b/test/prove.go index e89ab3f8d..45cee9e8b 100644 --- a/test/prove.go +++ b/test/prove.go @@ -11,11 +11,11 @@ import "math" func f0(a []int) int { a[0] = 1 - a[0] = 1 // ERROR "Proved boolean IsInBounds$" + a[0] = 1 // ERROR "Proved IsInBounds$" a[6] = 1 - a[6] = 1 // ERROR "Proved boolean IsInBounds$" + a[6] = 1 // ERROR "Proved IsInBounds$" + a[5] = 1 // ERROR "Proved IsInBounds$" a[5] = 1 // ERROR "Proved IsInBounds$" - a[5] = 1 // ERROR "Proved boolean IsInBounds$" return 13 } @@ -23,27 +23,27 @@ func f1(a []int) int { if len(a) <= 5 { return 18 } - a[0] = 1 // ERROR "Proved non-negative bounds IsInBounds$" - a[0] = 1 // ERROR "Proved boolean IsInBounds$" + a[0] = 1 // ERROR "Proved IsInBounds$" + a[0] = 1 // ERROR "Proved IsInBounds$" a[6] = 1 - a[6] = 1 // ERROR "Proved boolean IsInBounds$" + a[6] = 1 // ERROR "Proved IsInBounds$" + a[5] = 1 // ERROR "Proved IsInBounds$" a[5] = 1 // ERROR "Proved IsInBounds$" - a[5] = 1 // ERROR "Proved boolean IsInBounds$" return 26 } func f1b(a []int, i int, j uint) int { if i >= 0 && i < len(a) { - return a[i] // ERROR "Proved non-negative bounds IsInBounds$" + return a[i] // ERROR "Proved IsInBounds$" } if i >= 10 && i < len(a) { - return a[i] // ERROR "Proved non-negative bounds IsInBounds$" + return a[i] // ERROR "Proved IsInBounds$" } if i >= 10 && i < len(a) { - return a[i] // ERROR "Proved non-negative bounds IsInBounds$" + return a[i] // ERROR "Proved IsInBounds$" } - if i >= 10 && i < len(a) { // todo: handle this case - return a[i-10] + if i >= 10 && i < len(a) { + return a[i-10] // ERROR "Proved IsInBounds$" } if j < uint(len(a)) { return a[j] // ERROR "Proved IsInBounds$" @@ -62,9 +62,9 @@ func f1c(a []int, i int64) int { } func f2(a []int) int { - for i := range a { + for i := range a { // ERROR "Induction variable: limits \[0,\?\), increment 1" a[i+1] = i - a[i+1] = i // ERROR "Proved boolean IsInBounds$" + a[i+1] = i // ERROR "Proved IsInBounds$" } return 34 } @@ -84,15 +84,14 @@ func f4a(a, b, c int) int { if a > b { // ERROR "Disproved Greater64$" return 50 } - if a < b { // ERROR "Proved boolean Less64$" + if a < b { // ERROR "Proved Less64$" return 53 } - if a == b { // ERROR "Disproved boolean Eq64$" + // We can't get to this point and prove knows that, so + // there's no message for the next (obvious) branch. + if a != a { return 56 } - if a > b { // ERROR "Disproved boolean Greater64$" - return 59 - } return 61 } return 63 @@ -127,8 +126,8 @@ func f4c(a, b, c int) int { func f4d(a, b, c int) int { if a < b { if a < c { - if a < b { // ERROR "Proved boolean Less64$" - if a < c { // ERROR "Proved boolean Less64$" + if a < b { // ERROR "Proved Less64$" + if a < c { // ERROR "Proved Less64$" return 87 } return 89 @@ -218,8 +217,8 @@ func f6e(a uint8) int { func f7(a []int, b int) int { if b < len(a) { a[b] = 3 - if b < len(a) { // ERROR "Proved boolean Less64$" - a[b] = 5 // ERROR "Proved boolean IsInBounds$" + if b < len(a) { // ERROR "Proved Less64$" + a[b] = 5 // ERROR "Proved IsInBounds$" } } return 161 @@ -242,7 +241,7 @@ func f9(a, b bool) int { if a { return 1 } - if a || b { // ERROR "Disproved boolean Arg$" + if a || b { // ERROR "Disproved Arg$" return 2 } return 3 @@ -260,22 +259,22 @@ func f10(a string) int { func f11a(a []int, i int) { useInt(a[i]) - useInt(a[i]) // ERROR "Proved boolean IsInBounds$" + useInt(a[i]) // ERROR "Proved IsInBounds$" } func f11b(a []int, i int) { useSlice(a[i:]) - useSlice(a[i:]) // ERROR "Proved boolean IsSliceInBounds$" + useSlice(a[i:]) // ERROR "Proved IsSliceInBounds$" } func f11c(a []int, i int) { useSlice(a[:i]) - useSlice(a[:i]) // ERROR "Proved boolean IsSliceInBounds$" + useSlice(a[:i]) // ERROR "Proved IsSliceInBounds$" } func f11d(a []int, i int) { useInt(a[2*i+7]) - useInt(a[2*i+7]) // ERROR "Proved boolean IsInBounds$" + useInt(a[2*i+7]) // ERROR "Proved IsInBounds$" } func f12(a []int, b int) { @@ -305,7 +304,7 @@ func f13a(a, b, c int, x bool) int { } } if x { - if a > 12 { // ERROR "Proved boolean Greater64$" + if a > 12 { // ERROR "Proved Greater64$" return 5 } } @@ -327,7 +326,7 @@ func f13b(a int, x bool) int { } } if x { - if a == -9 { // ERROR "Proved boolean Eq64$" + if a == -9 { // ERROR "Proved Eq64$" return 9 } } @@ -349,7 +348,7 @@ func f13b(a int, x bool) int { func f13c(a int, x bool) int { if a < 90 { if x { - if a < 90 { // ERROR "Proved boolean Less64$" + if a < 90 { // ERROR "Proved Less64$" return 13 } } @@ -398,8 +397,7 @@ func f13e(a int) int { func f13f(a int64) int64 { if a > math.MaxInt64 { - // Unreachable, but prove doesn't know that. - if a == 0 { + if a == 0 { // ERROR "Disproved Eq64$" return 1 } } @@ -450,7 +448,7 @@ func f14(p, q *int, a []int) { j := *q i2 := *p useInt(a[i1+j]) - useInt(a[i2+j]) // ERROR "Proved boolean IsInBounds$" + useInt(a[i2+j]) // ERROR "Proved IsInBounds$" } func f15(s []int, x int) { @@ -460,11 +458,238 @@ func f15(s []int, x int) { func f16(s []int) []int { if len(s) >= 10 { - return s[:10] // ERROR "Proved non-negative bounds IsSliceInBounds$" + return s[:10] // ERROR "Proved IsSliceInBounds$" } return nil } +func f17(b []int) { + for i := 0; i < len(b); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1" + // This tests for i <= cap, which we can only prove + // using the derived relation between len and cap. + // This depends on finding the contradiction, since we + // don't query this condition directly. + useSlice(b[:i]) // ERROR "Proved IsSliceInBounds$" + } +} + +func f18(b []int, x int, y uint) { + _ = b[x] + _ = b[y] + + if x > len(b) { // ERROR "Disproved Greater64$" + return + } + if y > uint(len(b)) { // ERROR "Disproved Greater64U$" + return + } + if int(y) > len(b) { // ERROR "Disproved Greater64$" + return + } +} + +func sm1(b []int, x int) { + // Test constant argument to slicemask. + useSlice(b[2:8]) // ERROR "Proved slicemask not needed$" + // Test non-constant argument with known limits. + if cap(b) > 10 { + useSlice(b[2:]) // ERROR "Proved slicemask not needed$" + } +} + +func lim1(x, y, z int) { + // Test relations between signed and unsigned limits. + if x > 5 { + if uint(x) > 5 { // ERROR "Proved Greater64U$" + return + } + } + if y >= 0 && y < 4 { + if uint(y) > 4 { // ERROR "Disproved Greater64U$" + return + } + if uint(y) < 5 { // ERROR "Proved Less64U$" + return + } + } + if z < 4 { + if uint(z) > 4 { // Not provable without disjunctions. + return + } + } +} + +// fence1–4 correspond to the four fence-post implications. + +func fence1(b []int, x, y int) { + // Test proofs that rely on fence-post implications. + if x+1 > y { + if x < y { // ERROR "Disproved Less64$" + return + } + } + if len(b) < cap(b) { + // This eliminates the growslice path. + b = append(b, 1) // ERROR "Disproved Greater64$" + } +} + +func fence2(x, y int) { + if x-1 < y { + if x > y { // ERROR "Disproved Greater64$" + return + } + } +} + +func fence3(b []int, x, y int64) { + if x-1 >= y { + if x <= y { // Can't prove because x may have wrapped. + return + } + } + + if x != math.MinInt64 && x-1 >= y { + if x <= y { // ERROR "Disproved Leq64$" + return + } + } + + if n := len(b); n > 0 { + b[n-1] = 0 // ERROR "Proved IsInBounds$" + } +} + +func fence4(x, y int64) { + if x >= y+1 { + if x <= y { + return + } + } + if y != math.MaxInt64 && x >= y+1 { + if x <= y { // ERROR "Disproved Leq64$" + return + } + } +} + +// Check transitive relations +func trans1(x, y int64) { + if x > 5 { + if y > x { + if y > 2 { // ERROR "Proved Greater64" + return + } + } else if y == x { + if y > 5 { // ERROR "Proved Greater64" + return + } + } + } + if x >= 10 { + if y > x { + if y > 10 { // ERROR "Proved Greater64" + return + } + } + } +} + +func trans2(a, b []int, i int) { + if len(a) != len(b) { + return + } + + _ = a[i] + _ = b[i] // ERROR "Proved IsInBounds$" +} + +func trans3(a, b []int, i int) { + if len(a) > len(b) { + return + } + + _ = a[i] + _ = b[i] // ERROR "Proved IsInBounds$" +} + +// Derived from nat.cmp +func natcmp(x, y []uint) (r int) { + m := len(x) + n := len(y) + if m != n || m == 0 { + return + } + + i := m - 1 + for i > 0 && // ERROR "Induction variable: limits \(0,\?\], increment 1" + x[i] == // ERROR "Proved IsInBounds$" + y[i] { // ERROR "Proved IsInBounds$" + i-- + } + + switch { + case x[i] < // todo, cannot prove this because it's dominated by i<=0 || x[i]==y[i] + y[i]: // ERROR "Proved IsInBounds$" + r = -1 + case x[i] > // ERROR "Proved IsInBounds$" + y[i]: // ERROR "Proved IsInBounds$" + r = 1 + } + return +} + +func suffix(s, suffix string) bool { + // todo, we're still not able to drop the bound check here in the general case + return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix +} + +func constsuffix(s string) bool { + return suffix(s, "abc") // ERROR "Proved IsSliceInBounds$" +} + +// oforuntil tests the pattern created by OFORUNTIL blocks. These are +// handled by addLocalInductiveFacts rather than findIndVar. +func oforuntil(b []int) { + i := 0 + if len(b) > i { + top: + println(b[i]) // ERROR "Induction variable: limits \[0,\?\), increment 1$" "Proved IsInBounds$" + i++ + if i < len(b) { + goto top + } + } +} + +// The range tests below test the index variable of range loops. + +// range1 compiles to the "efficiently indexable" form of a range loop. +func range1(b []int) { + for i, v := range b { // ERROR "Induction variable: limits \[0,\?\), increment 1$" + b[i] = v + 1 // ERROR "Proved IsInBounds$" + if i < len(b) { // ERROR "Proved Less64$" + println("x") + } + if i >= 0 { // ERROR "Proved Geq64$" + println("x") + } + } +} + +// range2 elements are larger, so they use the general form of a range loop. +func range2(b [][32]int) { + for i, v := range b { + b[i][0] = v[0] + 1 // ERROR "Induction variable: limits \[0,\?\), increment 1$" "Proved IsInBounds$" + if i < len(b) { // ERROR "Proved Less64$" + println("x") + } + if i >= 0 { // ERROR "Proved Geq64" + println("x") + } + } +} + //go:noinline func useInt(a int) { } diff --git a/test/retjmp.dir/a.s b/test/retjmp.dir/a.s new file mode 100644 index 000000000..c67a06638 --- /dev/null +++ b/test/retjmp.dir/a.s @@ -0,0 +1,12 @@ +// Copyright 2018 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. + +TEXT ·f(SB), 4, $8-0 + CALL ·f1(SB) + RET ·f2(SB) + CALL ·unreachable(SB) + +TEXT ·leaf(SB), 4, $0-0 + RET ·f3(SB) + JMP ·unreachable(SB) diff --git a/test/retjmp.dir/main.go b/test/retjmp.dir/main.go new file mode 100644 index 000000000..cb4bd018b --- /dev/null +++ b/test/retjmp.dir/main.go @@ -0,0 +1,32 @@ +// Copyright 2018 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. + +package main + +func f() +func leaf() + +var f1called, f2called, f3called bool + +func main() { + f() + if !f1called { + panic("f1 not called") + } + if !f2called { + panic("f2 not called") + } + leaf() + if !f3called { + panic("f3 not called") + } +} + +func f1() { f1called = true } +func f2() { f2called = true } +func f3() { f3called = true } + +func unreachable() { + panic("unreachable function called") +} diff --git a/test/retjmp.go b/test/retjmp.go new file mode 100644 index 000000000..778d90362 --- /dev/null +++ b/test/retjmp.go @@ -0,0 +1,9 @@ +// buildrundir + +// Copyright 2018 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 return jump works correctly in assembly code. + +package ignored diff --git a/test/run.go b/test/run.go index 22ec7576f..99ef79feb 100644 --- a/test/run.go +++ b/test/run.go @@ -5,9 +5,6 @@ // license that can be found in the LICENSE file. // Run runs tests in the test directory. -// -// TODO(bradfitz): docs of some sort, once we figure out how we're changing -// headers of files package main import ( @@ -52,7 +49,7 @@ var ( // dirs are the directories to look for *.go files in. // TODO(bradfitz): just use all directories? - dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs"} + dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "codegen"} // ratec controls the max number of tests running at a time. ratec chan bool @@ -167,6 +164,22 @@ func toolPath(name string) string { return p } +// goTool reports the path of the go tool to use to run the tests. +// If possible, use the same Go used to run run.go, otherwise +// fallback to the go version found in the PATH. +func goTool() string { + var exeSuffix string + if runtime.GOOS == "windows" { + exeSuffix = ".exe" + } + path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix) + if _, err := os.Stat(path); err == nil { + return path + } + // Just run "go" from PATH + return "go" +} + func shardMatch(name string) bool { if *shards == 0 { return true @@ -194,7 +207,7 @@ func goFiles(dir string) []string { type runCmd func(...string) ([]byte, error) func compileFile(runcmd runCmd, longname string, flags []string) (out []byte, err error) { - cmd := []string{"go", "tool", "compile", "-e"} + cmd := []string{goTool(), "tool", "compile", "-e"} cmd = append(cmd, flags...) if *linkshared { cmd = append(cmd, "-dynlink", "-installsuffix=dynlink") @@ -203,8 +216,12 @@ func compileFile(runcmd runCmd, longname string, flags []string) (out []byte, er return runcmd(cmd...) } -func compileInDir(runcmd runCmd, dir string, flags []string, names ...string) (out []byte, err error) { - cmd := []string{"go", "tool", "compile", "-e", "-D", ".", "-I", "."} +func compileInDir(runcmd runCmd, dir string, flags []string, localImports bool, names ...string) (out []byte, err error) { + cmd := []string{goTool(), "tool", "compile", "-e"} + if localImports { + // Set relative path for local imports and import search path to current dir. + cmd = append(cmd, "-D", ".", "-I", ".") + } cmd = append(cmd, flags...) if *linkshared { cmd = append(cmd, "-dynlink", "-installsuffix=dynlink") @@ -217,7 +234,7 @@ func compileInDir(runcmd runCmd, dir string, flags []string, names ...string) (o func linkFile(runcmd runCmd, goname string) (err error) { pfile := strings.Replace(goname, ".go", ".o", -1) - cmd := []string{"go", "tool", "link", "-w", "-o", "a.exe", "-L", "."} + cmd := []string{goTool(), "tool", "link", "-w", "-o", "a.exe", "-L", "."} if *linkshared { cmd = append(cmd, "-linkshared", "-installsuffix=dynlink") } @@ -476,6 +493,7 @@ func (t *test) run() { wantError := false wantAuto := false singlefilepkgs := false + localImports := true f := strings.Fields(action) if len(f) > 0 { action = f[0] @@ -484,11 +502,7 @@ func (t *test) run() { // TODO: Clean up/simplify this switch statement. switch action { - case "rundircmpout": - action = "rundir" - case "cmpout": - action = "run" // the run case already looks for <dir>/<test>.out files - case "compile", "compiledir", "build", "builddir", "run", "buildrun", "runoutput", "rundir": + case "compile", "compiledir", "build", "builddir", "buildrundir", "run", "buildrun", "runoutput", "rundir", "asmcheck": // nothing to do case "errorcheckandrundir": wantError = false // should be no error if also will run @@ -511,10 +525,18 @@ func (t *test) run() { // collect flags for len(args) > 0 && strings.HasPrefix(args[0], "-") { switch args[0] { + case "-1": + wantError = true case "-0": wantError = false case "-s": singlefilepkgs = true + case "-n": + // Do not set relative path for local imports to current dir, + // e.g. do not pass -D . -I . to the compiler. + // Used in fixedbugs/bug345.go to allow compilation and import of local pkg. + // See golang.org/issue/25635 + localImports = false case "-t": // timeout in seconds args = args[1:] var err error @@ -593,9 +615,38 @@ func (t *test) run() { default: t.err = fmt.Errorf("unimplemented action %q", action) + case "asmcheck": + // Compile Go file and match the generated assembly + // against a set of regexps in comments. + ops := t.wantedAsmOpcodes(long) + for _, env := range ops.Envs() { + cmdline := []string{"build", "-gcflags", "-S"} + cmdline = append(cmdline, flags...) + cmdline = append(cmdline, long) + cmd := exec.Command(goTool(), cmdline...) + cmd.Env = append(os.Environ(), env.Environ()...) + + var buf bytes.Buffer + cmd.Stdout, cmd.Stderr = &buf, &buf + if err := cmd.Run(); err != nil { + fmt.Println(env, "\n", cmd.Stderr) + t.err = err + return + } + + t.err = t.asmCheck(buf.String(), long, env, ops[env]) + if t.err != nil { + return + } + } + return + case "errorcheck": + // Compile Go file. + // Fail if wantError is true and compilation was successful and vice versa. + // Match errors produced by gc against errors in comments. // TODO(gri) remove need for -C (disable printing of columns in error messages) - cmdline := []string{"go", "tool", "compile", "-C", "-e", "-o", "a.o"} + cmdline := []string{goTool(), "tool", "compile", "-C", "-e", "-o", "a.o"} // No need to add -dynlink even if linkshared if we're just checking for errors... cmdline = append(cmdline, flags...) cmdline = append(cmdline, long) @@ -618,10 +669,11 @@ func (t *test) run() { return case "compile": + // Compile Go file. _, t.err = compileFile(runcmd, long, flags) case "compiledir": - // Compile all files in the directory in lexicographic order. + // Compile all files in the directory as packages in lexicographic order. longdir := filepath.Join(cwd, t.goDirName()) pkgs, err := goDirPackages(longdir, singlefilepkgs) if err != nil { @@ -629,24 +681,31 @@ func (t *test) run() { return } for _, gofiles := range pkgs { - _, t.err = compileInDir(runcmd, longdir, flags, gofiles...) + _, t.err = compileInDir(runcmd, longdir, flags, localImports, gofiles...) if t.err != nil { return } } case "errorcheckdir", "errorcheckandrundir": - // errorcheck all files in lexicographic order - // useful for finding importing errors + // Compile and errorCheck all files in the directory as packages in lexicographic order. + // If errorcheckdir and wantError, compilation of the last package must fail. + // If errorcheckandrundir and wantError, compilation of the package prior the last must fail. longdir := filepath.Join(cwd, t.goDirName()) pkgs, err := goDirPackages(longdir, singlefilepkgs) if err != nil { t.err = err return } + errPkg := len(pkgs) - 1 + if wantError && action == "errorcheckandrundir" { + // The last pkg should compiled successfully and will be run in next case. + // Preceding pkg must return an error from compileInDir. + errPkg-- + } for i, gofiles := range pkgs { - out, err := compileInDir(runcmd, longdir, flags, gofiles...) - if i == len(pkgs)-1 { + out, err := compileInDir(runcmd, longdir, flags, localImports, gofiles...) + if i == errPkg { if wantError && err == nil { t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out) return @@ -673,8 +732,10 @@ func (t *test) run() { fallthrough case "rundir": - // Compile all files in the directory in lexicographic order. - // then link as if the last file is the main package and run it + // Compile all files in the directory as packages in lexicographic order. + // In case of errorcheckandrundir, ignore failed compilation of the package before the last. + // Link as if the last file is the main package, run it. + // Verify the expected output. longdir := filepath.Join(cwd, t.goDirName()) pkgs, err := goDirPackages(longdir, singlefilepkgs) if err != nil { @@ -682,8 +743,10 @@ func (t *test) run() { return } for i, gofiles := range pkgs { - _, err := compileInDir(runcmd, longdir, flags, gofiles...) - if err != nil { + _, err := compileInDir(runcmd, longdir, flags, localImports, gofiles...) + // Allow this package compilation fail based on conditions below; + // its errors were checked in previous case. + if err != nil && !(wantError && action == "errorcheckandrundir" && i == len(pkgs)-2) { t.err = err return } @@ -709,14 +772,15 @@ func (t *test) run() { } case "build": - _, err := runcmd("go", "build", goGcflags(), "-o", "a.exe", long) + // Build Go file. + _, err := runcmd(goTool(), "build", goGcflags(), "-o", "a.exe", long) if err != nil { t.err = err } - case "builddir": + case "builddir", "buildrundir": // Build an executable from all the .go and .s files in a subdirectory. - useTmp = true + // Run it and verify its output in the buildrundir case. longdir := filepath.Join(cwd, t.goDirName()) files, dirErr := ioutil.ReadDir(longdir) if dirErr != nil { @@ -735,7 +799,7 @@ func (t *test) run() { } var objs []string - cmd := []string{"go", "tool", "compile", "-e", "-D", ".", "-I", ".", "-o", "go.o"} + cmd := []string{goTool(), "tool", "compile", "-e", "-D", ".", "-I", ".", "-o", "go.o"} if len(asms) > 0 { cmd = append(cmd, "-asmhdr", "go_asm.h") } @@ -749,7 +813,7 @@ func (t *test) run() { } objs = append(objs, "go.o") if len(asms) > 0 { - cmd = []string{"go", "tool", "asm", "-e", "-I", ".", "-o", "asm.o"} + cmd = []string{goTool(), "tool", "asm", "-e", "-I", ".", "-o", "asm.o"} for _, file := range asms { cmd = append(cmd, filepath.Join(longdir, file.Name())) } @@ -760,24 +824,36 @@ func (t *test) run() { } objs = append(objs, "asm.o") } - cmd = []string{"go", "tool", "pack", "c", "all.a"} + cmd = []string{goTool(), "tool", "pack", "c", "all.a"} cmd = append(cmd, objs...) _, err = runcmd(cmd...) if err != nil { t.err = err break } - cmd = []string{"go", "tool", "link", "all.a"} + cmd = []string{goTool(), "tool", "link", "-o", "a.exe", "all.a"} _, err = runcmd(cmd...) if err != nil { t.err = err break } + if action == "buildrundir" { + cmd = append(findExecCmd(), filepath.Join(t.tempDir, "a.exe")) + out, err := runcmd(cmd...) + if err != nil { + t.err = err + break + } + if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() { + t.err = fmt.Errorf("incorrect output\n%s", out) + } + } - case "buildrun": // build binary, then run binary, instead of go run. Useful for timeout tests where failure mode is infinite loop. + case "buildrun": + // Build an executable from Go file, then run it, verify its output. + // Useful for timeout tests where failure mode is infinite loop. // TODO: not supported on NaCl - useTmp = true - cmd := []string{"go", "build", goGcflags(), "-o", "a.exe"} + cmd := []string{goTool(), "build", goGcflags(), "-o", "a.exe"} if *linkshared { cmd = append(cmd, "-linkshared") } @@ -801,6 +877,9 @@ func (t *test) run() { } case "run": + // Run Go file if no special go command flags are provided; + // otherwise build an executable and run it. + // Verify the output. useTmp = false var out []byte var err error @@ -813,12 +892,12 @@ func (t *test) run() { // Because we run lots of trivial test programs, // the time adds up. pkg := filepath.Join(t.tempDir, "pkg.a") - if _, err := runcmd("go", "tool", "compile", "-o", pkg, t.goFileName()); err != nil { + if _, err := runcmd(goTool(), "tool", "compile", "-o", pkg, t.goFileName()); err != nil { t.err = err return } exe := filepath.Join(t.tempDir, "test.exe") - cmd := []string{"go", "tool", "link", "-s", "-w"} + cmd := []string{goTool(), "tool", "link", "-s", "-w"} cmd = append(cmd, "-o", exe, pkg) if _, err := runcmd(cmd...); err != nil { t.err = err @@ -826,7 +905,7 @@ func (t *test) run() { } out, err = runcmd(append([]string{exe}, args...)...) } else { - cmd := []string{"go", "run", goGcflags()} + cmd := []string{goTool(), "run", goGcflags()} if *linkshared { cmd = append(cmd, "-linkshared") } @@ -843,12 +922,14 @@ func (t *test) run() { } case "runoutput": + // Run Go file and write its output into temporary Go file. + // Run generated Go file and verify its output. rungatec <- true defer func() { <-rungatec }() useTmp = false - cmd := []string{"go", "run", goGcflags()} + cmd := []string{goTool(), "run", goGcflags()} if *linkshared { cmd = append(cmd, "-linkshared") } @@ -863,7 +944,7 @@ func (t *test) run() { t.err = fmt.Errorf("write tempfile:%s", err) return } - cmd = []string{"go", "run", goGcflags()} + cmd = []string{goTool(), "run", goGcflags()} if *linkshared { cmd = append(cmd, "-linkshared") } @@ -878,8 +959,10 @@ func (t *test) run() { } case "errorcheckoutput": + // Run Go file and write its output into temporary Go file. + // Compile and errorCheck generated Go file. useTmp = false - cmd := []string{"go", "run", goGcflags()} + cmd := []string{goTool(), "run", goGcflags()} if *linkshared { cmd = append(cmd, "-linkshared") } @@ -895,7 +978,7 @@ func (t *test) run() { t.err = fmt.Errorf("write tempfile:%s", err) return } - cmdline := []string{"go", "tool", "compile", "-e", "-o", "a.o"} + cmdline := []string{goTool(), "tool", "compile", "-e", "-o", "a.o"} cmdline = append(cmdline, flags...) cmdline = append(cmdline, tfile) out, err = runcmd(cmdline...) @@ -973,6 +1056,17 @@ func splitOutput(out string, wantAuto bool) []string { return res } +// errorCheck matches errors in outStr against comments in source files. +// For each line of the source files which should generate an error, +// there should be a comment of the form // ERROR "regexp". +// If outStr has an error for a line which has no such comment, +// this function will report an error. +// Likewise if outStr does not have an error for a line which has a comment, +// or if the error message does not match the <regexp>. +// The <regexp> syntax is Perl but its best to stick to egrep. +// +// Sources files are supplied as fullshort slice. +// It consists of pairs: full path to source file and it's base name. func (t *test) errorCheck(outStr string, wantAuto bool, fullshort ...string) (err error) { defer func() { if *verbose && err != nil { @@ -1115,7 +1209,7 @@ func (t *test) updateErrors(out, file string) { return } // Polish. - exec.Command("go", "fmt", file).CombinedOutput() + exec.Command(goTool(), "fmt", file).CombinedOutput() } // matchPrefix reports whether s is of the form ^(.*/)?prefix(:|[), @@ -1226,6 +1320,264 @@ func (t *test) wantedErrors(file, short string) (errs []wantedError) { return } +const ( + // Regexp to match a single opcode check: optionally begin with "-" (to indicate + // a negative check), followed by a string literal enclosed in "" or ``. For "", + // backslashes must be handled. + reMatchCheck = `-?(?:\x60[^\x60]*\x60|"(?:[^"\\]|\\.)*")` +) + +var ( + // Regexp to split a line in code and comment, trimming spaces + rxAsmComment = regexp.MustCompile(`^\s*(.*?)\s*(?:\/\/\s*(.+)\s*)?$`) + + // Regexp to extract an architecture check: architecture name, followed by semi-colon, + // followed by a comma-separated list of opcode checks. + rxAsmPlatform = regexp.MustCompile(`(\w+)(/\w+)?(/\w*)?:(` + reMatchCheck + `(?:,` + reMatchCheck + `)*)`) + + // Regexp to extract a single opcoded check + rxAsmCheck = regexp.MustCompile(reMatchCheck) + + // List of all architecture variants. Key is the GOARCH architecture, + // value[0] is the variant-changing environment variable, and values[1:] + // are the supported variants. + archVariants = map[string][]string{ + "386": {"GO386", "387", "sse2"}, + "amd64": {}, + "arm": {"GOARM", "5", "6", "7"}, + "arm64": {}, + "mips": {"GOMIPS", "hardfloat", "softfloat"}, + "mips64": {"GOMIPS64", "hardfloat", "softfloat"}, + "ppc64": {}, + "ppc64le": {}, + "s390x": {}, + } +) + +// wantedAsmOpcode is a single asmcheck check +type wantedAsmOpcode struct { + fileline string // original source file/line (eg: "/path/foo.go:45") + line int // original source line + opcode *regexp.Regexp // opcode check to be performed on assembly output + negative bool // true if the check is supposed to fail rather than pass + found bool // true if the opcode check matched at least one in the output +} + +// A build environment triplet separated by slashes (eg: linux/386/sse2). +// The third field can be empty if the arch does not support variants (eg: "plan9/amd64/") +type buildEnv string + +// Environ returns the environment it represents in cmd.Environ() "key=val" format +// For instance, "linux/386/sse2".Environ() returns {"GOOS=linux", "GOARCH=386", "GO386=sse2"} +func (b buildEnv) Environ() []string { + fields := strings.Split(string(b), "/") + if len(fields) != 3 { + panic("invalid buildEnv string: " + string(b)) + } + env := []string{"GOOS=" + fields[0], "GOARCH=" + fields[1]} + if fields[2] != "" { + env = append(env, archVariants[fields[1]][0]+"="+fields[2]) + } + return env +} + +// asmChecks represents all the asmcheck checks present in a test file +// The outer map key is the build triplet in which the checks must be performed. +// The inner map key represent the source file line ("filename.go:1234") at which the +// checks must be performed. +type asmChecks map[buildEnv]map[string][]wantedAsmOpcode + +// Envs returns all the buildEnv in which at least one check is present +func (a asmChecks) Envs() []buildEnv { + var envs []buildEnv + for e := range a { + envs = append(envs, e) + } + sort.Slice(envs, func(i, j int) bool { + return string(envs[i]) < string(envs[j]) + }) + return envs +} + +func (t *test) wantedAsmOpcodes(fn string) asmChecks { + ops := make(asmChecks) + + comment := "" + src, _ := ioutil.ReadFile(fn) + for i, line := range strings.Split(string(src), "\n") { + matches := rxAsmComment.FindStringSubmatch(line) + code, cmt := matches[1], matches[2] + + // Keep comments pending in the comment variable until + // we find a line that contains some code. + comment += " " + cmt + if code == "" { + continue + } + + // Parse and extract any architecture check from comments, + // made by one architecture name and multiple checks. + lnum := fn + ":" + strconv.Itoa(i+1) + for _, ac := range rxAsmPlatform.FindAllStringSubmatch(comment, -1) { + archspec, allchecks := ac[1:4], ac[4] + + var arch, subarch, os string + switch { + case archspec[2] != "": // 3 components: "linux/386/sse2" + os, arch, subarch = archspec[0], archspec[1][1:], archspec[2][1:] + case archspec[1] != "": // 2 components: "386/sse2" + os, arch, subarch = "linux", archspec[0], archspec[1][1:] + default: // 1 component: "386" + os, arch, subarch = "linux", archspec[0], "" + } + + if _, ok := archVariants[arch]; !ok { + log.Fatalf("%s:%d: unsupported architecture: %v", t.goFileName(), i+1, arch) + } + + // Create the build environments corresponding the above specifiers + envs := make([]buildEnv, 0, 4) + if subarch != "" { + envs = append(envs, buildEnv(os+"/"+arch+"/"+subarch)) + } else { + subarchs := archVariants[arch] + if len(subarchs) == 0 { + envs = append(envs, buildEnv(os+"/"+arch+"/")) + } else { + for _, sa := range archVariants[arch][1:] { + envs = append(envs, buildEnv(os+"/"+arch+"/"+sa)) + } + } + } + + for _, m := range rxAsmCheck.FindAllString(allchecks, -1) { + negative := false + if m[0] == '-' { + negative = true + m = m[1:] + } + + rxsrc, err := strconv.Unquote(m) + if err != nil { + log.Fatalf("%s:%d: error unquoting string: %v", t.goFileName(), i+1, err) + } + + // Compile the checks as regular expressions. Notice that we + // consider checks as matching from the beginning of the actual + // assembler source (that is, what is left on each line of the + // compile -S output after we strip file/line info) to avoid + // trivial bugs such as "ADD" matching "FADD". This + // doesn't remove genericity: it's still possible to write + // something like "F?ADD", but we make common cases simpler + // to get right. + oprx, err := regexp.Compile("^" + rxsrc) + if err != nil { + log.Fatalf("%s:%d: %v", t.goFileName(), i+1, err) + } + + for _, env := range envs { + if ops[env] == nil { + ops[env] = make(map[string][]wantedAsmOpcode) + } + ops[env][lnum] = append(ops[env][lnum], wantedAsmOpcode{ + negative: negative, + fileline: lnum, + line: i + 1, + opcode: oprx, + }) + } + } + } + comment = "" + } + + return ops +} + +func (t *test) asmCheck(outStr string, fn string, env buildEnv, fullops map[string][]wantedAsmOpcode) (err error) { + // The assembly output contains the concatenated dump of multiple functions. + // the first line of each function begins at column 0, while the rest is + // indented by a tabulation. These data structures help us index the + // output by function. + functionMarkers := make([]int, 1) + lineFuncMap := make(map[string]int) + + lines := strings.Split(outStr, "\n") + rxLine := regexp.MustCompile(fmt.Sprintf(`\((%s:\d+)\)\s+(.*)`, regexp.QuoteMeta(fn))) + + for nl, line := range lines { + // Check if this line begins a function + if len(line) > 0 && line[0] != '\t' { + functionMarkers = append(functionMarkers, nl) + } + + // Search if this line contains a assembly opcode (which is prefixed by the + // original source file/line in parenthesis) + matches := rxLine.FindStringSubmatch(line) + if len(matches) == 0 { + continue + } + srcFileLine, asm := matches[1], matches[2] + + // Associate the original file/line information to the current + // function in the output; it will be useful to dump it in case + // of error. + lineFuncMap[srcFileLine] = len(functionMarkers) - 1 + + // If there are opcode checks associated to this source file/line, + // run the checks. + if ops, found := fullops[srcFileLine]; found { + for i := range ops { + if !ops[i].found && ops[i].opcode.FindString(asm) != "" { + ops[i].found = true + } + } + } + } + functionMarkers = append(functionMarkers, len(lines)) + + var failed []wantedAsmOpcode + for _, ops := range fullops { + for _, o := range ops { + // There's a failure if a negative match was found, + // or a positive match was not found. + if o.negative == o.found { + failed = append(failed, o) + } + } + } + if len(failed) == 0 { + return + } + + // At least one asmcheck failed; report them + sort.Slice(failed, func(i, j int) bool { + return failed[i].line < failed[j].line + }) + + lastFunction := -1 + var errbuf bytes.Buffer + fmt.Fprintln(&errbuf) + for _, o := range failed { + // Dump the function in which this opcode check was supposed to + // pass but failed. + funcIdx := lineFuncMap[o.fileline] + if funcIdx != 0 && funcIdx != lastFunction { + funcLines := lines[functionMarkers[funcIdx]:functionMarkers[funcIdx+1]] + log.Println(strings.Join(funcLines, "\n")) + lastFunction = funcIdx // avoid printing same function twice + } + + if o.negative { + fmt.Fprintf(&errbuf, "%s:%d: %s: wrong opcode found: %q\n", t.goFileName(), o.line, env, o.opcode.String()) + } else { + fmt.Fprintf(&errbuf, "%s:%d: %s: opcode not found: %q\n", t.goFileName(), o.line, env, o.opcode.String()) + } + } + err = errors.New(errbuf.String()) + return +} + // defaultRunOutputLimit returns the number of runoutput tests that // can be executed in parallel. func defaultRunOutputLimit() int { diff --git a/test/sigchld.go b/test/sigchld.go index 38437e552..3b4960640 100644 --- a/test/sigchld.go +++ b/test/sigchld.go @@ -1,5 +1,5 @@ // +build !plan9,!windows -// cmpout +// run // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/sinit_run.go b/test/sinit_run.go index c9afd3b77..fdd19c492 100644 --- a/test/sinit_run.go +++ b/test/sinit_run.go @@ -1,4 +1,4 @@ -// +build !nacl +// +build !nacl,!js // run // Copyright 2014 The Go Authors. All rights reserved. diff --git a/test/sliceopt.go b/test/sliceopt.go index eb24701f3..b8b947229 100644 --- a/test/sliceopt.go +++ b/test/sliceopt.go @@ -1,4 +1,4 @@ -// errorcheck -0 -d=append,slice,ssa/prove/debug=1 +// errorcheck -0 -d=append,slice // Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -21,51 +21,12 @@ func a3(x *[]int, y int) { *x = append(*x, y) // ERROR "append: len-only update$" } -// s1_if_false_then_anything -func s1_if_false_then_anything(x **[]int, xs **string, i, j int) { - z := (**x)[0:i] - z = z[i : i+1] - println(z) // if we get here, then we have proven that i==i+1 (this cannot happen, but the program is still being analyzed...) - - zs := (**xs)[0:i] // since i=i+1 is proven, i+1 is "in bounds", ha-ha - zs = zs[i : i+1] // ERROR "Proved boolean IsSliceInBounds$" - println(zs) -} - func s1(x **[]int, xs **string, i, j int) { var z []int - z = (**x)[2:] - z = (**x)[2:len(**x)] // ERROR "Proved boolean IsSliceInBounds$" - z = (**x)[2:cap(**x)] // ERROR "Proved IsSliceInBounds$" - z = (**x)[i:i] // -ERROR "Proved IsSliceInBounds" - z = (**x)[1:i:i] // ERROR "Proved boolean IsSliceInBounds$" - z = (**x)[i:j:0] - z = (**x)[i:0:j] // ERROR "Disproved IsSliceInBounds$" - z = (**x)[0:i:j] // ERROR "Proved boolean IsSliceInBounds$" - z = (**x)[0:] // ERROR "slice: omit slice operation$" - z = (**x)[2:8] // ERROR "Proved slicemask not needed$" - println(z) - z = (**x)[2:2] - z = (**x)[0:i] - z = (**x)[2:i:8] // ERROR "Disproved IsSliceInBounds$" "Proved IsSliceInBounds$" - z = (**x)[i:2:i] // ERROR "Proved IsSliceInBounds$" "Proved boolean IsSliceInBounds$" - - z = z[0:i] // ERROR "Proved boolean IsSliceInBounds" - z = z[0:i : i+1] - z = z[i : i+1] // ERROR "Proved boolean IsSliceInBounds$" - + z = (**x)[0:] // ERROR "slice: omit slice operation$" println(z) var zs string - zs = (**xs)[2:] - zs = (**xs)[2:len(**xs)] // ERROR "Proved IsSliceInBounds$" "Proved boolean IsSliceInBounds$" - zs = (**xs)[i:i] // -ERROR "Proved boolean IsSliceInBounds" - zs = (**xs)[0:] // ERROR "slice: omit slice operation$" - zs = (**xs)[2:8] - zs = (**xs)[2:2] // ERROR "Proved boolean IsSliceInBounds$" - zs = (**xs)[0:i] // ERROR "Proved boolean IsSliceInBounds$" - - zs = zs[0:i] // See s1_if_false_then_anything above to explain the counterfactual bounds check result below - zs = zs[i : i+1] // ERROR "Proved boolean IsSliceInBounds$" + zs = (**xs)[0:] // ERROR "slice: omit slice operation$" println(zs) } diff --git a/test/strcopy.go b/test/strcopy.go new file mode 100644 index 000000000..6d32baeec --- /dev/null +++ b/test/strcopy.go @@ -0,0 +1,29 @@ +// run + +// Copyright 2018 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 string([]byte(string)) makes a copy and doesn't reduce to +// nothing. (Issue 25834) + +package main + +import ( + "reflect" + "unsafe" +) + +func main() { + var ( + buf = make([]byte, 2<<10) + large = string(buf) + sub = large[10:12] + subcopy = string([]byte(sub)) + subh = *(*reflect.StringHeader)(unsafe.Pointer(&sub)) + subcopyh = *(*reflect.StringHeader)(unsafe.Pointer(&subcopy)) + ) + if subh.Data == subcopyh.Data { + panic("sub and subcopy have the same underlying array") + } +} diff --git a/test/syntax/typesw.go b/test/syntax/typesw.go index 8d89860d1..f9120e885 100644 --- a/test/syntax/typesw.go +++ b/test/syntax/typesw.go @@ -7,7 +7,7 @@ package main func main() { - switch main() := interface{}(nil).(type) { // ERROR "invalid variable name" + switch main() := interface{}(nil).(type) { // ERROR "invalid variable name|used as value" default: } } diff --git a/test/uintptrescapes2.go b/test/uintptrescapes2.go index 57c21edbc..c94bc148c 100644 --- a/test/uintptrescapes2.go +++ b/test/uintptrescapes2.go @@ -20,12 +20,24 @@ func F1(a uintptr) {} // ERROR "escaping uintptr" //go:noinline func F2(a ...uintptr) {} // ERROR "escaping ...uintptr" "a does not escape" +//go:uintptrescapes +//go:noinline +func F3(uintptr) {} // ERROR "escaping uintptr" + +//go:uintptrescapes +//go:noinline +func F4(...uintptr) {} // ERROR "escaping ...uintptr" + func G() { - var t int // ERROR "moved to heap" - F1(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to F1: .?autotmp" "&t escapes to heap" + var t int // ERROR "moved to heap" + F1(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to F1: .?autotmp" "&t escapes to heap" + var t2 int // ERROR "moved to heap" + F3(uintptr(unsafe.Pointer(&t2))) // ERROR "live at call to F3: .?autotmp" "&t2 escapes to heap" } func H() { - var v int // ERROR "moved to heap" - F2(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to newobject: .?autotmp" "live at call to F2: .?autotmp" "escapes to heap" + var v int // ERROR "moved to heap" + F2(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to newobject: .?autotmp" "live at call to F2: .?autotmp" "escapes to heap" + var v2 int // ERROR "moved to heap" + F4(0, 1, uintptr(unsafe.Pointer(&v2)), 2) // ERROR "live at call to newobject: .?autotmp" "live at call to F4: .?autotmp" "escapes to heap" } |