diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE new file mode 100644 index 0000000000..f44c6b9a36 --- /dev/null +++ b/.github/ISSUE_TEMPLATE @@ -0,0 +1,18 @@ +Please answer these questions before submitting your issue. Thanks! + +1. What version of Go are you using (`go version`)? + + +2. What operating system and processor architecture are you using (`go env`)? + + +3. What did you do? +(Use play.golang.org to provide a runnable example, if possible.) + + +4. What did you expect to see? + + +5. What did you see instead? + + diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE new file mode 100644 index 0000000000..00014e9b86 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE @@ -0,0 +1,7 @@ +Please do not send pull requests to the golang/* repositories. + +We do, however, take contributions gladly. + +See https://golang.org/doc/contribute.html + +Thanks! diff --git a/.gitignore b/.gitignore index de48481bf1..1677a18fc8 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ src/cmd/**/y.output src/cmd/cgo/zdefaultcc.go src/cmd/go/zdefaultcc.go src/cmd/internal/obj/zbootstrap.go +src/go/build/zcgo.go src/go/doc/headscan src/runtime/internal/sys/zversion.go src/unicode/maketables diff --git a/doc/go1.7.txt b/doc/go1.7.txt index 15efa287ce..88f22b2df1 100644 --- a/doc/go1.7.txt +++ b/doc/go1.7.txt @@ -1,14 +1,20 @@ Tools: +cmd/dist: add list subcommand to list all supported platforms (CL 19837) cmd/go: GO15VENDOREXPERIMENT gone, assumed on (CL 19615) cmd/link: "-X name value" form gone (CL 19614) +cmd/compile: smaller binaries (many CLs) +cmd/go, go/build: add support for Fortran (CL 19670, CL 4114) +cmd/dist: introduce list subcommand to list all supported platforms (CL 19837) Ports: -SOMETHING WILL HAPPEN +We now require OpenBSD 5.6+ (CL 18219, crypto/rand using getentropy) +plan9/arm support? Start at least. API additions and behavior changes: -SOMETHING WILL HAPPEN - - +runtime: add CallerFrames and Frames (CL 19869) +testing/quick: now generates nil values (CL 16470) +net/url: support query string without values (CL 19931) +net/textproto: permit all valid token chars in CanonicalMIMEHeaderKey input (CL 18725) diff --git a/doc/install-source.html b/doc/install-source.html index 332c72097e..b8cd8dbcfc 100644 --- a/doc/install-source.html +++ b/doc/install-source.html @@ -445,6 +445,9 @@ The valid combinations of $GOOS and $GOARCH are: $GOOS $GOARCH +android arm + + darwin 386 diff --git a/misc/cgo/errors/ptr.go b/misc/cgo/errors/ptr.go index 834cde9199..bbcaaabecb 100644 --- a/misc/cgo/errors/ptr.go +++ b/misc/cgo/errors/ptr.go @@ -122,6 +122,16 @@ var ptrTests = []ptrTest{ body: `i := 0; p := &S{p:&i}; s := p.a[:]; C.f(unsafe.Pointer(&s[0]))`, fail: false, }, + { + // Passing the address of a slice of an array that is + // an element in a struct, with a type conversion. + name: "slice-ok-4", + c: `typedef void* PV; void f(PV p) {}`, + imports: []string{"unsafe"}, + support: `type S struct { p *int; a [4]byte }`, + body: `i := 0; p := &S{p:&i}; C.f(C.PV(unsafe.Pointer(&p.a[0])))`, + fail: false, + }, { // Passing the address of a static variable with no // pointers doesn't matter. diff --git a/misc/cgo/fortran/answer.f90 b/misc/cgo/fortran/answer.f90 new file mode 100644 index 0000000000..6b29d78da1 --- /dev/null +++ b/misc/cgo/fortran/answer.f90 @@ -0,0 +1,9 @@ +! 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. + +function the_answer() result(j) bind(C) + use iso_c_binding, only: c_int + integer(c_int) :: j ! output + j = 42 +end function the_answer diff --git a/misc/cgo/fortran/fortran.go b/misc/cgo/fortran/fortran.go new file mode 100644 index 0000000000..8d008b48c8 --- /dev/null +++ b/misc/cgo/fortran/fortran.go @@ -0,0 +1,12 @@ +// 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 fortran + +// int the_answer(); +import "C" + +func TheAnswer() int { + return int(C.the_answer()) +} diff --git a/misc/cgo/fortran/fortran_test.go b/misc/cgo/fortran/fortran_test.go new file mode 100644 index 0000000000..a7ba64850a --- /dev/null +++ b/misc/cgo/fortran/fortran_test.go @@ -0,0 +1,13 @@ +// 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 fortran + +import "testing" + +func TestFortran(t *testing.T) { + if a := TheAnswer(); a != 42 { + t.Errorf("Unexpected result for The Answer. Got: %d Want: 42", a) + } +} diff --git a/misc/cgo/fortran/helloworld/helloworld.f90 b/misc/cgo/fortran/helloworld/helloworld.f90 new file mode 100644 index 0000000000..cbc34c16ef --- /dev/null +++ b/misc/cgo/fortran/helloworld/helloworld.f90 @@ -0,0 +1,3 @@ + program HelloWorldF90 + write(*,*) "Hello World!" + end program HelloWorldF90 diff --git a/misc/cgo/fortran/test.bash b/misc/cgo/fortran/test.bash new file mode 100755 index 0000000000..9350dbe2d6 --- /dev/null +++ b/misc/cgo/fortran/test.bash @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# 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. + +# This directory is intended to test the use of Fortran with cgo. + +set -e + +FC=$1 + +if ! $FC helloworld/helloworld.f90 -o main.exe >& /dev/null; then + echo "skipping Fortran test: could not build helloworld.f90 with $FC" + exit 0 +fi + +if ! go test; then + echo "FAIL: go test" + status=1 +fi + +exit $status diff --git a/misc/cgo/testcshared/test.bash b/misc/cgo/testcshared/test.bash index 23c97675bb..c5c49a10e1 100755 --- a/misc/cgo/testcshared/test.bash +++ b/misc/cgo/testcshared/test.bash @@ -10,7 +10,7 @@ set -e if [ ! -f src/libgo/libgo.go ]; then cwd=$(pwd) - echo 'misc/cgo/testcshared/test.bash is running in $cwd' 1>&2 + echo "misc/cgo/testcshared/test.bash is running in $cwd" 1>&2 exit 1 fi diff --git a/misc/git/pre-commit b/misc/git/pre-commit index 18b7f832f3..242159f04a 100755 --- a/misc/git/pre-commit +++ b/misc/git/pre-commit @@ -10,7 +10,7 @@ # # This script does not handle file names that contain spaces. -gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$') +gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '\.go$') [ -z "$gofiles" ] && exit 0 unformatted=$(gofmt -l $gofiles) diff --git a/src/bufio/bufio.go b/src/bufio/bufio.go index 6a70f7034d..33470ccd41 100644 --- a/src/bufio/bufio.go +++ b/src/bufio/bufio.go @@ -705,7 +705,7 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) { } } if err == io.EOF { - // If we filled the buffer exactly, flush pre-emptively. + // If we filled the buffer exactly, flush preemptively. if b.Available() == 0 { err = b.flush() } else { diff --git a/src/bufio/scan_test.go b/src/bufio/scan_test.go index 07b1a56dc0..d64d0b45d7 100644 --- a/src/bufio/scan_test.go +++ b/src/bufio/scan_test.go @@ -351,7 +351,7 @@ func TestSplitError(t *testing.T) { // Test that an EOF is overridden by a user-generated scan error. func TestErrAtEOF(t *testing.T) { s := NewScanner(strings.NewReader("1 2 33")) - // This spitter will fail on last entry, after s.err==EOF. + // This splitter will fail on last entry, after s.err==EOF. split := func(data []byte, atEOF bool) (advance int, token []byte, err error) { advance, token, err = ScanWords(data, atEOF) if len(token) > 1 { diff --git a/src/buildall.bash b/src/buildall.bash index f686dd8996..a322fe537a 100755 --- a/src/buildall.bash +++ b/src/buildall.bash @@ -32,13 +32,13 @@ if [ "$pattern" = "" ]; then pattern=. fi -# put linux, nacl first in the target list to get all the architectures up front. -targets="$((ls runtime | sed -n 's/^rt0_\(.*\)_\(.*\)\.s/\1-\2/p'; echo linux-386-387 linux-arm-arm5) | sort | sed -e 's|linux-mips64x|linux-mips64 linux-mips64le|' | egrep -v android-arm | egrep "$pattern" | egrep 'linux|nacl') -$(ls runtime | sed -n 's/^rt0_\(.*\)_\(.*\)\.s/\1-\2/p' | egrep -v 'android-arm|darwin-arm' | egrep "$pattern" | egrep -v 'linux|nacl')" - ./make.bash || exit 1 GOROOT="$(cd .. && pwd)" +# put linux, nacl first in the target list to get all the architectures up front. +targets="$((../bin/go tool dist list | sed -n 's/^\(.*\)\/\(.*\)/\1-\2/p'; echo linux-386-387 linux-arm-arm5) | sort | egrep -v android-arm | egrep "$pattern" | egrep 'linux|nacl') +$(../bin/go tool dist list | sed -n 's/^\(.*\)\/\(.*\)/\1-\2/p' | egrep -v 'android-arm|darwin-arm' | egrep "$pattern" | egrep -v 'linux|nacl')" + failed=false for target in $targets do diff --git a/src/bytes/bytes_test.go b/src/bytes/bytes_test.go index 8df62fcc6a..a412dc89b9 100644 --- a/src/bytes/bytes_test.go +++ b/src/bytes/bytes_test.go @@ -335,6 +335,41 @@ func TestIndexByteBig(t *testing.T) { } } +// test a small index across all page offsets +func TestIndexByteSmall(t *testing.T) { + b := make([]byte, 5015) // bigger than a page + // Make sure we find the correct byte even when straddling a page. + for i := 0; i <= len(b)-15; i++ { + for j := 0; j < 15; j++ { + b[i+j] = byte(100 + j) + } + for j := 0; j < 15; j++ { + p := IndexByte(b[i:i+15], byte(100+j)) + if p != j { + t.Errorf("IndexByte(%q, %d) = %d", b[i:i+15], 100+j, p) + } + } + for j := 0; j < 15; j++ { + b[i+j] = 0 + } + } + // Make sure matches outside the slice never trigger. + for i := 0; i <= len(b)-15; i++ { + for j := 0; j < 15; j++ { + b[i+j] = 1 + } + for j := 0; j < 15; j++ { + p := IndexByte(b[i:i+15], byte(0)) + if p != -1 { + t.Errorf("IndexByte(%q, %d) = %d", b[i:i+15], 0, p) + } + } + for j := 0; j < 15; j++ { + b[i+j] = 0 + } + } +} + func TestIndexRune(t *testing.T) { for _, tt := range indexRuneTests { a := []byte(tt.a) @@ -348,10 +383,12 @@ func TestIndexRune(t *testing.T) { var bmbuf []byte +func BenchmarkIndexByte10(b *testing.B) { bmIndexByte(b, IndexByte, 10) } func BenchmarkIndexByte32(b *testing.B) { bmIndexByte(b, IndexByte, 32) } func BenchmarkIndexByte4K(b *testing.B) { bmIndexByte(b, IndexByte, 4<<10) } func BenchmarkIndexByte4M(b *testing.B) { bmIndexByte(b, IndexByte, 4<<20) } func BenchmarkIndexByte64M(b *testing.B) { bmIndexByte(b, IndexByte, 64<<20) } +func BenchmarkIndexBytePortable10(b *testing.B) { bmIndexByte(b, IndexBytePortable, 10) } func BenchmarkIndexBytePortable32(b *testing.B) { bmIndexByte(b, IndexBytePortable, 32) } func BenchmarkIndexBytePortable4K(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<10) } func BenchmarkIndexBytePortable4M(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<20) } diff --git a/src/cmd/asm/doc.go b/src/cmd/asm/doc.go index a9c8bfb7db..aa62147957 100644 --- a/src/cmd/asm/doc.go +++ b/src/cmd/asm/doc.go @@ -20,7 +20,7 @@ The GOOS and GOARCH environment variables set the desired target. Flags: -D value - predefined symbol with optional simple value -D=identifer=value; + predefined symbol with optional simple value -D=identifier=value; can be set multiple times -I value include directory; can be set multiple times diff --git a/src/cmd/asm/internal/asm/endtoend_test.go b/src/cmd/asm/internal/asm/endtoend_test.go index 4bc7e2fb74..4a3e0ee265 100644 --- a/src/cmd/asm/internal/asm/endtoend_test.go +++ b/src/cmd/asm/internal/asm/endtoend_test.go @@ -179,7 +179,7 @@ Diff: t.Errorf(format, args...) ok = false } - obj.Flushplist(ctxt) + obj.FlushplistNoFree(ctxt) for p := top; p != nil; p = p.Link { if p.As == obj.ATEXT { diff --git a/src/cmd/asm/internal/asm/parse.go b/src/cmd/asm/internal/asm/parse.go index 4258c5ce26..6c324ce3af 100644 --- a/src/cmd/asm/internal/asm/parse.go +++ b/src/cmd/asm/internal/asm/parse.go @@ -297,7 +297,7 @@ func (p *Parser) operand(a *obj.Addr) bool { p.errorf("illegal use of register list") } p.registerList(a) - p.expect(scanner.EOF) + p.expectOperandEnd() return true } @@ -331,7 +331,7 @@ func (p *Parser) operand(a *obj.Addr) bool { } } // fmt.Printf("REG %s\n", obj.Dconv(&emptyProg, 0, a)) - p.expect(scanner.EOF) + p.expectOperandEnd() return true } @@ -363,7 +363,7 @@ func (p *Parser) operand(a *obj.Addr) bool { a.Type = obj.TYPE_FCONST a.Val = p.floatExpr() // fmt.Printf("FCONST %s\n", obj.Dconv(&emptyProg, 0, a)) - p.expect(scanner.EOF) + p.expectOperandEnd() return true } if p.have(scanner.String) { @@ -378,7 +378,7 @@ func (p *Parser) operand(a *obj.Addr) bool { a.Type = obj.TYPE_SCONST a.Val = str // fmt.Printf("SCONST %s\n", obj.Dconv(&emptyProg, 0, a)) - p.expect(scanner.EOF) + p.expectOperandEnd() return true } a.Offset = int64(p.expr()) @@ -392,7 +392,7 @@ func (p *Parser) operand(a *obj.Addr) bool { a.Type = obj.TYPE_MEM } // fmt.Printf("CONST %d %s\n", a.Offset, obj.Dconv(&emptyProg, 0, a)) - p.expect(scanner.EOF) + p.expectOperandEnd() return true } // fmt.Printf("offset %d \n", a.Offset) @@ -402,7 +402,7 @@ func (p *Parser) operand(a *obj.Addr) bool { p.registerIndirect(a, prefix) // fmt.Printf("DONE %s\n", p.arch.Dconv(&emptyProg, 0, a)) - p.expect(scanner.EOF) + p.expectOperandEnd() return true } @@ -983,14 +983,19 @@ func (p *Parser) more() bool { // get verifies that the next item has the expected type and returns it. func (p *Parser) get(expected lex.ScanToken) lex.Token { - p.expect(expected) + p.expect(expected, expected.String()) return p.next() } +// expectOperandEnd verifies that the parsing state is properly at the end of an operand. +func (p *Parser) expectOperandEnd() { + p.expect(scanner.EOF, "end of operand") +} + // expect verifies that the next item has the expected type. It does not consume it. -func (p *Parser) expect(expected lex.ScanToken) { - if p.peek() != expected { - p.errorf("expected %s, found %s", expected, p.next()) +func (p *Parser) expect(expectedToken lex.ScanToken, expectedMessage string) { + if p.peek() != expectedToken { + p.errorf("expected %s, found %s", expectedMessage, p.next()) } } diff --git a/src/cmd/asm/internal/asm/pseudo_test.go b/src/cmd/asm/internal/asm/pseudo_test.go index ee13b724eb..2e6d6c8154 100644 --- a/src/cmd/asm/internal/asm/pseudo_test.go +++ b/src/cmd/asm/internal/asm/pseudo_test.go @@ -35,8 +35,8 @@ func TestErroneous(t *testing.T) { {"TEXT", "%", "expect two or three operands for TEXT"}, {"TEXT", "1, 1", "TEXT symbol \"\" must be a symbol(SB)"}, {"TEXT", "$\"foo\", 0, $1", "TEXT symbol \"\" must be a symbol(SB)"}, - {"TEXT", "$0É:0, 0, $1", "expected EOF, found É"}, // Issue #12467. - {"TEXT", "$:0:(SB, 0, $1", "expected '(', found 0"}, // Issue 12468. + {"TEXT", "$0É:0, 0, $1", "expected end of operand, found É"}, // Issue #12467. + {"TEXT", "$:0:(SB, 0, $1", "expected '(', found 0"}, // Issue 12468. {"FUNCDATA", "", "expect two operands for FUNCDATA"}, {"FUNCDATA", "(SB ", "expect two operands for FUNCDATA"}, {"DATA", "", "expect two operands for DATA"}, diff --git a/src/cmd/asm/internal/asm/testdata/arm64.s b/src/cmd/asm/internal/asm/testdata/arm64.s index 22d430631c..6466da7190 100644 --- a/src/cmd/asm/internal/asm/testdata/arm64.s +++ b/src/cmd/asm/internal/asm/testdata/arm64.s @@ -237,7 +237,7 @@ again: // // LSTXR reg ',' addr ',' reg // { -// outtcode($1, &$2, &$4, &$6); +// outcode($1, &$2, &$4, &$6); // } LDAXRW (R0), R2 STLXRW R1, (R0), R3 diff --git a/src/cmd/asm/internal/flags/flags.go b/src/cmd/asm/internal/flags/flags.go index 9f3c3bfc1d..4557c2a7f9 100644 --- a/src/cmd/asm/internal/flags/flags.go +++ b/src/cmd/asm/internal/flags/flags.go @@ -29,7 +29,7 @@ var ( ) func init() { - flag.Var(&D, "D", "predefined symbol with optional simple value -D=identifer=value; can be set multiple times") + flag.Var(&D, "D", "predefined symbol with optional simple value -D=identifier=value; can be set multiple times") flag.Var(&I, "I", "include directory; can be set multiple times") } diff --git a/src/cmd/asm/internal/lex/input.go b/src/cmd/asm/internal/lex/input.go index 33b9d8adea..4855daa892 100644 --- a/src/cmd/asm/internal/lex/input.go +++ b/src/cmd/asm/internal/lex/input.go @@ -30,7 +30,7 @@ type Input struct { peekText string } -// NewInput returns a +// NewInput returns an Input from the given path. func NewInput(name string) *Input { return &Input{ // include directories: look in source dir, then -I directories. diff --git a/src/cmd/asm/internal/lex/lex.go b/src/cmd/asm/internal/lex/lex.go index 6fce55f7f4..81339059b1 100644 --- a/src/cmd/asm/internal/lex/lex.go +++ b/src/cmd/asm/internal/lex/lex.go @@ -77,7 +77,7 @@ func NewLexer(name string, ctxt *obj.Link) TokenReader { input := NewInput(name) fd, err := os.Open(name) if err != nil { - log.Fatalf("asm: %s\n", err) + log.Fatalf("%s\n", err) } input.Push(NewTokenizer(name, fd, fd)) return input diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go index f48050c137..4e450bec98 100644 --- a/src/cmd/asm/main.go +++ b/src/cmd/asm/main.go @@ -26,7 +26,7 @@ func main() { architecture := arch.Set(GOARCH) if architecture == nil { - log.Fatalf("asm: unrecognized architecture %s", GOARCH) + log.Fatalf("unrecognized architecture %s", GOARCH) } flags.Parse() @@ -66,7 +66,7 @@ func main() { obj.Writeobjdirect(ctxt, output) } if !ok || diag { - log.Printf("asm: assembly of %s failed", flag.Arg(0)) + log.Printf("assembly of %s failed", flag.Arg(0)) os.Remove(*flags.OutputFile) os.Exit(1) } diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index 90c2584c7f..58be391573 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -31,9 +31,9 @@ See $GOROOT/misc/cgo/stdio and $GOROOT/misc/cgo/gmp for examples. See "C? Go? Cgo!" for an introduction to using cgo: https://golang.org/doc/articles/c_go_cgo.html. -CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS may be defined with pseudo #cgo -directives within these comments to tweak the behavior of the C or C++ -compiler. Values defined in multiple directives are concatenated +CFLAGS, CPPFLAGS, CXXFLAGS, FFLAGS and LDFLAGS may be defined with pseudo +#cgo directives within these comments to tweak the behavior of the C, C++ +or Fortran compiler. Values defined in multiple directives are concatenated together. The directive can include a list of build constraints limiting its effect to systems satisfying one of the constraints (see https://golang.org/pkg/go/build/#hdr-Build_Constraints for details about the constraint syntax). @@ -53,7 +53,7 @@ For example: // #include import "C" -When building, the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS and +When building, the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS and CGO_LDFLAGS environment variables are added to the flags derived from these directives. Package-specific flags should be set using the directives, not the environment variables, so that builds work in @@ -62,10 +62,11 @@ unmodified environments. All the cgo CPPFLAGS and CFLAGS directives in a package are concatenated and used to compile C files in that package. All the CPPFLAGS and CXXFLAGS directives in a package are concatenated and used to compile C++ files in that -package. All the LDFLAGS directives in any package in the program are -concatenated and used at link time. All the pkg-config directives are -concatenated and sent to pkg-config simultaneously to add to each appropriate -set of command-line flags. +package. All the CPPFLAGS and FFLAGS directives in a package are concatenated +and used to compile Fortran files in that package. All the LDFLAGS directives +in any package in the program are concatenated and used at link time. All the +pkg-config directives are concatenated and sent to pkg-config simultaneously +to add to each appropriate set of command-line flags. When the cgo directives are parsed, any occurrence of the string ${SRCDIR} will be replaced by the absolute path to the directory containing the source @@ -83,7 +84,8 @@ When the Go tool sees that one or more Go files use the special import "C", it will look for other non-Go files in the directory and compile them as part of the Go package. Any .c, .s, or .S files will be compiled with the C compiler. Any .cc, .cpp, or .cxx files will be -compiled with the C++ compiler. Any .h, .hh, .hpp, or .hxx files will +compiled with the C++ compiler. Any .f, .F, .for or .f90 files will be +compiled with the fortran compiler. Any .h, .hh, .hpp, or .hxx files will not be compiled separately, but, if these header files are changed, the C and C++ files will be recompiled. The default C and C++ compilers may be changed by the CC and CXX environment variables, diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 5bfdef785c..fe9af1769d 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -819,14 +819,17 @@ func (p *Package) hasSideEffects(f *File, x ast.Expr) bool { func (p *Package) isType(t ast.Expr) bool { switch t := t.(type) { case *ast.SelectorExpr: - if t.Sel.Name != "Pointer" { - return false - } id, ok := t.X.(*ast.Ident) if !ok { return false } - return id.Name == "unsafe" + if id.Name == "unsafe" && t.Sel.Name == "Pointer" { + return true + } + if id.Name == "C" && typedef["_Ctype_"+t.Sel.Name] != nil { + return true + } + return false case *ast.Ident: // TODO: This ignores shadowing. switch t.Name { diff --git a/src/cmd/compile/internal/amd64/ggen.go b/src/cmd/compile/internal/amd64/ggen.go index 84c3d5d7d4..55fb9e0a43 100644 --- a/src/cmd/compile/internal/amd64/ggen.go +++ b/src/cmd/compile/internal/amd64/ggen.go @@ -10,9 +10,10 @@ import ( "cmd/internal/obj/x86" ) -func defframe(ptxt *obj.Prog) { - var n *gc.Node +// no floating point in note handlers on Plan 9 +var isPlan9 = obj.Getgoos() == "plan9" +func defframe(ptxt *obj.Prog) { // fill in argument size, stack size ptxt.To.Type = obj.TYPE_TEXTSIZE @@ -31,8 +32,7 @@ func defframe(ptxt *obj.Prog) { x0 := uint32(0) // iterate through declarations - they are sorted in decreasing xoffset order. - for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next { - n = l.N + for _, n := range gc.Curfn.Func.Dcl { if !n.Name.Needzero { continue } @@ -126,7 +126,7 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32, x0 *uin *ax = 1 } p = appendpp(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo) - } else if cnt <= int64(8*gc.Widthreg) { + } else if !isPlan9 && cnt <= int64(8*gc.Widthreg) { if *x0 == 0 { p = appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0) *x0 = 1 @@ -139,12 +139,11 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32, x0 *uin if cnt%16 != 0 { p = appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo+cnt-int64(16)) } - } else if !gc.Nacl && (cnt <= int64(128*gc.Widthreg)) { + } else if !gc.Nacl && !isPlan9 && (cnt <= int64(128*gc.Widthreg)) { if *x0 == 0 { p = appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0) *x0 = 1 } - p = appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, frame+lo+dzDI(cnt), obj.TYPE_REG, x86.REG_DI, 0) p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, dzOff(cnt)) p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg)) @@ -563,7 +562,7 @@ func clearfat(nl *gc.Node) { w := nl.Type.Width - if w > 1024 || (gc.Nacl && w >= 64) { + if w > 1024 || (w >= 64 && (gc.Nacl || isPlan9)) { var oldn1 gc.Node var n1 gc.Node savex(x86.REG_DI, &n1, &oldn1, nil, gc.Types[gc.Tptr]) @@ -630,6 +629,22 @@ func clearfat(nl *gc.Node) { } func clearfat_tail(n1 *gc.Node, b int64) { + if b >= 16 && isPlan9 { + var z gc.Node + gc.Nodconst(&z, gc.Types[gc.TUINT64], 0) + q := b / 8 + for ; q > 0; q-- { + n1.Type = z.Type + gins(x86.AMOVQ, &z, n1) + n1.Xoffset += 8 + b -= 8 + } + if b != 0 { + n1.Xoffset -= 8 - b + gins(x86.AMOVQ, &z, n1) + } + return + } if b >= 16 { var vec_zero gc.Node gc.Regalloc(&vec_zero, gc.Types[gc.TFLOAT64], nil) diff --git a/src/cmd/compile/internal/amd64/peep.go b/src/cmd/compile/internal/amd64/peep.go index 452f954bd8..810214504f 100644 --- a/src/cmd/compile/internal/amd64/peep.go +++ b/src/cmd/compile/internal/amd64/peep.go @@ -427,7 +427,7 @@ func elimshortmov(g *gc.Graph) { } if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST { - // move or artihmetic into partial register. + // move or arithmetic into partial register. // from another register or constant can be movl. // we don't switch to 64-bit arithmetic if it can // change how the carry bit is set (and the carry bit is needed). diff --git a/src/cmd/compile/internal/arm/ggen.go b/src/cmd/compile/internal/arm/ggen.go index 517b4f4c8e..5e282c8cd5 100644 --- a/src/cmd/compile/internal/arm/ggen.go +++ b/src/cmd/compile/internal/arm/ggen.go @@ -11,8 +11,6 @@ import ( ) func defframe(ptxt *obj.Prog) { - var n *gc.Node - // fill in argument size, stack size ptxt.To.Type = obj.TYPE_TEXTSIZE @@ -28,8 +26,7 @@ func defframe(ptxt *obj.Prog) { hi := int64(0) lo := hi r0 := uint32(0) - for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next { - n = l.N + for _, n := range gc.Curfn.Func.Dcl { if !n.Name.Needzero { continue } diff --git a/src/cmd/compile/internal/arm64/ggen.go b/src/cmd/compile/internal/arm64/ggen.go index c495bbc77f..a33b2b42bf 100644 --- a/src/cmd/compile/internal/arm64/ggen.go +++ b/src/cmd/compile/internal/arm64/ggen.go @@ -12,8 +12,6 @@ import ( ) func defframe(ptxt *obj.Prog) { - var n *gc.Node - // fill in argument size, stack size ptxt.To.Type = obj.TYPE_TEXTSIZE @@ -37,8 +35,7 @@ func defframe(ptxt *obj.Prog) { lo := hi // iterate through declarations - they are sorted in decreasing xoffset order. - for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next { - n = l.N + for _, n := range gc.Curfn.Func.Dcl { if !n.Name.Needzero { continue } @@ -146,7 +143,7 @@ func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) { // The hardware will generate undefined result. // Also need to explicitly trap on division on zero, // the hardware will silently generate undefined result. - // DIVW will leave unpredicable result in higher 32-bit, + // DIVW will leave unpredictable result in higher 32-bit, // so always use DIVD/DIVDU. t := nl.Type diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go new file mode 100644 index 0000000000..c993737598 --- /dev/null +++ b/src/cmd/compile/internal/gc/alg.go @@ -0,0 +1,626 @@ +// 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 gc + +import "fmt" + +const ( + // These values are known by runtime. + ANOEQ = iota + AMEM0 + AMEM8 + AMEM16 + AMEM32 + AMEM64 + AMEM128 + ASTRING + AINTER + ANILINTER + AFLOAT32 + AFLOAT64 + ACPLX64 + ACPLX128 + AMEM = 100 +) + +func algtype(t *Type) int { + a := algtype1(t, nil) + if a == AMEM { + switch t.Width { + case 0: + return AMEM0 + case 1: + return AMEM8 + case 2: + return AMEM16 + case 4: + return AMEM32 + case 8: + return AMEM64 + case 16: + return AMEM128 + } + } + + return a +} + +func algtype1(t *Type, bad **Type) int { + if bad != nil { + *bad = nil + } + if t.Broke { + return AMEM + } + if t.Noalg { + return ANOEQ + } + + switch t.Etype { + // will be defined later. + case TANY, TFORW: + *bad = t + + return -1 + + case TINT8, + TUINT8, + TINT16, + TUINT16, + TINT32, + TUINT32, + TINT64, + TUINT64, + TINT, + TUINT, + TUINTPTR, + TBOOL, + TPTR32, + TPTR64, + TCHAN, + TUNSAFEPTR: + return AMEM + + case TFUNC, TMAP: + if bad != nil { + *bad = t + } + return ANOEQ + + case TFLOAT32: + return AFLOAT32 + + case TFLOAT64: + return AFLOAT64 + + case TCOMPLEX64: + return ACPLX64 + + case TCOMPLEX128: + return ACPLX128 + + case TSTRING: + return ASTRING + + case TINTER: + if isnilinter(t) { + return ANILINTER + } + return AINTER + + case TARRAY: + if Isslice(t) { + if bad != nil { + *bad = t + } + return ANOEQ + } + + a := algtype1(t.Type, bad) + if a == ANOEQ || a == AMEM { + if a == ANOEQ && bad != nil { + *bad = t + } + return a + } + + switch t.Bound { + case 0: + // We checked above that the element type is comparable. + return AMEM + case 1: + // Single-element array is same as its lone element. + return a + } + + return -1 // needs special compare + + case TSTRUCT: + if t.Type != nil && t.Type.Down == nil && !isblanksym(t.Type.Sym) { + // One-field struct is same as that one field alone. + return algtype1(t.Type.Type, bad) + } + + ret := AMEM + var a int + for t1 := t.Type; t1 != nil; t1 = t1.Down { + // All fields must be comparable. + a = algtype1(t1.Type, bad) + + if a == ANOEQ { + return ANOEQ + } + + // Blank fields, padded fields, fields with non-memory + // equality need special compare. + if a != AMEM || isblanksym(t1.Sym) || ispaddedfield(t1, t.Width) { + ret = -1 + continue + } + } + + return ret + } + + Fatalf("algtype1: unexpected type %v", t) + return 0 +} + +// Generate a helper function to compute the hash of a value of type t. +func genhash(sym *Sym, t *Type) { + if Debug['r'] != 0 { + fmt.Printf("genhash %v %v\n", sym, t) + } + + lineno = 1 // less confusing than end of input + dclcontext = PEXTERN + markdcl() + + // func sym(p *T, h uintptr) uintptr + fn := Nod(ODCLFUNC, nil, nil) + + fn.Func.Nname = newname(sym) + fn.Func.Nname.Class = PFUNC + tfn := Nod(OTFUNC, nil, nil) + fn.Func.Nname.Name.Param.Ntype = tfn + + n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t))) + tfn.List = list(tfn.List, n) + np := n.Left + n = Nod(ODCLFIELD, newname(Lookup("h")), typenod(Types[TUINTPTR])) + tfn.List = list(tfn.List, n) + nh := n.Left + n = Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])) // return value + tfn.Rlist = list(tfn.Rlist, n) + + funchdr(fn) + typecheck(&fn.Func.Nname.Name.Param.Ntype, Etype) + + // genhash is only called for types that have equality but + // cannot be handled by the standard algorithms, + // so t must be either an array or a struct. + switch t.Etype { + default: + Fatalf("genhash %v", t) + + case TARRAY: + if Isslice(t) { + Fatalf("genhash %v", t) + } + + // An array of pure memory would be handled by the + // standard algorithm, so the element type must not be + // pure memory. + hashel := hashfor(t.Type) + + n := Nod(ORANGE, nil, Nod(OIND, np, nil)) + ni := newname(Lookup("i")) + ni.Type = Types[TINT] + n.List = list1(ni) + n.Colas = true + colasdefn(n.List, n) + ni = n.List.N + + // h = hashel(&p[i], h) + call := Nod(OCALL, hashel, nil) + + nx := Nod(OINDEX, np, ni) + nx.Bounded = true + na := Nod(OADDR, nx, nil) + na.Etype = 1 // no escape to heap + call.List = list(call.List, na) + call.List = list(call.List, nh) + n.Nbody = list(n.Nbody, Nod(OAS, nh, call)) + + fn.Nbody = list(fn.Nbody, n) + + // Walk the struct using memhash for runs of AMEM + // and calling specific hash functions for the others. + case TSTRUCT: + var call *Node + var nx *Node + var na *Node + var hashel *Node + + t1 := t.Type + for { + first, size, next := memrun(t, t1) + t1 = next + + // Run memhash for fields up to this one. + if first != nil { + hashel = hashmem(first.Type) + + // h = hashel(&p.first, size, h) + call = Nod(OCALL, hashel, nil) + + nx = Nod(OXDOT, np, newname(first.Sym)) // TODO: fields from other packages? + na = Nod(OADDR, nx, nil) + na.Etype = 1 // no escape to heap + call.List = list(call.List, na) + call.List = list(call.List, nh) + call.List = list(call.List, Nodintconst(size)) + fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call)) + } + + if t1 == nil { + break + } + if isblanksym(t1.Sym) { + t1 = t1.Down + continue + } + if algtype1(t1.Type, nil) == AMEM { + // Our memory run might have been stopped by padding or a blank field. + // If the next field is memory-ish, it could be the start of a new run. + continue + } + + hashel = hashfor(t1.Type) + call = Nod(OCALL, hashel, nil) + nx = Nod(OXDOT, np, newname(t1.Sym)) // TODO: fields from other packages? + na = Nod(OADDR, nx, nil) + na.Etype = 1 // no escape to heap + call.List = list(call.List, na) + call.List = list(call.List, nh) + fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call)) + + t1 = t1.Down + } + } + + r := Nod(ORETURN, nil, nil) + r.List = list(r.List, nh) + fn.Nbody = list(fn.Nbody, r) + + if Debug['r'] != 0 { + dumplist("genhash body", fn.Nbody) + } + + funcbody(fn) + Curfn = fn + fn.Func.Dupok = true + typecheck(&fn, Etop) + typechecklist(fn.Nbody, Etop) + Curfn = nil + + // Disable safemode while compiling this code: the code we + // generate internally can refer to unsafe.Pointer. + // In this case it can happen if we need to generate an == + // for a struct containing a reflect.Value, which itself has + // an unexported field of type unsafe.Pointer. + old_safemode := safemode + + safemode = 0 + funccompile(fn) + safemode = old_safemode +} + +func hashfor(t *Type) *Node { + var sym *Sym + + a := algtype1(t, nil) + switch a { + case AMEM: + Fatalf("hashfor with AMEM type") + + case AINTER: + sym = Pkglookup("interhash", Runtimepkg) + + case ANILINTER: + sym = Pkglookup("nilinterhash", Runtimepkg) + + case ASTRING: + sym = Pkglookup("strhash", Runtimepkg) + + case AFLOAT32: + sym = Pkglookup("f32hash", Runtimepkg) + + case AFLOAT64: + sym = Pkglookup("f64hash", Runtimepkg) + + case ACPLX64: + sym = Pkglookup("c64hash", Runtimepkg) + + case ACPLX128: + sym = Pkglookup("c128hash", Runtimepkg) + + default: + sym = typesymprefix(".hash", t) + } + + n := newname(sym) + n.Class = PFUNC + tfn := Nod(OTFUNC, nil, nil) + tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t)))) + tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR]))) + tfn.Rlist = list(tfn.Rlist, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR]))) + typecheck(&tfn, Etype) + n.Type = tfn.Type + return n +} + +// geneq generates a helper function to +// check equality of two values of type t. +func geneq(sym *Sym, t *Type) { + if Debug['r'] != 0 { + fmt.Printf("geneq %v %v\n", sym, t) + } + + lineno = 1 // less confusing than end of input + dclcontext = PEXTERN + markdcl() + + // func sym(p, q *T) bool + fn := Nod(ODCLFUNC, nil, nil) + + fn.Func.Nname = newname(sym) + fn.Func.Nname.Class = PFUNC + tfn := Nod(OTFUNC, nil, nil) + fn.Func.Nname.Name.Param.Ntype = tfn + + n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t))) + tfn.List = list(tfn.List, n) + np := n.Left + n = Nod(ODCLFIELD, newname(Lookup("q")), typenod(Ptrto(t))) + tfn.List = list(tfn.List, n) + nq := n.Left + n = Nod(ODCLFIELD, nil, typenod(Types[TBOOL])) + tfn.Rlist = list(tfn.Rlist, n) + + funchdr(fn) + + // geneq is only called for types that have equality but + // cannot be handled by the standard algorithms, + // so t must be either an array or a struct. + switch t.Etype { + default: + Fatalf("geneq %v", t) + + case TARRAY: + if Isslice(t) { + Fatalf("geneq %v", t) + } + + // An array of pure memory would be handled by the + // standard memequal, so the element type must not be + // pure memory. Even if we unrolled the range loop, + // each iteration would be a function call, so don't bother + // unrolling. + nrange := Nod(ORANGE, nil, Nod(OIND, np, nil)) + + ni := newname(Lookup("i")) + ni.Type = Types[TINT] + nrange.List = list1(ni) + nrange.Colas = true + colasdefn(nrange.List, nrange) + ni = nrange.List.N + + // if p[i] != q[i] { return false } + nx := Nod(OINDEX, np, ni) + + nx.Bounded = true + ny := Nod(OINDEX, nq, ni) + ny.Bounded = true + + nif := Nod(OIF, nil, nil) + nif.Left = Nod(ONE, nx, ny) + r := Nod(ORETURN, nil, nil) + r.List = list(r.List, Nodbool(false)) + nif.Nbody = list(nif.Nbody, r) + nrange.Nbody = list(nrange.Nbody, nif) + fn.Nbody = list(fn.Nbody, nrange) + + // return true + ret := Nod(ORETURN, nil, nil) + ret.List = list(ret.List, Nodbool(true)) + fn.Nbody = list(fn.Nbody, ret) + + // Walk the struct using memequal for runs of AMEM + // and calling specific equality tests for the others. + // Skip blank-named fields. + case TSTRUCT: + var conjuncts []*Node + + t1 := t.Type + for { + first, size, next := memrun(t, t1) + t1 = next + + // Run memequal for fields up to this one. + // TODO(rsc): All the calls to newname are wrong for + // cross-package unexported fields. + if first != nil { + if first.Down == t1 { + conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym))) + } else if first.Down.Down == t1 { + conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym))) + first = first.Down + if !isblanksym(first.Sym) { + conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym))) + } + } else { + // More than two fields: use memequal. + conjuncts = append(conjuncts, eqmem(np, nq, newname(first.Sym), size)) + } + } + + if t1 == nil { + break + } + if isblanksym(t1.Sym) { + t1 = t1.Down + continue + } + if algtype1(t1.Type, nil) == AMEM { + // Our memory run might have been stopped by padding or a blank field. + // If the next field is memory-ish, it could be the start of a new run. + continue + } + + // Check this field, which is not just memory. + conjuncts = append(conjuncts, eqfield(np, nq, newname(t1.Sym))) + t1 = t1.Down + } + + var and *Node + switch len(conjuncts) { + case 0: + and = Nodbool(true) + case 1: + and = conjuncts[0] + default: + and = Nod(OANDAND, conjuncts[0], conjuncts[1]) + for _, conjunct := range conjuncts[2:] { + and = Nod(OANDAND, and, conjunct) + } + } + + ret := Nod(ORETURN, nil, nil) + ret.List = list(ret.List, and) + fn.Nbody = list(fn.Nbody, ret) + } + + if Debug['r'] != 0 { + dumplist("geneq body", fn.Nbody) + } + + funcbody(fn) + Curfn = fn + fn.Func.Dupok = true + typecheck(&fn, Etop) + typechecklist(fn.Nbody, Etop) + Curfn = nil + + // Disable safemode while compiling this code: the code we + // generate internally can refer to unsafe.Pointer. + // In this case it can happen if we need to generate an == + // for a struct containing a reflect.Value, which itself has + // an unexported field of type unsafe.Pointer. + old_safemode := safemode + safemode = 0 + + // Disable checknils while compiling this code. + // We are comparing a struct or an array, + // neither of which can be nil, and our comparisons + // are shallow. + Disable_checknil++ + + funccompile(fn) + + safemode = old_safemode + Disable_checknil-- +} + +// eqfield returns the node +// p.field == q.field +func eqfield(p *Node, q *Node, field *Node) *Node { + nx := Nod(OXDOT, p, field) + ny := Nod(OXDOT, q, field) + ne := Nod(OEQ, nx, ny) + return ne +} + +// eqmem returns the node +// memequal(&p.field, &q.field [, size]) +func eqmem(p *Node, q *Node, field *Node, size int64) *Node { + var needsize int + + nx := Nod(OADDR, Nod(OXDOT, p, field), nil) + nx.Etype = 1 // does not escape + ny := Nod(OADDR, Nod(OXDOT, q, field), nil) + ny.Etype = 1 // does not escape + typecheck(&nx, Erv) + typecheck(&ny, Erv) + + call := Nod(OCALL, eqmemfunc(size, nx.Type.Type, &needsize), nil) + call.List = list(call.List, nx) + call.List = list(call.List, ny) + if needsize != 0 { + call.List = list(call.List, Nodintconst(size)) + } + + return call +} + +func eqmemfunc(size int64, type_ *Type, needsize *int) *Node { + var fn *Node + + switch size { + default: + fn = syslook("memequal", 1) + *needsize = 1 + + case 1, 2, 4, 8, 16: + buf := fmt.Sprintf("memequal%d", int(size)*8) + fn = syslook(buf, 1) + *needsize = 0 + } + + substArgTypes(fn, type_, type_) + return fn +} + +// memrun finds runs of struct fields for which memory-only algs are appropriate. +// t is the parent struct type, and field is the field at which to start. +// first is the first field in the memory run. +// size is the length in bytes of the memory included in the run. +// next is the next field after the memory run. +func memrun(t *Type, field *Type) (first *Type, size int64, next *Type) { + var offend int64 + for { + if field == nil || algtype1(field.Type, nil) != AMEM || isblanksym(field.Sym) { + break + } + offend = field.Width + field.Type.Width + if first == nil { + first = field + } + + // If it's a memory field but it's padded, stop here. + if ispaddedfield(field, t.Width) { + field = field.Down + break + } + field = field.Down + } + if first != nil { + size = offend - first.Width // first.Width is offset + } + return first, size, field +} + +// ispaddedfield reports whether the given field +// is followed by padding. For the case where t is +// the last field, total gives the size of the enclosing struct. +func ispaddedfield(t *Type, total int64) bool { + if t.Etype != TFIELD { + Fatalf("ispaddedfield called non-field %v", t) + } + if t.Down == nil { + return t.Width+t.Type.Width != total + } + return t.Width+t.Type.Width != t.Down.Width +} diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index ff0465f64c..dc55bb023e 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -72,7 +72,7 @@ amount of space to hold the list without the need to grow it later. All integer values use a variable-length encoding for compact representation. -If debugFormat is set, each integer and string value is preceeded by a marker +If debugFormat is set, each integer and string value is preceded by a marker and position information in the encoding. This mechanism permits an importer to recognize immediately when it is out of sync. The importer recognizes this mode automatically (i.e., it can import export data produced with debugging diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go index 731f31ba52..f330f1b9e6 100644 --- a/src/cmd/compile/internal/gc/bimport.go +++ b/src/cmd/compile/internal/gc/bimport.go @@ -240,10 +240,9 @@ func (p *importer) typ() *Type { { saved := structpkg structpkg = tsym.Pkg - addmethod(sym, n.Type, false, nointerface) + addmethod(sym, n.Type, false, false) structpkg = saved } - nointerface = false funchdr(n) // (comment from go.y) diff --git a/src/cmd/compile/internal/gc/cgen.go b/src/cmd/compile/internal/gc/cgen.go index 6456240a12..74f61129c2 100644 --- a/src/cmd/compile/internal/gc/cgen.go +++ b/src/cmd/compile/internal/gc/cgen.go @@ -781,7 +781,7 @@ var sys_wbptr *Node func cgen_wbptr(n, res *Node) { if Curfn != nil { - if Curfn.Func.Nowritebarrier { + if Curfn.Func.Pragma&Nowritebarrier != 0 { Yyerror("write barrier prohibited") } if Curfn.Func.WBLineno == 0 { @@ -831,7 +831,7 @@ func cgen_wbptr(n, res *Node) { func cgen_wbfat(n, res *Node) { if Curfn != nil { - if Curfn.Func.Nowritebarrier { + if Curfn.Func.Pragma&Nowritebarrier != 0 { Yyerror("write barrier prohibited") } if Curfn.Func.WBLineno == 0 { @@ -2263,9 +2263,9 @@ func sgen_wb(n *Node, ns *Node, w int64, wb bool) { // If copying .args, that's all the results, so record definition sites // for them for the liveness analysis. if ns.Op == ONAME && ns.Sym.Name == ".args" { - for l := Curfn.Func.Dcl; l != nil; l = l.Next { - if l.N.Class == PPARAMOUT { - Gvardef(l.N) + for _, ln := range Curfn.Func.Dcl { + if ln.Class == PPARAMOUT { + Gvardef(ln) } } } @@ -2621,7 +2621,7 @@ func cgen_ret(n *Node) { if hasdefer { Ginscall(Deferreturn, 0) } - Genlist(Curfn.Func.Exit) + Genslice(Curfn.Func.Exit.Slice()) p := Thearch.Gins(obj.ARET, nil, nil) if n != nil && n.Op == ORETJMP { p.To.Type = obj.TYPE_MEM diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go index 6853e625b0..72aeeee2c4 100644 --- a/src/cmd/compile/internal/gc/closure.go +++ b/src/cmd/compile/internal/gc/closure.go @@ -67,9 +67,7 @@ func closurebody(body *NodeList) *Node { // ordinary ones in the symbol table; see oldname. // unhook them. // make the list of pointers for the closure call. - var v *Node - for l := func_.Func.Cvars; l != nil; l = l.Next { - v = l.N + for _, v := range func_.Func.Cvars.Slice() { v.Name.Param.Closure.Name.Param.Closure = v.Name.Param.Outer v.Name.Param.Outerexpr = oldname(v.Sym) } @@ -78,10 +76,8 @@ func closurebody(body *NodeList) *Node { } func typecheckclosure(func_ *Node, top int) { - var n *Node - - for l := func_.Func.Cvars; l != nil; l = l.Next { - n = l.N.Name.Param.Closure + for _, ln := range func_.Func.Cvars.Slice() { + n := ln.Name.Param.Closure if !n.Name.Captured { n.Name.Captured = true if n.Name.Decldepth == 0 { @@ -96,9 +92,9 @@ func typecheckclosure(func_ *Node, top int) { } } - for l := func_.Func.Dcl; l != nil; l = l.Next { - if l.N.Op == ONAME && (l.N.Class == PPARAM || l.N.Class == PPARAMOUT) { - l.N.Name.Decldepth = 1 + for _, ln := range func_.Func.Dcl { + if ln.Op == ONAME && (ln.Class == PPARAM || ln.Class == PPARAMOUT) { + ln.Name.Decldepth = 1 } } @@ -198,7 +194,8 @@ func makeclosure(func_ *Node) *Node { makefuncsym(xfunc.Func.Nname.Sym) xfunc.Nbody = func_.Nbody - xfunc.Func.Dcl = concat(func_.Func.Dcl, xfunc.Func.Dcl) + xfunc.Func.Dcl = append(func_.Func.Dcl, xfunc.Func.Dcl...) + func_.Func.Dcl = nil if xfunc.Nbody == nil { Fatalf("empty body - won't generate any code") } @@ -220,16 +217,14 @@ func makeclosure(func_ *Node) *Node { // We use value capturing for values <= 128 bytes that are never reassigned // after capturing (effectively constant). func capturevars(xfunc *Node) { - var v *Node var outer *Node lno := int(lineno) lineno = xfunc.Lineno func_ := xfunc.Func.Closure - func_.Func.Enter = nil - for l := func_.Func.Cvars; l != nil; l = l.Next { - v = l.N + func_.Func.Enter.Set(nil) + for _, v := range func_.Func.Cvars.Slice() { if v.Type == nil { // if v->type is nil, it means v looked like it was // going to be used in the closure but wasn't. @@ -270,7 +265,7 @@ func capturevars(xfunc *Node) { } typecheck(&outer, Erv) - func_.Func.Enter = list(func_.Func.Enter, outer) + func_.Func.Enter.Append(outer) } lineno = int32(lno) @@ -309,11 +304,9 @@ func transformclosure(xfunc *Node) { original_dcl := xfunc.Func.Dcl xfunc.Func.Dcl = nil - var v *Node var addr *Node var fld *Type - for l := func_.Func.Cvars; l != nil; l = l.Next { - v = l.N + for _, v := range func_.Func.Cvars.Slice() { if v.Op == OXXX { continue } @@ -341,13 +334,13 @@ func transformclosure(xfunc *Node) { fld.Sym = fld.Nname.Sym // Declare the new param and add it the first part of the input arguments. - xfunc.Func.Dcl = list(xfunc.Func.Dcl, fld.Nname) + xfunc.Func.Dcl = append(xfunc.Func.Dcl, fld.Nname) *param = fld param = &fld.Down } *param = original_args - xfunc.Func.Dcl = concat(xfunc.Func.Dcl, original_dcl) + xfunc.Func.Dcl = append(xfunc.Func.Dcl, original_dcl...) // Recalculate param offsets. if f.Type.Width > 0 { @@ -357,19 +350,14 @@ func transformclosure(xfunc *Node) { xfunc.Type = f.Type // update type of ODCLFUNC } else { // The closure is not called, so it is going to stay as closure. - nvar := 0 - - var body *NodeList + var body []*Node offset := int64(Widthptr) var addr *Node - var v *Node var cv *Node - for l := func_.Func.Cvars; l != nil; l = l.Next { - v = l.N + for _, v := range func_.Func.Cvars.Slice() { if v.Op == OXXX { continue } - nvar++ // cv refers to the field inside of closure OSTRUCTLIT. cv = Nod(OCLOSUREVAR, nil, nil) @@ -386,8 +374,8 @@ func transformclosure(xfunc *Node) { // If it is a small variable captured by value, downgrade it to PAUTO. v.Class = PAUTO v.Ullman = 1 - xfunc.Func.Dcl = list(xfunc.Func.Dcl, v) - body = list(body, Nod(OAS, v, cv)) + xfunc.Func.Dcl = append(xfunc.Func.Dcl, v) + body = append(body, Nod(OAS, v, cv)) } else { // Declare variable holding addresses taken from closure // and initialize in entry prologue. @@ -396,19 +384,21 @@ func transformclosure(xfunc *Node) { addr.Class = PAUTO addr.Used = true addr.Name.Curfn = xfunc - xfunc.Func.Dcl = list(xfunc.Func.Dcl, addr) + xfunc.Func.Dcl = append(xfunc.Func.Dcl, addr) v.Name.Heapaddr = addr if v.Name.Byval { cv = Nod(OADDR, cv, nil) } - body = list(body, Nod(OAS, addr, cv)) + body = append(body, Nod(OAS, addr, cv)) } } - typechecklist(body, Etop) - walkstmtlist(body) - xfunc.Func.Enter = body - xfunc.Func.Needctxt = nvar > 0 + if len(body) > 0 { + typecheckslice(body, Etop) + walkstmtslice(body) + xfunc.Func.Enter.Set(body) + xfunc.Func.Needctxt = true + } } lineno = int32(lno) @@ -416,7 +406,7 @@ func transformclosure(xfunc *Node) { func walkclosure(func_ *Node, init **NodeList) *Node { // If no closure vars, don't bother wrapping. - if func_.Func.Cvars == nil { + if len(func_.Func.Cvars.Slice()) == 0 { return func_.Func.Closure.Func.Nname } @@ -438,9 +428,7 @@ func walkclosure(func_ *Node, init **NodeList) *Node { typ.List = list1(Nod(ODCLFIELD, newname(Lookup(".F")), typenod(Types[TUINTPTR]))) var typ1 *Node - var v *Node - for l := func_.Func.Cvars; l != nil; l = l.Next { - v = l.N + for _, v := range func_.Func.Cvars.Slice() { if v.Op == OXXX { continue } @@ -454,7 +442,7 @@ func walkclosure(func_ *Node, init **NodeList) *Node { clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil)) clos.Esc = func_.Esc clos.Right.Implicit = true - clos.List = concat(list1(Nod(OCFUNC, func_.Func.Closure.Func.Nname, nil)), func_.Func.Enter) + clos.List = concat(list1(Nod(OCFUNC, func_.Func.Closure.Func.Nname, nil)), func_.Func.Enter.NodeList()) // Force type conversion from *struct to the func type. clos = Nod(OCONVNOP, clos, nil) @@ -551,7 +539,7 @@ func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node { n = newname(Lookupf("a%d", i)) i++ n.Class = PPARAM - xfunc.Func.Dcl = list(xfunc.Func.Dcl, n) + xfunc.Func.Dcl = append(xfunc.Func.Dcl, n) callargs = list(callargs, n) fld = Nod(ODCLFIELD, n, typenod(t.Type)) if t.Isddd { @@ -570,7 +558,7 @@ func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node { n = newname(Lookupf("r%d", i)) i++ n.Class = PPARAMOUT - xfunc.Func.Dcl = list(xfunc.Func.Dcl, n) + xfunc.Func.Dcl = append(xfunc.Func.Dcl, n) retargs = list(retargs, n) l = list(l, Nod(ODCLFIELD, n, typenod(t.Type))) } @@ -601,7 +589,7 @@ func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node { ptr.Used = true ptr.Name.Curfn = xfunc ptr.Xoffset = 0 - xfunc.Func.Dcl = list(xfunc.Func.Dcl, ptr) + xfunc.Func.Dcl = append(xfunc.Func.Dcl, ptr) var body *NodeList if Isptr[rcvrtype.Etype] || Isinter(rcvrtype) { ptr.Name.Param.Ntype = typenod(rcvrtype) diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go index fc47a39ee6..33c04c501d 100644 --- a/src/cmd/compile/internal/gc/dcl.go +++ b/src/cmd/compile/internal/gc/dcl.go @@ -187,7 +187,7 @@ func declare(n *Node, ctxt Class) { Fatalf("automatic outside function") } if Curfn != nil { - Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) + Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) } if n.Op == OTYPE { declare_typegen++ @@ -426,7 +426,7 @@ func oldname(s *Sym) *Node { n.Name.Param.Closure = c c.Name.Param.Closure = n c.Xoffset = 0 - Curfn.Func.Cvars = list(Curfn.Func.Cvars, c) + Curfn.Func.Cvars.Append(c) } // return ref to closure var, not original @@ -1449,8 +1449,13 @@ func funccompile(n *Node) { Funcdepth = n.Func.Depth + 1 compile(n) Curfn = nil + Pc = nil + continpc = nil + breakpc = nil Funcdepth = 0 dclcontext = PEXTERN + flushdata() + obj.Flushplist(Ctxt) // convert from Prog list to machine code } func funcsym(s *Sym) *Sym { @@ -1525,7 +1530,7 @@ func checknowritebarrierrec() { // Check nowritebarrierrec functions. for _, n := range list { - if !n.Func.Nowritebarrierrec { + if n.Func.Pragma&Nowritebarrierrec == 0 { continue } call, hasWB := c.best[n] diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go index 1a5a433eeb..5745994785 100644 --- a/src/cmd/compile/internal/gc/esc.go +++ b/src/cmd/compile/internal/gc/esc.go @@ -476,35 +476,35 @@ func escfunc(e *EscState, func_ *Node) { savefn := Curfn Curfn = func_ - for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { - if ll.N.Op != ONAME { + for _, ln := range Curfn.Func.Dcl { + if ln.Op != ONAME { continue } - llNE := e.nodeEscState(ll.N) - switch ll.N.Class { + llNE := e.nodeEscState(ln) + switch ln.Class { // out params are in a loopdepth between the sink and all local variables case PPARAMOUT: llNE.Escloopdepth = 0 case PPARAM: llNE.Escloopdepth = 1 - if ll.N.Type != nil && !haspointers(ll.N.Type) { + if ln.Type != nil && !haspointers(ln.Type) { break } if Curfn.Nbody == nil && !Curfn.Noescape { - ll.N.Esc = EscHeap + ln.Esc = EscHeap } else { - ll.N.Esc = EscNone // prime for escflood later + ln.Esc = EscNone // prime for escflood later } - e.noesc = list(e.noesc, ll.N) + e.noesc = list(e.noesc, ln) } } // in a mutually recursive group we lose track of the return values if e.recursive { - for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { - if ll.N.Op == ONAME && ll.N.Class == PPARAMOUT { - escflows(e, &e.theSink, ll.N) + for _, ln := range Curfn.Func.Dcl { + if ln.Op == ONAME && ln.Class == PPARAMOUT { + escflows(e, &e.theSink, ln) } } } @@ -779,11 +779,14 @@ func esc(e *EscState, n *Node, up *Node) { ll = e.nodeEscState(n.List.N).Escretval } - for lr := Curfn.Func.Dcl; lr != nil && ll != nil; lr = lr.Next { - if lr.N.Op != ONAME || lr.N.Class != PPARAMOUT { + for _, lrn := range Curfn.Func.Dcl { + if ll == nil { + break + } + if lrn.Op != ONAME || lrn.Class != PPARAMOUT { continue } - escassign(e, lr.N, ll.N) + escassign(e, lrn, ll.N) ll = ll.Next } @@ -861,9 +864,7 @@ func esc(e *EscState, n *Node, up *Node) { // Link addresses of captured variables to closure. case OCLOSURE: var a *Node - var v *Node - for ll := n.Func.Cvars; ll != nil; ll = ll.Next { - v = ll.N + for _, v := range n.Func.Cvars.Slice() { if v.Op == OXXX { // unnamed out argument; see dcl.go:/^funcargs continue } @@ -1870,16 +1871,16 @@ func esctag(e *EscState, func_ *Node) { savefn := Curfn Curfn = func_ - for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { - if ll.N.Op != ONAME { + for _, ln := range Curfn.Func.Dcl { + if ln.Op != ONAME { continue } - switch ll.N.Esc & EscMask { + switch ln.Esc & EscMask { case EscNone, // not touched by escflood EscReturn: - if haspointers(ll.N.Type) { // don't bother tagging for scalars - ll.N.Name.Param.Field.Note = mktag(int(ll.N.Esc)) + if haspointers(ln.Type) { // don't bother tagging for scalars + ln.Name.Param.Field.Note = mktag(int(ln.Esc)) } case EscHeap, // touched by escflood, moved to heap diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 69022814ef..a7982706c7 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -1730,10 +1730,42 @@ func Hconv(l *NodeList, flag int) string { return buf.String() } +func Hconvslice(l []*Node, flag int) string { + if len(l) == 0 && fmtmode == FDbg { + return "" + } + + sf := flag + sm, sb := setfmode(&flag) + sep := "; " + if fmtmode == FDbg { + sep = "\n" + } else if flag&obj.FmtComma != 0 { + sep = ", " + } + + var buf bytes.Buffer + for i, n := range l { + buf.WriteString(Nconv(n, 0)) + if i+1 < len(l) { + buf.WriteString(sep) + } + } + + flag = sf + fmtbody = sb + fmtmode = sm + return buf.String() +} + func dumplist(s string, l *NodeList) { fmt.Printf("%s%v\n", s, Hconv(l, obj.FmtSign)) } +func dumpslice(s string, l []*Node) { + fmt.Printf("%s%v\n", s, Hconvslice(l, obj.FmtSign)) +} + func Dump(s string, n *Node) { fmt.Printf("%s [%p]%v\n", s, n, Nconv(n, obj.FmtSign)) } diff --git a/src/cmd/compile/internal/gc/gen.go b/src/cmd/compile/internal/gc/gen.go index 721ef31247..ddd864f609 100644 --- a/src/cmd/compile/internal/gc/gen.go +++ b/src/cmd/compile/internal/gc/gen.go @@ -76,6 +76,9 @@ func addrescapes(n *Node) { oldfn := Curfn Curfn = n.Name.Curfn + if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE { + Curfn = Curfn.Func.Closure + } n.Name.Heapaddr = temp(Ptrto(n.Type)) buf := fmt.Sprintf("&%v", n.Sym) n.Name.Heapaddr.Sym = Lookup(buf) @@ -218,6 +221,12 @@ func Genlist(l *NodeList) { } } +func Genslice(l []*Node) { + for _, n := range l { + gen(n) + } +} + // generate code to start new proc running call n. func cgen_proc(n *Node, proc int) { switch n.Left.Op { @@ -587,6 +596,10 @@ func Tempname(nn *Node, t *Type) { if Curfn == nil { Fatalf("no curfn for tempname") } + if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE { + Dump("Tempname", Curfn) + Fatalf("adding tempname to wrong closure function") + } if t == nil { Yyerror("tempname called with nil type") @@ -606,7 +619,7 @@ func Tempname(nn *Node, t *Type) { n.Ullman = 1 n.Esc = EscNever n.Name.Curfn = Curfn - Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) + Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) dowidth(t) n.Xoffset = 0 diff --git a/src/cmd/compile/internal/gc/global_test.go b/src/cmd/compile/internal/gc/global_test.go index 6c388aff7c..bd1391d9ad 100644 --- a/src/cmd/compile/internal/gc/global_test.go +++ b/src/cmd/compile/internal/gc/global_test.go @@ -11,7 +11,8 @@ import ( "log" "os" "os/exec" - "path" + "path/filepath" + "strings" "testing" ) @@ -28,7 +29,7 @@ func TestScanfRemoval(t *testing.T) { defer os.RemoveAll(dir) // Create source. - src := path.Join(dir, "test.go") + src := filepath.Join(dir, "test.go") f, err := os.Create(src) if err != nil { log.Fatalf("could not create source file: %v", err) @@ -43,7 +44,7 @@ func main() { f.Close() // Name of destination. - dst := path.Join(dir, "test") + dst := filepath.Join(dir, "test") // Compile source. cmd := exec.Command("go", "build", "-o", dst, src) @@ -62,3 +63,53 @@ func main() { log.Fatalf("scanf code not removed from helloworld") } } + +// Make sure -S prints assembly code. See issue 14515. +func TestDashS(t *testing.T) { + testenv.MustHaveGoBuild(t) + + // Make a directory to work in. + dir, err := ioutil.TempDir("", "issue14515-") + if err != nil { + log.Fatalf("could not create directory: %v", err) + } + defer os.RemoveAll(dir) + + // Create source. + src := filepath.Join(dir, "test.go") + f, err := os.Create(src) + if err != nil { + log.Fatalf("could not create source file: %v", err) + } + f.Write([]byte(` +package main +import "fmt" +func main() { + fmt.Println("hello world") +} +`)) + f.Close() + + // Compile source. + cmd := exec.Command("go", "build", "-gcflags", "-S", "-o", filepath.Join(dir, "test"), src) + out, err := cmd.CombinedOutput() + if err != nil { + log.Fatalf("could not build target: %v", err) + } + + patterns := []string{ + // It is hard to look for actual instructions in an + // arch-independent way. So we'll just look for + // pseudo-ops that are arch-independent. + "\tTEXT\t", + "\tFUNCDATA\t", + "\tPCDATA\t", + } + outstr := string(out) + for _, p := range patterns { + if !strings.Contains(outstr, p) { + println(outstr) + panic("can't find pattern " + p) + } + } +} diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index f721fabfd7..fc63a514d0 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -26,25 +26,6 @@ const ( MaxStackVarSize = 10 * 1024 * 1024 ) -const ( - // These values are known by runtime. - ANOEQ = iota - AMEM0 - AMEM8 - AMEM16 - AMEM32 - AMEM64 - AMEM128 - ASTRING - AINTER - ANILINTER - AFLOAT32 - AFLOAT64 - ACPLX64 - ACPLX128 - AMEM = 100 -) - const ( // Maximum size in bits for Mpints before signalling // overflow and also mantissa precision for Mpflts. @@ -122,10 +103,9 @@ type Pkg struct { } type Sym struct { - Lexical uint16 Flags uint8 - Link *Sym Uniqgen uint32 + Link *Sym Importdef *Pkg // where imported definition was found Linkname string // link name @@ -425,12 +405,13 @@ var sizeof_String int // runtime sizeof(String) var dotlist [10]Dlist // size is max depth of embeddeds +// lexlineno is the line number _after_ the most recently read rune. +// In particular, it's advanced (or rewound) as newlines are read (or unread). var lexlineno int32 +// lineno is the line number at the start of the most recently lexed token. var lineno int32 -var prevlineno int32 - var pragcgobuf string var infile string @@ -616,23 +597,10 @@ var flag_largemodel int // when the race detector is enabled. var instrumenting bool -// Pending annotations for next func declaration. -var ( - noescape bool - noinline bool - norace bool - nosplit bool - nowritebarrier bool - nowritebarrierrec bool - systemstack bool -) - var debuglive int var Ctxt *obj.Link -var nointerface bool - var writearchive int var bstdout obj.Biobuf diff --git a/src/cmd/compile/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go index be0a0fb56d..73f71dd0fb 100644 --- a/src/cmd/compile/internal/gc/gsubr.go +++ b/src/cmd/compile/internal/gc/gsubr.go @@ -173,6 +173,18 @@ func dumpdata() { Clearp(Pc) } +func flushdata() { + if dfirst == nil { + return + } + newplist() + *Pc = *dfirst + Pc = dpc + Clearp(Pc) + dfirst = nil + dpc = nil +} + // Fixup instructions after allocauto (formerly compactframe) has moved all autos around. func fixautoused(p *obj.Prog) { for lp := &p; ; { @@ -554,9 +566,7 @@ func nodarg(t *Type, fp int) *Node { } if fp == 1 || fp == -1 { - var n *Node - for l := Curfn.Func.Dcl; l != nil; l = l.Next { - n = l.N + for _, n := range Curfn.Func.Dcl { if (n.Class == PPARAM || n.Class == PPARAMOUT) && !isblanksym(t.Sym) && n.Sym == t.Sym { return n } diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index a445f712e2..f5c3265a82 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -107,7 +107,7 @@ func caninl(fn *Node) { } // If marked "go:noinline", don't inline - if fn.Func.Noinline { + if fn.Func.Pragma&Noinline != 0 { return } @@ -150,7 +150,10 @@ func caninl(fn *Node) { fn.Func.Nname.Func.Inl = fn.Nbody fn.Nbody = inlcopylist(fn.Func.Nname.Func.Inl) - fn.Func.Nname.Func.Inldcl = inlcopylist(fn.Func.Nname.Name.Defn.Func.Dcl) + inldcl := inlcopyslice(fn.Func.Nname.Name.Defn.Func.Dcl) + if len(inldcl) > 0 { + fn.Func.Nname.Func.Inldcl = &inldcl + } fn.Func.Nname.Func.InlCost = int32(maxBudget - budget) // hack, TODO, check for better way to link method nodes back to the thing with the ->inl @@ -275,6 +278,18 @@ func inlcopy(n *Node) *Node { return m } +// Inlcopyslice is like inlcopylist, but for a slice. +func inlcopyslice(ll []*Node) []*Node { + r := make([]*Node, 0, len(ll)) + for _, ln := range ll { + c := inlcopy(ln) + if c != nil { + r = append(r, c) + } + } + return r +} + // Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any // calls made to inlineable functions. This is the external entry point. func inlcalls(fn *Node) { @@ -556,10 +571,14 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) { //dumplist("ninit pre", ninit); - var dcl *NodeList - if fn.Name.Defn != nil { // local function - dcl = fn.Func.Inldcl // imported function + var dcl []*Node + if fn.Name.Defn != nil { + // local function + if fn.Func.Inldcl != nil { + dcl = *fn.Func.Inldcl + } } else { + // imported function dcl = fn.Func.Dcl } @@ -567,18 +586,18 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) { i := 0 // Make temp names to use instead of the originals - for ll := dcl; ll != nil; ll = ll.Next { - if ll.N.Class == PPARAMOUT { // return values handled below. + for _, ln := range dcl { + if ln.Class == PPARAMOUT { // return values handled below. continue } - if ll.N.Op == ONAME { - ll.N.Name.Inlvar = inlvar(ll.N) + if ln.Op == ONAME { + ln.Name.Inlvar = inlvar(ln) // Typecheck because inlvar is not necessarily a function parameter. - typecheck(&ll.N.Name.Inlvar, Erv) + typecheck(&ln.Name.Inlvar, Erv) - if ll.N.Class&^PHEAP != PAUTO { - ninit = list(ninit, Nod(ODCL, ll.N.Name.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs + if ln.Class&^PHEAP != PAUTO { + ninit = list(ninit, Nod(ODCL, ln.Name.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs } } } @@ -852,7 +871,7 @@ func inlvar(var_ *Node) *Node { addrescapes(n) } - Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) + Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) return n } @@ -863,7 +882,7 @@ func retvar(t *Type, i int) *Node { n.Class = PAUTO n.Used = true n.Name.Curfn = Curfn // the calling function, not the called one - Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) + Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) return n } @@ -875,7 +894,7 @@ func argvar(t *Type, i int) *Node { n.Class = PAUTO n.Used = true n.Name.Curfn = Curfn // the calling function, not the called one - Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) + Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) return n } diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go index 46122d264d..49e5d6561a 100644 --- a/src/cmd/compile/internal/gc/lex.go +++ b/src/cmd/compile/internal/gc/lex.go @@ -7,7 +7,6 @@ package gc import ( - "bytes" "cmd/compile/internal/ssa" "cmd/internal/obj" "flag" @@ -38,6 +37,8 @@ var ( Debug_wb int ) +const BOM = 0xFEFF + // Debug arguments. // These can be specified with the -d flag, as in "-d nil" // to set the debug_checknil variable. In general the list passed @@ -237,6 +238,7 @@ func Main() { } Ctxt.Flag_shared = int32(flag_shared) Ctxt.Flag_dynlink = flag_dynlink + Ctxt.Flag_optimize = Debug['N'] == 0 Ctxt.Debugasm = int32(Debug['S']) Ctxt.Debugvlog = int32(Debug['v']) @@ -327,7 +329,6 @@ func Main() { dclcontext = PEXTERN nerrors = 0 lexlineno = 1 - const BOM = 0xFEFF loadsys() @@ -566,22 +567,9 @@ func skiptopkgdef(b *obj.Biobuf) bool { return false } - // symbol table may be first; skip it - sz := arsize(b, "__.GOSYMDEF") - - if sz >= 0 { - obj.Bseek(b, int64(sz), 1) - } else { - obj.Bseek(b, 8, 0) - } - - // package export block is next - sz = arsize(b, "__.PKGDEF") - - if sz <= 0 { - return false - } - return true + // package export block should be first + sz := arsize(b, "__.PKGDEF") + return sz > 0 } var idirs []string @@ -592,10 +580,14 @@ func addidir(dir string) { } } +func isDriveLetter(b byte) bool { + return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z' +} + // is this path a local name? begins with ./ or ../ or / func islocalname(name string) bool { return strings.HasPrefix(name, "/") || - Ctxt.Windows != 0 && len(name) >= 3 && isAlpha(int(name[0])) && name[1] == ':' && name[2] == '/' || + Ctxt.Windows != 0 && len(name) >= 3 && isDriveLetter(name[0]) && name[1] == ':' && name[2] == '/' || strings.HasPrefix(name, "./") || name == "." || strings.HasPrefix(name, "../") || name == ".." } @@ -846,20 +838,17 @@ func importfile(f *Val, indent []byte) { } } -func isSpace(c int) bool { +func isSpace(c rune) bool { return c == ' ' || c == '\t' || c == '\n' || c == '\r' } -func isAlpha(c int) bool { - return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' +func isLetter(c rune) bool { + return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' } -func isDigit(c int) bool { +func isDigit(c rune) bool { return '0' <= c && c <= '9' } -func isAlnum(c int) bool { - return isAlpha(c) || isDigit(c) -} func plan9quote(s string) string { if s == "" { @@ -873,35 +862,55 @@ func plan9quote(s string) string { return s } -func isfrog(c int) bool { - // complain about possibly invisible control characters - if c < ' ' { - return !isSpace(c) // exclude good white space - } +type Pragma uint8 - if 0x7f <= c && c <= 0xa0 { // DEL, unicode block including unbreakable space. - return true - } - return false -} +const ( + Nointerface Pragma = 1 << iota + Noescape // func parameters don't escape + Norace // func must not have race detector annotations + Nosplit // func should not execute on separate stack + Noinline // func should not be inlined + Systemstack // func must run on system stack + Nowritebarrier // emit compiler error instead of write barrier + Nowritebarrierrec // error on write barrier in this or recursive callees +) type lexer struct { // source bin *obj.Biobuf - peekc int - peekc1 int // second peekc for ... + peekr1 rune + peekr2 rune // second peekc for ... nlsemi bool // if set, '\n' and EOF translate to ';' + // pragma flags + // accumulated by lexer; reset by parser + pragma Pragma + // current token tok int32 - sym_ *Sym // valid if tok == LNAME - val Val // valid if tok == LLITERAL - op Op // valid if tok == LASOP + sym_ *Sym // valid if tok == LNAME + val Val // valid if tok == LLITERAL + op Op // valid if tok == LASOP or LINCOP, or prec > 0 + prec OpPrec // operator precedence; 0 if not a binary operator } +type OpPrec int + const ( - LLITERAL = 57346 + iota + // Precedences of binary operators (must be > 0). + PCOMM OpPrec = 1 + iota + POROR + PANDAND + PCMP + PADD + PMUL +) + +const ( + // The value of single-char tokens is just their character's Unicode value. + // They are all below utf8.RuneSelf. Shift other tokens up to avoid conflicts. + LLITERAL = utf8.RuneSelf + iota LASOP LCOLAS LBREAK @@ -934,12 +943,11 @@ const ( LANDAND LANDNOT LCOMM - LDEC LEQ LGE LGT LIGNORE - LINC + LINCOP LLE LLSH LLT @@ -949,133 +957,48 @@ const ( ) func (l *lexer) next() { - var c1 int - var op Op - var escflag int - var v int64 - var cp *bytes.Buffer - var s *Sym - var str string - - prevlineno = lineno - nlsemi := l.nlsemi l.nlsemi = false + l.prec = 0 l0: // skip white space - c := l.getc() + c := l.getr() for isSpace(c) { if c == '\n' && nlsemi { - l.ungetc(c) if Debug['x'] != 0 { fmt.Printf("lex: implicit semi\n") } + // Insert implicit semicolon on previous line, + // before the newline character. + lineno = lexlineno - 1 l.tok = ';' return } - c = l.getc() + c = l.getr() } // start of token lineno = lexlineno - if c >= utf8.RuneSelf { - // all multibyte runes are alpha - cp = &lexbuf - cp.Reset() - goto talph + // identifiers and keywords + // (for better error messages consume all chars >= utf8.RuneSelf for identifiers) + if isLetter(c) || c >= utf8.RuneSelf { + l.ident(c) + if l.tok == LIGNORE { + goto l0 + } + return } + // c < utf8.RuneSelf - if isAlpha(c) { - cp = &lexbuf - cp.Reset() - goto talph - } - - if isDigit(c) { - cp = &lexbuf - cp.Reset() - if c != '0' { - for { - cp.WriteByte(byte(c)) - c = l.getc() - if isDigit(c) { - continue - } - if c == '.' { - goto casedot - } - if c == 'e' || c == 'E' || c == 'p' || c == 'P' { - goto caseep - } - if c == 'i' { - goto casei - } - goto ncu - } - } - - cp.WriteByte(byte(c)) - c = l.getc() - if c == 'x' || c == 'X' { - for { - cp.WriteByte(byte(c)) - c = l.getc() - if isDigit(c) { - continue - } - if c >= 'a' && c <= 'f' { - continue - } - if c >= 'A' && c <= 'F' { - continue - } - if lexbuf.Len() == 2 { - Yyerror("malformed hex constant") - } - if c == 'p' { - goto caseep - } - goto ncu - } - } - - if c == 'p' { // 0p begins floating point zero - goto caseep - } - - c1 = 0 - for { - if !isDigit(c) { - break - } - if c < '0' || c > '7' { - c1 = 1 // not octal - } - cp.WriteByte(byte(c)) - c = l.getc() - } - - if c == '.' { - goto casedot - } - if c == 'e' || c == 'E' { - goto caseep - } - if c == 'i' { - goto casei - } - if c1 != 0 { - Yyerror("malformed octal constant") - } - goto ncu - } + var c1 rune + var op Op + var prec OpPrec switch c { case EOF: - lineno = prevlineno - l.ungetc(EOF) + l.ungetr(EOF) // return EOF again in future next call // Treat EOF as "end of line" for the purposes // of inserting a semicolon. if nlsemi { @@ -1088,427 +1011,469 @@ l0: l.tok = -1 return - case '_': - cp = &lexbuf - cp.Reset() - goto talph + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + l.number(c) + return case '.': - c1 = l.getc() + c1 = l.getr() if isDigit(c1) { - cp = &lexbuf - cp.Reset() - cp.WriteByte(byte(c)) - c = c1 - goto casedot + l.ungetr(c1) + l.number('.') + return } if c1 == '.' { - c1 = l.getc() + c1 = l.getr() if c1 == '.' { c = LDDD goto lx } - l.ungetc(c1) + l.ungetr(c1) c1 = '.' } - // "..." case '"': - lexbuf.Reset() - lexbuf.WriteString(`""`) + l.stdString() + return - cp = &strbuf - cp.Reset() - - for { - if l.escchar('"', &escflag, &v) { - break - } - if v < utf8.RuneSelf || escflag != 0 { - cp.WriteByte(byte(v)) - } else { - cp.WriteRune(rune(v)) - } - } - - goto strlit - - // `...` case '`': - lexbuf.Reset() - lexbuf.WriteString("``") + l.rawString() + return - cp = &strbuf - cp.Reset() - - for { - c = int(l.getr()) - if c == '\r' { - continue - } - if c == EOF { - Yyerror("eof in string") - break - } - - if c == '`' { - break - } - cp.WriteRune(rune(c)) - } - - goto strlit - - // '.' case '\'': - if l.escchar('\'', &escflag, &v) { - Yyerror("empty character literal or unescaped ' in character literal") - v = '\'' - } - - if !l.escchar('\'', &escflag, &v) { - Yyerror("missing '") - l.ungetc(int(v)) - } - - x := new(Mpint) - l.val.U = x - Mpmovecfix(x, v) - x.Rune = true - if Debug['x'] != 0 { - fmt.Printf("lex: codepoint literal\n") - } - litbuf = "string literal" - l.nlsemi = true - l.tok = LLITERAL + l.rune() return case '/': - c1 = l.getc() + c1 = l.getr() if c1 == '*' { - nl := false + c = l.getr() for { - c = int(l.getr()) - if c == '\n' { - nl = true - } - for c == '*' { - c = int(l.getr()) + if c == '*' { + c = l.getr() if c == '/' { - if nl { - l.ungetc('\n') - } - goto l0 - } - - if c == '\n' { - nl = true + break } + continue } - if c == EOF { Yyerror("eof in comment") errorexit() } + c = l.getr() } + + // A comment containing newlines acts like a newline. + if lexlineno > lineno && nlsemi { + if Debug['x'] != 0 { + fmt.Printf("lex: implicit semi\n") + } + l.tok = ';' + return + } + goto l0 } if c1 == '/' { c = l.getlinepragma() for { if c == '\n' || c == EOF { - l.ungetc(c) + l.ungetr(c) goto l0 } - c = int(l.getr()) + c = l.getr() } } - if c1 == '=' { - op = ODIV - goto asop - } + op = ODIV + prec = PMUL + goto binop1 case ':': - c1 = l.getc() + c1 = l.getr() if c1 == '=' { - c = int(LCOLAS) + c = LCOLAS goto lx } case '*': - c1 = l.getc() - if c1 == '=' { - op = OMUL - goto asop - } + op = OMUL + prec = PMUL + goto binop case '%': - c1 = l.getc() - if c1 == '=' { - op = OMOD - goto asop - } + op = OMOD + prec = PMUL + goto binop case '+': - c1 = l.getc() - if c1 == '+' { - l.nlsemi = true - c = int(LINC) - goto lx - } - - if c1 == '=' { - op = OADD - goto asop - } + op = OADD + goto incop case '-': - c1 = l.getc() - if c1 == '-' { - l.nlsemi = true - c = int(LDEC) - goto lx - } - - if c1 == '=' { - op = OSUB - goto asop - } + op = OSUB + goto incop case '>': - c1 = l.getc() + c1 = l.getr() if c1 == '>' { - c = int(LRSH) - c1 = l.getc() - if c1 == '=' { - op = ORSH - goto asop - } - - break + c = LRSH + op = ORSH + prec = PMUL + goto binop } + l.prec = PCMP if c1 == '=' { - c = int(LGE) + c = LGE + l.op = OGE goto lx } - - c = int(LGT) + c = LGT + l.op = OGT case '<': - c1 = l.getc() + c1 = l.getr() if c1 == '<' { - c = int(LLSH) - c1 = l.getc() - if c1 == '=' { - op = OLSH - goto asop - } - - break - } - - if c1 == '=' { - c = int(LLE) - goto lx + c = LLSH + op = OLSH + prec = PMUL + goto binop } if c1 == '-' { - c = int(LCOMM) + c = LCOMM + // Not a binary operator, but parsed as one + // so we can give a good error message when used + // in an expression context. + l.prec = PCOMM + l.op = OSEND goto lx } - c = int(LLT) + l.prec = PCMP + if c1 == '=' { + c = LLE + l.op = OLE + goto lx + } + c = LLT + l.op = OLT case '=': - c1 = l.getc() + c1 = l.getr() if c1 == '=' { - c = int(LEQ) + c = LEQ + l.prec = PCMP + l.op = OEQ goto lx } case '!': - c1 = l.getc() + c1 = l.getr() if c1 == '=' { - c = int(LNE) + c = LNE + l.prec = PCMP + l.op = ONE goto lx } case '&': - c1 = l.getc() + c1 = l.getr() if c1 == '&' { - c = int(LANDAND) + c = LANDAND + l.prec = PANDAND + l.op = OANDAND goto lx } if c1 == '^' { - c = int(LANDNOT) - c1 = l.getc() - if c1 == '=' { - op = OANDNOT - goto asop - } - - break + c = LANDNOT + op = OANDNOT + prec = PMUL + goto binop } - if c1 == '=' { - op = OAND - goto asop - } + op = OAND + prec = PMUL + goto binop1 case '|': - c1 = l.getc() + c1 = l.getr() if c1 == '|' { - c = int(LOROR) + c = LOROR + l.prec = POROR + l.op = OOROR goto lx } - if c1 == '=' { - op = OOR - goto asop - } + op = OOR + prec = PADD + goto binop1 case '^': - c1 = l.getc() - if c1 == '=' { - op = OXOR - goto asop - } + op = OXOR + prec = PADD + goto binop + + case '(', '[', '{', ',', ';': + goto lx case ')', ']', '}': l.nlsemi = true goto lx + case '#', '$', '?', '@', '\\': + if importpkg != nil { + goto lx + } + fallthrough + default: - goto lx + // anything else is illegal + Yyerror("syntax error: illegal character %#U", c) + goto l0 } - l.ungetc(c1) + l.ungetr(c1) lx: if Debug['x'] != 0 { - if c > 0xff { - fmt.Printf("%v lex: TOKEN %s\n", Ctxt.Line(int(lexlineno)), lexname(c)) + if c >= utf8.RuneSelf { + fmt.Printf("%v lex: TOKEN %s\n", Ctxt.Line(int(lineno)), lexname(c)) } else { - fmt.Printf("%v lex: TOKEN '%c'\n", Ctxt.Line(int(lexlineno)), c) + fmt.Printf("%v lex: TOKEN '%c'\n", Ctxt.Line(int(lineno)), c) } } - if isfrog(c) { - Yyerror("illegal character 0x%x", uint(c)) - goto l0 - } - if importpkg == nil && (c == '#' || c == '$' || c == '?' || c == '@' || c == '\\') { - Yyerror("%s: unexpected %c", "syntax error", c) - goto l0 - } - - l.tok = int32(c) + l.tok = c return -asop: +incop: + c1 = l.getr() + if c1 == c { + l.nlsemi = true + l.op = op + c = LINCOP + goto lx + } + prec = PADD + goto binop1 + +binop: + c1 = l.getr() +binop1: + if c1 != '=' { + l.ungetr(c1) + l.op = op + l.prec = prec + goto lx + } + l.op = op if Debug['x'] != 0 { fmt.Printf("lex: TOKEN ASOP %s=\n", goopnames[op]) } l.tok = LASOP - return +} - // cp is set to lexbuf and some - // prefix has been stored -talph: +func (l *lexer) ident(c rune) { + cp := &lexbuf + cp.Reset() + + // accelerate common case (7bit ASCII) + for isLetter(c) || isDigit(c) { + cp.WriteByte(byte(c)) + c = l.getr() + } + + // general case for { if c >= utf8.RuneSelf { - l.ungetc(c) - r := rune(l.getr()) - - // 0xb7 · is used for internal names - if !unicode.IsLetter(r) && !unicode.IsDigit(r) && (importpkg == nil || r != 0xb7) { - Yyerror("invalid identifier character U+%04x", r) + if unicode.IsLetter(c) || c == '_' || unicode.IsDigit(c) || importpkg != nil && c == 0xb7 { + if cp.Len() == 0 && unicode.IsDigit(c) { + Yyerror("identifier cannot begin with digit %#U", c) + } + } else { + Yyerror("invalid identifier character %#U", c) } - if cp.Len() == 0 && unicode.IsDigit(r) { - Yyerror("identifier cannot begin with digit U+%04x", r) - } - cp.WriteRune(r) - } else if !isAlnum(c) && c != '_' { - break - } else { + cp.WriteRune(c) + } else if isLetter(c) || isDigit(c) { cp.WriteByte(byte(c)) + } else { + break } - c = l.getc() + c = l.getr() } cp = nil - l.ungetc(c) + l.ungetr(c) - s = LookupBytes(lexbuf.Bytes()) - if s.Lexical == LIGNORE { - goto l0 + name := lexbuf.Bytes() + + if len(name) >= 2 { + if tok, ok := keywords[string(name)]; ok { + if Debug['x'] != 0 { + fmt.Printf("lex: %s\n", lexname(tok)) + } + switch tok { + case LBREAK, LCONTINUE, LFALL, LRETURN: + l.nlsemi = true + } + l.tok = tok + return + } } + s := LookupBytes(name) if Debug['x'] != 0 { - fmt.Printf("lex: %s %s\n", s, lexname(int(s.Lexical))) + fmt.Printf("lex: ident %s\n", s) } l.sym_ = s - switch s.Lexical { - case LNAME, LRETURN, LBREAK, LCONTINUE, LFALL: - l.nlsemi = true - } - l.tok = int32(s.Lexical) - return - -ncu: - cp = nil - l.ungetc(c) - - str = lexbuf.String() - l.val.U = new(Mpint) - mpatofix(l.val.U.(*Mpint), str) - if l.val.U.(*Mpint).Ovf { - Yyerror("overflow in constant") - Mpmovecfix(l.val.U.(*Mpint), 0) - } - - if Debug['x'] != 0 { - fmt.Printf("lex: integer literal\n") - } - litbuf = "literal " + str l.nlsemi = true - l.tok = LLITERAL - return + l.tok = LNAME +} + +var keywords = map[string]int32{ + "break": LBREAK, + "case": LCASE, + "chan": LCHAN, + "const": LCONST, + "continue": LCONTINUE, + "default": LDEFAULT, + "defer": LDEFER, + "else": LELSE, + "fallthrough": LFALL, + "for": LFOR, + "func": LFUNC, + "go": LGO, + "goto": LGOTO, + "if": LIF, + "import": LIMPORT, + "interface": LINTERFACE, + "map": LMAP, + "package": LPACKAGE, + "range": LRANGE, + "return": LRETURN, + "select": LSELECT, + "struct": LSTRUCT, + "switch": LSWITCH, + "type": LTYPE, + "var": LVAR, + + // 💩 + "notwithstanding": LIGNORE, + "thetruthofthematter": LIGNORE, + "despiteallobjections": LIGNORE, + "whereas": LIGNORE, + "insofaras": LIGNORE, +} + +func (l *lexer) number(c rune) { + // TODO(gri) this can be done nicely with fewer or even without labels + + var str string + cp := &lexbuf + cp.Reset() + + if c != '.' { + if c != '0' { + for isDigit(c) { + cp.WriteByte(byte(c)) + c = l.getr() + } + if c == '.' { + goto casedot + } + if c == 'e' || c == 'E' || c == 'p' || c == 'P' { + goto caseep + } + if c == 'i' { + goto casei + } + goto ncu + } + + // c == 0 + cp.WriteByte('0') + c = l.getr() + if c == 'x' || c == 'X' { + cp.WriteByte(byte(c)) + c = l.getr() + for isDigit(c) || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { + cp.WriteByte(byte(c)) + c = l.getr() + } + if lexbuf.Len() == 2 { + Yyerror("malformed hex constant") + } + if c == 'p' { + goto caseep + } + goto ncu + } + + if c == 'p' { // 0p begins floating point zero + goto caseep + } + + has8or9 := false + for isDigit(c) { + if c > '7' { + has8or9 = true + } + cp.WriteByte(byte(c)) + c = l.getr() + } + if c == '.' { + goto casedot + } + if c == 'e' || c == 'E' { + goto caseep + } + if c == 'i' { + goto casei + } + if has8or9 { + Yyerror("malformed octal constant") + } + goto ncu + } casedot: - for { + // fraction + // c == '.' + cp.WriteByte('.') + c = l.getr() + for isDigit(c) { cp.WriteByte(byte(c)) - c = l.getc() - if !isDigit(c) { - break - } + c = l.getr() } - if c == 'i' { goto casei } if c != 'e' && c != 'E' { goto caseout } + // base-2-exponents (p or P) don't appear in numbers + // with fractions - ok to not test for 'p' or 'P' + // above caseep: + // exponent if importpkg == nil && (c == 'p' || c == 'P') { // p is allowed in .a/.o imports, // but not in .go sources. See #9036. Yyerror("malformed floating point constant") } cp.WriteByte(byte(c)) - c = l.getc() + c = l.getr() if c == '+' || c == '-' { cp.WriteByte(byte(c)) - c = l.getc() + c = l.getr() } if !isDigit(c) { @@ -1516,16 +1481,15 @@ caseep: } for isDigit(c) { cp.WriteByte(byte(c)) - c = l.getc() + c = l.getr() } - if c == 'i' { - goto casei + if c != 'i' { + goto caseout } - goto caseout - // imaginary constant casei: + // imaginary constant cp = nil str = lexbuf.String() @@ -1540,14 +1504,11 @@ casei: if Debug['x'] != 0 { fmt.Printf("lex: imaginary literal\n") } - litbuf = "literal " + str - l.nlsemi = true - l.tok = LLITERAL - return + goto done caseout: cp = nil - l.ungetc(c) + l.ungetr(c) str = lexbuf.String() l.val.U = newMpflt() @@ -1560,12 +1521,49 @@ caseout: if Debug['x'] != 0 { fmt.Printf("lex: floating literal\n") } + goto done + +ncu: + cp = nil + l.ungetr(c) + + str = lexbuf.String() + l.val.U = new(Mpint) + mpatofix(l.val.U.(*Mpint), str) + if l.val.U.(*Mpint).Ovf { + Yyerror("overflow in constant") + Mpmovecfix(l.val.U.(*Mpint), 0) + } + + if Debug['x'] != 0 { + fmt.Printf("lex: integer literal\n") + } + +done: litbuf = "literal " + str l.nlsemi = true l.tok = LLITERAL - return +} + +func (l *lexer) stdString() { + lexbuf.Reset() + lexbuf.WriteString(`""`) + + cp := &strbuf + cp.Reset() + + for { + r, b, ok := l.onechar('"') + if !ok { + break + } + if r == 0 { + cp.WriteByte(b) + } else { + cp.WriteRune(r) + } + } -strlit: l.val.U = internString(cp.Bytes()) if Debug['x'] != 0 { fmt.Printf("lex: string literal\n") @@ -1575,6 +1573,64 @@ strlit: l.tok = LLITERAL } +func (l *lexer) rawString() { + lexbuf.Reset() + lexbuf.WriteString("``") + + cp := &strbuf + cp.Reset() + + for { + c := l.getr() + if c == '\r' { + continue + } + if c == EOF { + Yyerror("eof in string") + break + } + if c == '`' { + break + } + cp.WriteRune(c) + } + + l.val.U = internString(cp.Bytes()) + if Debug['x'] != 0 { + fmt.Printf("lex: string literal\n") + } + litbuf = "string literal" + l.nlsemi = true + l.tok = LLITERAL +} + +func (l *lexer) rune() { + r, b, ok := l.onechar('\'') + if !ok { + Yyerror("empty character literal or unescaped ' in character literal") + r = '\'' + } + if r == 0 { + r = rune(b) + } + + if c := l.getr(); c != '\'' { + Yyerror("missing '") + l.ungetr(c) + } + + x := new(Mpint) + l.val.U = x + Mpmovecfix(x, int64(r)) + x.Rune = true + if Debug['x'] != 0 { + fmt.Printf("lex: codepoint literal\n") + } + litbuf = "rune literal" + l.nlsemi = true + l.tok = LLITERAL +} + var internedStrings = map[string]string{} func internString(b []byte) string { @@ -1588,7 +1644,7 @@ func internString(b []byte) string { func more(pp *string) bool { p := *pp - for p != "" && isSpace(int(p[0])) { + for p != "" && isSpace(rune(p[0])) { p = p[1:] } *pp = p @@ -1599,16 +1655,14 @@ func more(pp *string) bool { // //line parse.y:15 // as a discontinuity in sequential line numbers. // the next line of input comes from parse.y:15 -func (l *lexer) getlinepragma() int { - var cmd, verb, name string - - c := int(l.getr()) - if c == 'g' { +func (l *lexer) getlinepragma() rune { + c := l.getr() + if c == 'g' { // check for //go: directive cp := &lexbuf cp.Reset() cp.WriteByte('g') // already read for { - c = int(l.getr()) + c = l.getr() if c == EOF || c >= utf8.RuneSelf { return c } @@ -1625,83 +1679,60 @@ func (l *lexer) getlinepragma() int { pragcgo(text) } - cmd = text - verb = cmd - if i := strings.Index(verb, " "); i >= 0 { + verb := text + if i := strings.Index(text, " "); i >= 0 { verb = verb[:i] } - if verb == "go:linkname" { + switch verb { + case "go:linkname": if !imported_unsafe { Yyerror("//go:linkname only allowed in Go files that import \"unsafe\"") } - f := strings.Fields(cmd) + f := strings.Fields(text) if len(f) != 3 { Yyerror("usage: //go:linkname localname linkname") - return c + break } - Lookup(f[1]).Linkname = f[2] - return c - } - - if verb == "go:nointerface" && obj.Fieldtrack_enabled != 0 { - nointerface = true - return c - } - - if verb == "go:noescape" { - noescape = true - return c - } - - if verb == "go:norace" { - norace = true - return c - } - - if verb == "go:nosplit" { - nosplit = true - return c - } - - if verb == "go:noinline" { - noinline = true - return c - } - - if verb == "go:systemstack" { + case "go:nointerface": + if obj.Fieldtrack_enabled != 0 { + l.pragma |= Nointerface + } + case "go:noescape": + l.pragma |= Noescape + case "go:norace": + l.pragma |= Norace + case "go:nosplit": + l.pragma |= Nosplit + case "go:noinline": + l.pragma |= Noinline + case "go:systemstack": if compiling_runtime == 0 { Yyerror("//go:systemstack only allowed in runtime") } - systemstack = true - return c - } - - if verb == "go:nowritebarrier" { + l.pragma |= Systemstack + case "go:nowritebarrier": if compiling_runtime == 0 { Yyerror("//go:nowritebarrier only allowed in runtime") } - nowritebarrier = true - return c - } - - if verb == "go:nowritebarrierrec" { + l.pragma |= Nowritebarrier + case "go:nowritebarrierrec": if compiling_runtime == 0 { Yyerror("//go:nowritebarrierrec only allowed in runtime") } - nowritebarrierrec = true - nowritebarrier = true // Implies nowritebarrier - return c + l.pragma |= Nowritebarrierrec | Nowritebarrier // implies Nowritebarrier } return c } + + // check for //line directive if c != 'l' { return c } for i := 1; i < 5; i++ { - c = int(l.getr()) - if c != int("line "[i]) { + c = l.getr() + if c != rune("line "[i]) { return c } } @@ -1710,7 +1741,7 @@ func (l *lexer) getlinepragma() int { cp.Reset() linep := 0 for { - c = int(l.getr()) + c = l.getr() if c == EOF { return c } @@ -1725,34 +1756,25 @@ func (l *lexer) getlinepragma() int { } cp.WriteByte(byte(c)) } - cp = nil if linep == 0 { return c } text := strings.TrimSuffix(lexbuf.String(), "\r") - n := 0 - for _, c := range text[linep:] { - if c < '0' || c > '9' { - goto out - } - n = n*10 + int(c) - '0' - if n > 1e8 { - Yyerror("line number out of range") - errorexit() - } + n, err := strconv.Atoi(text[linep:]) + if err != nil { + return c // todo: make this an error instead? it is almost certainly a bug. + } + if n > 1e8 { + Yyerror("line number out of range") + errorexit() } - if n <= 0 { return c } - name = text[:linep-1] - linehistupdate(name, n) - return c - -out: + linehistupdate(text[:linep-1], n) return c } @@ -1763,7 +1785,7 @@ func getimpsym(pp *string) string { return "" } i := 0 - for i < len(p) && !isSpace(int(p[i])) && p[i] != '"' { + for i < len(p) && !isSpace(rune(p[i])) && p[i] != '"' { i++ } sym := p[:i] @@ -1891,145 +1913,119 @@ func pragcgo(text string) { } } -func (l *lexer) getc() int { - c := l.peekc - if c != 0 { - l.peekc = l.peekc1 - l.peekc1 = 0 - goto check - } - -loop: - c = obj.Bgetc(l.bin) - // recognize BOM (U+FEFF): UTF-8 encoding is 0xef 0xbb 0xbf - if c == 0xef { - buf, err := l.bin.Peek(2) - if err != nil { - yyerrorl(int(lexlineno), "illegal UTF-8 sequence ef % x followed by read error (%v)", string(buf), err) - errorexit() +func (l *lexer) getr() rune { + // unread rune != 0 available + if r := l.peekr1; r != 0 { + l.peekr1 = l.peekr2 + l.peekr2 = 0 + if r == '\n' && importpkg == nil { + lexlineno++ } - if buf[0] == 0xbb && buf[1] == 0xbf { - yyerrorl(int(lexlineno), "Unicode (UTF-8) BOM in middle of file") + return r + } - // consume BOM bytes - obj.Bgetc(l.bin) - obj.Bgetc(l.bin) - goto loop +redo: + // common case: 7bit ASCII + c := obj.Bgetc(l.bin) + if c < utf8.RuneSelf { + if c == 0 { + yyerrorl(int(lexlineno), "illegal NUL byte") + return 0 } + if c == '\n' && importpkg == nil { + lexlineno++ + } + return rune(c) + } + // c >= utf8.RuneSelf + + // uncommon case: non-ASCII + var buf [utf8.UTFMax]byte + buf[0] = byte(c) + buf[1] = byte(obj.Bgetc(l.bin)) + i := 2 + for ; i < len(buf) && !utf8.FullRune(buf[:i]); i++ { + buf[i] = byte(obj.Bgetc(l.bin)) } -check: - if c == 0 { - Yyerror("illegal NUL byte") - return 0 + r, w := utf8.DecodeRune(buf[:i]) + if r == utf8.RuneError && w == 1 { + // The string conversion here makes a copy for passing + // to fmt.Printf, so that buf itself does not escape and + // can be allocated on the stack. + yyerrorl(int(lexlineno), "illegal UTF-8 sequence % x", string(buf[:i])) } - if c == '\n' && importpkg == nil { - lexlineno++ + + if r == BOM { + yyerrorl(int(lexlineno), "Unicode (UTF-8) BOM in middle of file") + goto redo } - return c + + return r } -func (l *lexer) ungetc(c int) { - l.peekc1 = l.peekc - l.peekc = c - if c == '\n' && importpkg == nil { +func (l *lexer) ungetr(r rune) { + l.peekr2 = l.peekr1 + l.peekr1 = r + if r == '\n' && importpkg == nil { lexlineno-- } } -func (l *lexer) getr() int32 { - var buf [utf8.UTFMax]byte - - for i := 0; ; i++ { - c := l.getc() - if i == 0 && c < utf8.RuneSelf { - return int32(c) - } - buf[i] = byte(c) - if i+1 == len(buf) || utf8.FullRune(buf[:i+1]) { - r, w := utf8.DecodeRune(buf[:i+1]) - if r == utf8.RuneError && w == 1 { - lineno = lexlineno - // The string conversion here makes a copy for passing - // to fmt.Printf, so that buf itself does not escape and can - // be allocated on the stack. - Yyerror("illegal UTF-8 sequence % x", string(buf[:i+1])) - } - return int32(r) - } - } -} - -func (l *lexer) escchar(e int, escflg *int, val *int64) bool { - *escflg = 0 - - c := int(l.getr()) +// onechar lexes a single character within a rune or interpreted string literal, +// handling escape sequences as necessary. +func (l *lexer) onechar(quote rune) (r rune, b byte, ok bool) { + c := l.getr() switch c { case EOF: Yyerror("eof in string") - return true + l.ungetr(EOF) + return case '\n': Yyerror("newline in string") - return true + l.ungetr('\n') + return case '\\': break + case quote: + return + default: - if c == e { - return true - } - *val = int64(c) - return false + return c, 0, true } - u := 0 - c = int(l.getr()) - var i int + c = l.getr() switch c { case 'x': - *escflg = 1 // it's a byte - i = 2 - goto hex + return 0, byte(l.hexchar(2)), true case 'u': - i = 4 - u = 1 - goto hex + return l.unichar(4), 0, true case 'U': - i = 8 - u = 1 - goto hex + return l.unichar(8), 0, true - case '0', - '1', - '2', - '3', - '4', - '5', - '6', - '7': - *escflg = 1 // it's a byte - x := int64(c) - '0' + case '0', '1', '2', '3', '4', '5', '6', '7': + x := c - '0' for i := 2; i > 0; i-- { - c = l.getc() + c = l.getr() if c >= '0' && c <= '7' { - x = x*8 + int64(c) - '0' + x = x*8 + c - '0' continue } Yyerror("non-octal character in escape sequence: %c", c) - l.ungetc(c) + l.ungetr(c) } if x > 255 { Yyerror("octal escape value > 255: %d", x) } - *val = x - return false + return 0, byte(x), true case 'a': c = '\a' @@ -2049,153 +2045,115 @@ func (l *lexer) escchar(e int, escflg *int, val *int64) bool { c = '\\' default: - if c != e { + if c != quote { Yyerror("unknown escape sequence: %c", c) } } - *val = int64(c) - return false + return c, 0, true +} -hex: - x := int64(0) - for ; i > 0; i-- { - c = l.getc() - if c >= '0' && c <= '9' { - x = x*16 + int64(c) - '0' - continue - } - - if c >= 'a' && c <= 'f' { - x = x*16 + int64(c) - 'a' + 10 - continue - } - - if c >= 'A' && c <= 'F' { - x = x*16 + int64(c) - 'A' + 10 - continue - } - - Yyerror("non-hex character in escape sequence: %c", c) - l.ungetc(c) - break - } - - if u != 0 && (x > utf8.MaxRune || (0xd800 <= x && x < 0xe000)) { +func (l *lexer) unichar(n int) rune { + x := l.hexchar(n) + if x > utf8.MaxRune || 0xd800 <= x && x < 0xe000 { Yyerror("invalid Unicode code point in escape sequence: %#x", x) x = utf8.RuneError } - - *val = x - return false + return rune(x) } -var syms = []struct { - name string - lexical int - etype EType - op Op +func (l *lexer) hexchar(n int) uint32 { + var x uint32 + + for ; n > 0; n-- { + var d uint32 + switch c := l.getr(); { + case isDigit(c): + d = uint32(c - '0') + case 'a' <= c && c <= 'f': + d = uint32(c - 'a' + 10) + case 'A' <= c && c <= 'F': + d = uint32(c - 'A' + 10) + default: + Yyerror("non-hex character in escape sequence: %c", c) + l.ungetr(c) + return x + } + x = x*16 + d + } + + return x +} + +var basicTypes = [...]struct { + name string + etype EType }{ - // basic types - {"int8", LNAME, TINT8, OXXX}, - {"int16", LNAME, TINT16, OXXX}, - {"int32", LNAME, TINT32, OXXX}, - {"int64", LNAME, TINT64, OXXX}, - {"uint8", LNAME, TUINT8, OXXX}, - {"uint16", LNAME, TUINT16, OXXX}, - {"uint32", LNAME, TUINT32, OXXX}, - {"uint64", LNAME, TUINT64, OXXX}, - {"float32", LNAME, TFLOAT32, OXXX}, - {"float64", LNAME, TFLOAT64, OXXX}, - {"complex64", LNAME, TCOMPLEX64, OXXX}, - {"complex128", LNAME, TCOMPLEX128, OXXX}, - {"bool", LNAME, TBOOL, OXXX}, - {"string", LNAME, TSTRING, OXXX}, - {"any", LNAME, TANY, OXXX}, - {"break", LBREAK, Txxx, OXXX}, - {"case", LCASE, Txxx, OXXX}, - {"chan", LCHAN, Txxx, OXXX}, - {"const", LCONST, Txxx, OXXX}, - {"continue", LCONTINUE, Txxx, OXXX}, - {"default", LDEFAULT, Txxx, OXXX}, - {"else", LELSE, Txxx, OXXX}, - {"defer", LDEFER, Txxx, OXXX}, - {"fallthrough", LFALL, Txxx, OXXX}, - {"for", LFOR, Txxx, OXXX}, - {"func", LFUNC, Txxx, OXXX}, - {"go", LGO, Txxx, OXXX}, - {"goto", LGOTO, Txxx, OXXX}, - {"if", LIF, Txxx, OXXX}, - {"import", LIMPORT, Txxx, OXXX}, - {"interface", LINTERFACE, Txxx, OXXX}, - {"map", LMAP, Txxx, OXXX}, - {"package", LPACKAGE, Txxx, OXXX}, - {"range", LRANGE, Txxx, OXXX}, - {"return", LRETURN, Txxx, OXXX}, - {"select", LSELECT, Txxx, OXXX}, - {"struct", LSTRUCT, Txxx, OXXX}, - {"switch", LSWITCH, Txxx, OXXX}, - {"type", LTYPE, Txxx, OXXX}, - {"var", LVAR, Txxx, OXXX}, - {"append", LNAME, Txxx, OAPPEND}, - {"cap", LNAME, Txxx, OCAP}, - {"close", LNAME, Txxx, OCLOSE}, - {"complex", LNAME, Txxx, OCOMPLEX}, - {"copy", LNAME, Txxx, OCOPY}, - {"delete", LNAME, Txxx, ODELETE}, - {"imag", LNAME, Txxx, OIMAG}, - {"len", LNAME, Txxx, OLEN}, - {"make", LNAME, Txxx, OMAKE}, - {"new", LNAME, Txxx, ONEW}, - {"panic", LNAME, Txxx, OPANIC}, - {"print", LNAME, Txxx, OPRINT}, - {"println", LNAME, Txxx, OPRINTN}, - {"real", LNAME, Txxx, OREAL}, - {"recover", LNAME, Txxx, ORECOVER}, - {"notwithstanding", LIGNORE, Txxx, OXXX}, - {"thetruthofthematter", LIGNORE, Txxx, OXXX}, - {"despiteallobjections", LIGNORE, Txxx, OXXX}, - {"whereas", LIGNORE, Txxx, OXXX}, - {"insofaras", LIGNORE, Txxx, OXXX}, + {"int8", TINT8}, + {"int16", TINT16}, + {"int32", TINT32}, + {"int64", TINT64}, + {"uint8", TUINT8}, + {"uint16", TUINT16}, + {"uint32", TUINT32}, + {"uint64", TUINT64}, + {"float32", TFLOAT32}, + {"float64", TFLOAT64}, + {"complex64", TCOMPLEX64}, + {"complex128", TCOMPLEX128}, + {"bool", TBOOL}, + {"string", TSTRING}, + {"any", TANY}, +} + +var builtinFuncs = [...]struct { + name string + op Op +}{ + {"append", OAPPEND}, + {"cap", OCAP}, + {"close", OCLOSE}, + {"complex", OCOMPLEX}, + {"copy", OCOPY}, + {"delete", ODELETE}, + {"imag", OIMAG}, + {"len", OLEN}, + {"make", OMAKE}, + {"new", ONEW}, + {"panic", OPANIC}, + {"print", OPRINT}, + {"println", OPRINTN}, + {"real", OREAL}, + {"recover", ORECOVER}, } // lexinit initializes known symbols and the basic types. func lexinit() { - for _, s := range syms { - lex := s.lexical - s1 := Lookup(s.name) - s1.Lexical = uint16(lex) - - if etype := s.etype; etype != Txxx { - if int(etype) >= len(Types) { - Fatalf("lexinit: %s bad etype", s.name) - } - s2 := Pkglookup(s.name, builtinpkg) - t := Types[etype] - if t == nil { - t = typ(etype) - t.Sym = s2 - - if etype != TANY && etype != TSTRING { - dowidth(t) - } - Types[etype] = t - } - - s2.Lexical = LNAME - s2.Def = typenod(t) - s2.Def.Name = new(Name) - continue + for _, s := range basicTypes { + etype := s.etype + if int(etype) >= len(Types) { + Fatalf("lexinit: %s bad etype", s.name) } + s2 := Pkglookup(s.name, builtinpkg) + t := Types[etype] + if t == nil { + t = typ(etype) + t.Sym = s2 + if etype != TANY && etype != TSTRING { + dowidth(t) + } + Types[etype] = t + } + s2.Def = typenod(t) + s2.Def.Name = new(Name) + } + for _, s := range builtinFuncs { // TODO(marvin): Fix Node.EType type union. - if etype := s.op; etype != OXXX { - s2 := Pkglookup(s.name, builtinpkg) - s2.Lexical = LNAME - s2.Def = Nod(ONAME, nil, nil) - s2.Def.Sym = s2 - s2.Def.Etype = EType(etype) - } + s2 := Pkglookup(s.name, builtinpkg) + s2.Def = Nod(ONAME, nil, nil) + s2.Def.Sym = s2 + s2.Def.Etype = EType(s.op) } // logically, the type of a string literal. @@ -2241,6 +2199,11 @@ func lexinit() { s.Def = nodlit(v) s.Def.Sym = s s.Def.Name = new(Name) + + s = Pkglookup("iota", builtinpkg) + s.Def = Nod(OIOTA, nil, nil) + s.Def.Sym = s + s.Def.Name = new(Name) } func lexinit1() { @@ -2270,121 +2233,46 @@ func lexinit1() { t.Type.Type = f // error type - s := Lookup("error") - - s.Lexical = LNAME - s1 := Pkglookup("error", builtinpkg) + s := Pkglookup("error", builtinpkg) errortype = t - errortype.Sym = s1 - s1.Lexical = LNAME - s1.Def = typenod(errortype) + errortype.Sym = s + s.Def = typenod(errortype) // byte alias - s = Lookup("byte") - - s.Lexical = LNAME - s1 = Pkglookup("byte", builtinpkg) + s = Pkglookup("byte", builtinpkg) bytetype = typ(TUINT8) - bytetype.Sym = s1 - s1.Lexical = LNAME - s1.Def = typenod(bytetype) - s1.Def.Name = new(Name) + bytetype.Sym = s + s.Def = typenod(bytetype) + s.Def.Name = new(Name) // rune alias - s = Lookup("rune") - - s.Lexical = LNAME - s1 = Pkglookup("rune", builtinpkg) + s = Pkglookup("rune", builtinpkg) runetype = typ(TINT32) - runetype.Sym = s1 - s1.Lexical = LNAME - s1.Def = typenod(runetype) - s1.Def.Name = new(Name) -} - -func lexfini() { - for i := range syms { - lex := syms[i].lexical - if lex != LNAME { - continue - } - s := Lookup(syms[i].name) - s.Lexical = uint16(lex) - - etype := syms[i].etype - if etype != Txxx && (etype != TANY || Debug['A'] != 0) && s.Def == nil { - s.Def = typenod(Types[etype]) - s.Def.Name = new(Name) - s.Origpkg = builtinpkg - } - - // TODO(marvin): Fix Node.EType type union. - etype = EType(syms[i].op) - if etype != EType(OXXX) && s.Def == nil { - s.Def = Nod(ONAME, nil, nil) - s.Def.Sym = s - s.Def.Etype = etype - s.Origpkg = builtinpkg - } - } + runetype.Sym = s + s.Def = typenod(runetype) + s.Def.Name = new(Name) // backend-specific builtin types (e.g. int). for i := range Thearch.Typedefs { - s := Lookup(Thearch.Typedefs[i].Name) + s := Pkglookup(Thearch.Typedefs[i].Name, builtinpkg) + s.Def = typenod(Types[Thearch.Typedefs[i].Etype]) + s.Def.Name = new(Name) + s.Origpkg = builtinpkg + } +} + +func lexfini() { + for _, s := range builtinpkg.Syms { if s.Def == nil { - s.Def = typenod(Types[Thearch.Typedefs[i].Etype]) - s.Def.Name = new(Name) - s.Origpkg = builtinpkg + continue + } + s1 := Lookup(s.Name) + if s1.Def != nil { + continue } - } - // there's only so much table-driven we can handle. - // these are special cases. - if s := Lookup("byte"); s.Def == nil { - s.Def = typenod(bytetype) - s.Def.Name = new(Name) - s.Origpkg = builtinpkg - } - - if s := Lookup("error"); s.Def == nil { - s.Def = typenod(errortype) - s.Def.Name = new(Name) - s.Origpkg = builtinpkg - } - - if s := Lookup("rune"); s.Def == nil { - s.Def = typenod(runetype) - s.Def.Name = new(Name) - s.Origpkg = builtinpkg - } - - if s := Lookup("nil"); s.Def == nil { - var v Val - v.U = new(NilVal) - s.Def = nodlit(v) - s.Def.Sym = s - s.Def.Name = new(Name) - s.Origpkg = builtinpkg - } - - if s := Lookup("iota"); s.Def == nil { - s.Def = Nod(OIOTA, nil, nil) - s.Def.Sym = s - s.Origpkg = builtinpkg - } - - if s := Lookup("true"); s.Def == nil { - s.Def = Nodbool(true) - s.Def.Sym = s - s.Def.Name = new(Name) - s.Origpkg = builtinpkg - } - - if s := Lookup("false"); s.Def == nil { - s.Def = Nodbool(false) - s.Def.Sym = s - s.Def.Name = new(Name) - s.Origpkg = builtinpkg + s1.Def = s.Def + s1.Block = s.Block } nodfp = Nod(ONAME, nil, nil) @@ -2394,7 +2282,7 @@ func lexfini() { nodfp.Sym = Lookup(".fp") } -var lexn = map[int]string{ +var lexn = map[rune]string{ LANDAND: "ANDAND", LANDNOT: "ANDNOT", LASOP: "ASOP", @@ -2406,7 +2294,6 @@ var lexn = map[int]string{ LCONST: "CONST", LCONTINUE: "CONTINUE", LDDD: "...", - LDEC: "DEC", LDEFAULT: "DEFAULT", LDEFER: "DEFER", LELSE: "ELSE", @@ -2420,7 +2307,7 @@ var lexn = map[int]string{ LGT: "GT", LIF: "IF", LIMPORT: "IMPORT", - LINC: "INC", + LINCOP: "INCOP", LINTERFACE: "INTERFACE", LLE: "LE", LLITERAL: "LITERAL", @@ -2441,7 +2328,7 @@ var lexn = map[int]string{ LVAR: "VAR", } -func lexname(lex int) string { +func lexname(lex rune) string { if s, ok := lexn[lex]; ok { return s } diff --git a/src/cmd/compile/internal/gc/mparith2.go b/src/cmd/compile/internal/gc/mparith2.go index 28c3a00825..67faf29479 100644 --- a/src/cmd/compile/internal/gc/mparith2.go +++ b/src/cmd/compile/internal/gc/mparith2.go @@ -217,7 +217,11 @@ func mplshfixfix(a, b *Mpint) { s := Mpgetfix(b) if s < 0 || s >= Mpprec { - Yyerror("stupid shift: %d", s) + msg := "shift count too large" + if s < 0 { + msg = "invalid negative shift count" + } + Yyerror("%s: %d", msg, s) Mpmovecfix(a, 0) return } @@ -236,7 +240,7 @@ func mprshfixfix(a, b *Mpint) { s := Mpgetfix(b) if s < 0 { - Yyerror("stupid shift: %d", s) + Yyerror("invalid negative shift count: %d", s) if a.Val.Sign() < 0 { Mpmovecfix(a, -1) } else { diff --git a/src/cmd/compile/internal/gc/mparith3.go b/src/cmd/compile/internal/gc/mparith3.go index 9bcfda7c0d..5b61a9e17f 100644 --- a/src/cmd/compile/internal/gc/mparith3.go +++ b/src/cmd/compile/internal/gc/mparith3.go @@ -11,7 +11,7 @@ import ( "math" ) -/// implements float arihmetic +// implements float arithmetic func newMpflt() *Mpflt { var a Mpflt diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index 12405d5f38..df8fc5976d 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -387,7 +387,7 @@ func ordercall(n *Node, order *Order) { // Ordermapassign appends n to order->out, introducing temporaries // to make sure that all map assignments have the form m[k] = x, -// where x is adressable. +// where x is addressable. // (Orderexpr has already been called on n, so we know k is addressable.) // // If n is m[k] = x where x is not addressable, the rewrite is: @@ -1138,7 +1138,7 @@ func orderexpr(np **Node, order *Order, lhs *Node) { } case OCLOSURE: - if n.Noescape && n.Func.Cvars != nil { + if n.Noescape && len(n.Func.Cvars.Slice()) > 0 { prealloc[n] = ordertemp(Types[TUINT8], order, false) // walk will fill in correct type } diff --git a/src/cmd/compile/internal/gc/parser.go b/src/cmd/compile/internal/gc/parser.go index fbc5a5e1eb..621be57b50 100644 --- a/src/cmd/compile/internal/gc/parser.go +++ b/src/cmd/compile/internal/gc/parser.go @@ -102,6 +102,8 @@ func (p *parser) syntax_error(msg string) { } case LASOP: tok = goopnames[p.op] + "=" + case LINCOP: + tok = goopnames[p.op] + goopnames[p.op] default: tok = tokstring(p.tok) } @@ -110,11 +112,11 @@ func (p *parser) syntax_error(msg string) { } // Like syntax_error, but reports error at given line rather than current lexer line. -func (p *parser) syntax_error_at(lineno int32, msg string) { - defer func(lineno int32) { - lexlineno = lineno - }(lexlineno) - lexlineno = lineno +func (p *parser) syntax_error_at(lno int32, msg string) { + defer func(lno int32) { + lineno = lno + }(lineno) + lineno = lno p.syntax_error(msg) } @@ -219,12 +221,11 @@ var tokstrings = map[int32]string{ LANDAND: "&&", LANDNOT: "&^", LCOMM: "<-", - LDEC: "--", LEQ: "==", LGE: ">=", LGT: ">", LIGNORE: "LIGNORE", // we should never see this one - LINC: "++", + LINCOP: "opop", LLE: "<=", LLSH: "<<", LLT: "<", @@ -280,13 +281,11 @@ func (p *parser) package_() { defer p.trace("package_")() } - if p.got(LPACKAGE) { - mkpackage(p.sym().Name) - } else { - prevlineno = lineno // see issue #13267 + if !p.got(LPACKAGE) { p.syntax_error("package statement must be first") errorexit() } + mkpackage(p.sym().Name) } // ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) . @@ -335,20 +334,22 @@ func (p *parser) importdcl() { } line := int32(parserline()) - path := p.val - p.next() - importfile(&path, p.indent) - if importpkg == nil { + // We need to clear importpkg before calling p.next(), + // otherwise it will affect lexlineno. + // TODO(mdempsky): Fix this clumsy API. + importfile(&p.val, p.indent) + ipkg := importpkg + importpkg = nil + + p.next() + if ipkg == nil { if nerrors == 0 { Fatalf("phase error in import") } return } - ipkg := importpkg - importpkg = nil - ipkg.Direct = true if my == nil { @@ -562,22 +563,13 @@ func (p *parser) simple_stmt(labelOk, rangeOk bool) *Node { stmt.Etype = EType(op) // rathole to pass opcode return stmt - case LINC: - // expr LINC + case LINCOP: + // expr LINCOP p.next() stmt := Nod(OASOP, lhs, Nodintconst(1)) stmt.Implicit = true - stmt.Etype = EType(OADD) - return stmt - - case LDEC: - // expr LDEC - p.next() - - stmt := Nod(OASOP, lhs, Nodintconst(1)) - stmt.Implicit = true - stmt.Etype = EType(OSUB) + stmt.Etype = EType(p.op) return stmt case ':': @@ -689,7 +681,7 @@ func (p *parser) labeled_stmt(label *Node) *Node { ls = p.stmt() if ls == missing_stmt { // report error at line of ':' token - p.syntax_error_at(prevlineno, "missing statement after label") + p.syntax_error_at(label.Lineno, "missing statement after label") // we are already at the end of the labeled statement - no need to advance return missing_stmt } @@ -1104,55 +1096,18 @@ func (p *parser) select_stmt() *Node { return hdr } -// TODO(gri) should have lexer return this info - no need for separate lookup -// (issue 13244) -var prectab = map[int32]struct { - prec int // > 0 (0 indicates not found) - op Op -}{ - // not an expression anymore, but left in so we can give a good error - // message when used in expression context - LCOMM: {1, OSEND}, - - LOROR: {2, OOROR}, - - LANDAND: {3, OANDAND}, - - LEQ: {4, OEQ}, - LNE: {4, ONE}, - LLE: {4, OLE}, - LGE: {4, OGE}, - LLT: {4, OLT}, - LGT: {4, OGT}, - - '+': {5, OADD}, - '-': {5, OSUB}, - '|': {5, OOR}, - '^': {5, OXOR}, - - '*': {6, OMUL}, - '/': {6, ODIV}, - '%': {6, OMOD}, - '&': {6, OAND}, - LLSH: {6, OLSH}, - LRSH: {6, ORSH}, - LANDNOT: {6, OANDNOT}, -} - // Expression = UnaryExpr | Expression binary_op Expression . -func (p *parser) bexpr(prec int) *Node { +func (p *parser) bexpr(prec OpPrec) *Node { // don't trace bexpr - only leads to overly nested trace output + // prec is precedence of the prior/enclosing binary operator (if any), + // so we only want to parse tokens of greater precedence. + x := p.uexpr() - t := prectab[p.tok] - for tprec := t.prec; tprec >= prec; tprec-- { - for tprec == prec { - p.next() - y := p.bexpr(t.prec + 1) - x = Nod(t.op, x, y) - t = prectab[p.tok] - tprec = t.prec - } + for p.prec > prec { + op, prec1 := p.op, p.prec + p.next() + x = Nod(op, x, p.bexpr(prec1)) } return x } @@ -1162,7 +1117,7 @@ func (p *parser) expr() *Node { defer p.trace("expr")() } - return p.bexpr(1) + return p.bexpr(0) } func unparen(x *Node) *Node { @@ -1611,13 +1566,15 @@ func (p *parser) new_name(sym *Sym) *Node { return nil } -func (p *parser) dcl_name(sym *Sym) *Node { +func (p *parser) dcl_name() *Node { if trace && Debug['x'] != 0 { defer p.trace("dcl_name")() } + symlineno := lineno + sym := p.sym() if sym == nil { - yyerrorl(int(prevlineno), "invalid declaration") + yyerrorl(int(symlineno), "invalid declaration") return nil } return dclname(sym) @@ -1894,25 +1851,21 @@ func (p *parser) xfndcl() *Node { } p.want(LFUNC) - f := p.fndcl() + f := p.fndcl(p.pragma&Nointerface != 0) body := p.fnbody() if f == nil { return nil } - if noescape && body != nil { - Yyerror("can only use //go:noescape with external func implementations") - } f.Nbody = body + f.Noescape = p.pragma&Noescape != 0 + if f.Noescape && body != nil { + Yyerror("can only use //go:noescape with external func implementations") + } + f.Func.Pragma = p.pragma f.Func.Endlineno = lineno - f.Noescape = noescape - f.Func.Norace = norace - f.Func.Nosplit = nosplit - f.Func.Noinline = noinline - f.Func.Nowritebarrier = nowritebarrier - f.Func.Nowritebarrierrec = nowritebarrierrec - f.Func.Systemstack = systemstack + funcbody(f) return f @@ -1923,7 +1876,7 @@ func (p *parser) xfndcl() *Node { // Function = Signature FunctionBody . // MethodDecl = "func" Receiver MethodName ( Function | Signature ) . // Receiver = Parameters . -func (p *parser) fndcl() *Node { +func (p *parser) fndcl(nointerface bool) *Node { if trace && Debug['x'] != 0 { defer p.trace("fndcl")() } @@ -2059,8 +2012,7 @@ func (p *parser) hidden_fndcl() *Node { ss.Type = functype(s2.N, s6, s8) checkwidth(ss.Type) - addmethod(s4, ss.Type, false, nointerface) - nointerface = false + addmethod(s4, ss.Type, false, false) funchdr(ss) // inl.C's inlnode in on a dotmeth node expects to find the inlineable body as @@ -2141,18 +2093,10 @@ loop: testdclstack() } - noescape = false - noinline = false - nointerface = false - norace = false - nosplit = false - nowritebarrier = false - nowritebarrierrec = false - systemstack = false + // Reset p.pragma BEFORE advancing to the next token (consuming ';') + // since comments before may set pragmas for the next function decl. + p.pragma = 0 - // Consume ';' AFTER resetting the above flags since - // it may read the subsequent comment line which may - // set the flags for the next function declaration. if p.tok != EOF && !p.got(';') { p.syntax_error("after top level declaration") p.advance(LVAR, LCONST, LTYPE, LFUNC) @@ -2567,15 +2511,15 @@ func (p *parser) stmt() *Node { stmt := Nod(ORETURN, nil, nil) stmt.List = results if stmt.List == nil && Curfn != nil { - for l := Curfn.Func.Dcl; l != nil; l = l.Next { - if l.N.Class == PPARAM { + for _, ln := range Curfn.Func.Dcl { + if ln.Class == PPARAM { continue } - if l.N.Class != PPARAMOUT { + if ln.Class != PPARAMOUT { break } - if l.N.Sym.Def != l.N { - Yyerror("%s is shadowed during return", l.N.Sym.Name) + if ln.Sym.Def != ln { + Yyerror("%s is shadowed during return", ln.Sym.Name) } } } @@ -2639,9 +2583,9 @@ func (p *parser) dcl_name_list() *NodeList { defer p.trace("dcl_name_list")() } - l := list1(p.dcl_name(p.sym())) + l := list1(p.dcl_name()) for p.got(',') { - l = list(l, p.dcl_name(p.sym())) + l = list(l, p.dcl_name()) } return l } diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index 987da2bfdd..a8b08ab309 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -9,6 +9,7 @@ import ( "cmd/internal/obj" "crypto/md5" "fmt" + "sort" "strings" ) @@ -218,6 +219,13 @@ func cmpstackvarlt(a, b *Node) bool { return a.Sym.Name < b.Sym.Name } +// byStackvar implements sort.Interface for []*Node using cmpstackvarlt. +type byStackVar []*Node + +func (s byStackVar) Len() int { return len(s) } +func (s byStackVar) Less(i, j int) bool { return cmpstackvarlt(s[i], s[j]) } +func (s byStackVar) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + // stkdelta records the stack offset delta for a node // during the compaction of the stack frame to remove // unused stack slots. @@ -228,25 +236,23 @@ func allocauto(ptxt *obj.Prog) { Stksize = 0 stkptrsize = 0 - if Curfn.Func.Dcl == nil { + if len(Curfn.Func.Dcl) == 0 { return } // Mark the PAUTO's unused. - for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { - if ll.N.Class == PAUTO { - ll.N.Used = false + for _, ln := range Curfn.Func.Dcl { + if ln.Class == PAUTO { + ln.Used = false } } markautoused(ptxt) - listsort(&Curfn.Func.Dcl, cmpstackvarlt) + sort.Sort(byStackVar(Curfn.Func.Dcl)) // Unused autos are at the end, chop 'em off. - ll := Curfn.Func.Dcl - - n := ll.N + n := Curfn.Func.Dcl[0] if n.Class == PAUTO && n.Op == ONAME && !n.Used { // No locals used at all Curfn.Func.Dcl = nil @@ -255,19 +261,17 @@ func allocauto(ptxt *obj.Prog) { return } - for ll := Curfn.Func.Dcl; ll.Next != nil; ll = ll.Next { - n = ll.Next.N + for i := 1; i < len(Curfn.Func.Dcl); i++ { + n = Curfn.Func.Dcl[i] if n.Class == PAUTO && n.Op == ONAME && !n.Used { - ll.Next = nil - Curfn.Func.Dcl.End = ll + Curfn.Func.Dcl = Curfn.Func.Dcl[:i] break } } // Reassign stack offsets of the locals that are still there. var w int64 - for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { - n = ll.N + for _, n := range Curfn.Func.Dcl { if n.Class != PAUTO || n.Op != ONAME { continue } @@ -299,12 +303,12 @@ func allocauto(ptxt *obj.Prog) { fixautoused(ptxt) // The debug information needs accurate offsets on the symbols. - for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { - if ll.N.Class != PAUTO || ll.N.Op != ONAME { + for _, ln := range Curfn.Func.Dcl { + if ln.Class != PAUTO || ln.Op != ONAME { continue } - ll.N.Xoffset += stkdelta[ll.N] - delete(stkdelta, ll.N) + ln.Xoffset += stkdelta[ln] + delete(stkdelta, ln) } } @@ -442,10 +446,10 @@ func compile(fn *Node) { if fn.Func.Needctxt { ptxt.From3.Offset |= obj.NEEDCTXT } - if fn.Func.Nosplit { + if fn.Func.Pragma&Nosplit != 0 { ptxt.From3.Offset |= obj.NOSPLIT } - if fn.Func.Systemstack { + if fn.Func.Pragma&Systemstack != 0 { ptxt.From.Sym.Cfunc = 1 } @@ -467,16 +471,15 @@ func compile(fn *Node) { gtrack(tracksym(t)) } - for l := fn.Func.Dcl; l != nil; l = l.Next { - n = l.N + for _, n := range fn.Func.Dcl { if n.Op != ONAME { // might be OTYPE or OLITERAL continue } switch n.Class { case PAUTO, PPARAM, PPARAMOUT: - Nodconst(&nod1, Types[TUINTPTR], l.N.Type.Width) - p = Thearch.Gins(obj.ATYPE, l.N, &nod1) - p.From.Gotype = Linksym(ngotype(l.N)) + Nodconst(&nod1, Types[TUINTPTR], n.Type.Width) + p = Thearch.Gins(obj.ATYPE, n, &nod1) + p.From.Gotype = Linksym(ngotype(n)) } } @@ -488,7 +491,7 @@ func compile(fn *Node) { ssafn.Free() return } - Genlist(Curfn.Func.Enter) + Genslice(Curfn.Func.Enter.Slice()) Genlist(Curfn.Nbody) gclean() checklabels() diff --git a/src/cmd/compile/internal/gc/plive.go b/src/cmd/compile/internal/gc/plive.go index 458497dcff..78872c1af2 100644 --- a/src/cmd/compile/internal/gc/plive.go +++ b/src/cmd/compile/internal/gc/plive.go @@ -122,7 +122,7 @@ func addedge(from *BasicBlock, to *BasicBlock) { } // Inserts prev before curr in the instruction -// stream. Any control flow, such as branches or fall throughs, that target the +// stream. Any control flow, such as branches or fall-throughs, that target the // existing instruction are adjusted to target the new instruction. func splicebefore(lv *Liveness, bb *BasicBlock, prev *obj.Prog, curr *obj.Prog) { // There may be other instructions pointing at curr, @@ -198,8 +198,8 @@ func blockany(bb *BasicBlock, f func(*obj.Prog) bool) bool { // variables. func getvariables(fn *Node) []*Node { result := make([]*Node, 0, 0) - for ll := fn.Func.Dcl; ll != nil; ll = ll.Next { - if ll.N.Op == ONAME { + for _, ln := range fn.Func.Dcl { + if ln.Op == ONAME { // In order for GODEBUG=gcdead=1 to work, each bitmap needs // to contain information about all variables covered by the bitmap. // For local variables, the bitmap only covers the stkptrsize @@ -219,24 +219,24 @@ func getvariables(fn *Node) []*Node { // Later, when we want to find the index of a node in the variables list, // we will check that n->curfn == curfn and n->opt > 0. Then n->opt - 1 // is the index in the variables list. - ll.N.SetOpt(nil) + ln.SetOpt(nil) // The compiler doesn't emit initializations for zero-width parameters or results. - if ll.N.Type.Width == 0 { + if ln.Type.Width == 0 { continue } - ll.N.Name.Curfn = Curfn - switch ll.N.Class { + ln.Name.Curfn = Curfn + switch ln.Class { case PAUTO: - if haspointers(ll.N.Type) { - ll.N.SetOpt(int32(len(result))) - result = append(result, ll.N) + if haspointers(ln.Type) { + ln.SetOpt(int32(len(result))) + result = append(result, ln) } case PPARAM, PPARAMOUT: - ll.N.SetOpt(int32(len(result))) - result = append(result, ll.N) + ln.SetOpt(int32(len(result))) + result = append(result, ln) } } } @@ -798,8 +798,8 @@ func livenessprintcfg(lv *Liveness) { } func checkauto(fn *Node, p *obj.Prog, n *Node) { - for l := fn.Func.Dcl; l != nil; l = l.Next { - if l.N.Op == ONAME && l.N.Class == PAUTO && l.N == n { + for _, ln := range fn.Func.Dcl { + if ln.Op == ONAME && ln.Class == PAUTO && ln == n { return } } @@ -810,8 +810,8 @@ func checkauto(fn *Node, p *obj.Prog, n *Node) { } fmt.Printf("checkauto %v: %v (%p; class=%d) not found in %p %v\n", funcSym(Curfn), n, n, n.Class, p, p) - for l := fn.Func.Dcl; l != nil; l = l.Next { - fmt.Printf("\t%v (%p; class=%d)\n", l.N, l.N, l.N.Class) + for _, ln := range fn.Func.Dcl { + fmt.Printf("\t%v (%p; class=%d)\n", ln, ln, ln.Class) } Yyerror("checkauto: invariant lost") } @@ -820,10 +820,8 @@ func checkparam(fn *Node, p *obj.Prog, n *Node) { if isfunny(n) { return } - var a *Node var class Class - for l := fn.Func.Dcl; l != nil; l = l.Next { - a = l.N + for _, a := range fn.Func.Dcl { class = a.Class &^ PHEAP if a.Op == ONAME && (class == PPARAM || class == PPARAMOUT) && a == n { return @@ -831,8 +829,8 @@ func checkparam(fn *Node, p *obj.Prog, n *Node) { } fmt.Printf("checkparam %v: %v (%p; class=%d) not found in %v\n", Curfn, n, n, n.Class, p) - for l := fn.Func.Dcl; l != nil; l = l.Next { - fmt.Printf("\t%v (%p; class=%d)\n", l.N, l.N, l.N.Class) + for _, ln := range fn.Func.Dcl { + fmt.Printf("\t%v (%p; class=%d)\n", ln, ln, ln.Class) } Yyerror("checkparam: invariant lost") } @@ -1815,9 +1813,9 @@ func liveness(fn *Node, firstp *obj.Prog, argssym *Sym, livesym *Sym) { onebitwritesymbol(lv.argslivepointers, argssym) // Free everything. - for l := fn.Func.Dcl; l != nil; l = l.Next { - if l.N != nil { - l.N.SetOpt(nil) + for _, ln := range fn.Func.Dcl { + if ln != nil { + ln.SetOpt(nil) } } freeliveness(lv) diff --git a/src/cmd/compile/internal/gc/popt.go b/src/cmd/compile/internal/gc/popt.go index b708222845..0a2d8c45d4 100644 --- a/src/cmd/compile/internal/gc/popt.go +++ b/src/cmd/compile/internal/gc/popt.go @@ -138,15 +138,16 @@ func fixjmp(firstp *obj.Prog) { fmt.Printf("%v\n", p) } if p.As != obj.ACALL && p.To.Type == obj.TYPE_BRANCH && p.To.Val.(*obj.Prog) != nil && p.To.Val.(*obj.Prog).As == obj.AJMP { - p.To.Val = chasejmp(p.To.Val.(*obj.Prog), &jmploop) - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("->%v\n", p) + if Debug['N'] == 0 { + p.To.Val = chasejmp(p.To.Val.(*obj.Prog), &jmploop) + if Debug['R'] != 0 && Debug['v'] != 0 { + fmt.Printf("->%v\n", p) + } } } p.Opt = dead } - if Debug['R'] != 0 && Debug['v'] != 0 { fmt.Printf("\n") } @@ -186,7 +187,7 @@ func fixjmp(firstp *obj.Prog) { // pass 4: elide JMP to next instruction. // only safe if there are no jumps to JMPs anymore. - if jmploop == 0 { + if jmploop == 0 && Debug['N'] == 0 { var last *obj.Prog for p := firstp; p != nil; p = p.Link { if p.As == obj.AJMP && p.To.Type == obj.TYPE_BRANCH && p.To.Val == p.Link { @@ -217,22 +218,22 @@ func fixjmp(firstp *obj.Prog) { // Control flow analysis. The Flow structures hold predecessor and successor // information as well as basic loop analysis. // -// graph = flowstart(firstp, 0); +// graph = Flowstart(firstp, nil) // ... use flow graph ... -// flowend(graph); // free graph +// Flowend(graph) // free graph // // Typical uses of the flow graph are to iterate over all the flow-relevant instructions: // -// for(f = graph->start; f != nil; f = f->link) +// for f := graph.Start; f != nil; f = f.Link {} // // or, given an instruction f, to iterate over all the predecessors, which is -// f->p1 and this list: +// f.P1 and this list: // -// for(f2 = f->p2; f2 != nil; f2 = f2->p2link) +// for f2 := f.P2; f2 != nil; f2 = f2.P2link {} // -// The size argument to flowstart specifies an amount of zeroed memory -// to allocate in every f->data field, for use by the client. -// If size == 0, f->data will be nil. +// The second argument (newData) to Flowstart specifies a func to create object +// for every f.Data field, for use by the client. +// If newData is nil, f.Data will be nil. var flowmark int @@ -471,8 +472,8 @@ func flowrpo(g *Graph) { me = r1.Rpo d = -1 - // rpo2r[r->rpo] == r protects against considering dead code, - // which has r->rpo == 0. + // rpo2r[r.Rpo] == r protects against considering dead code, + // which has r.Rpo == 0. if r1.P1 != nil && rpo2r[r1.P1.Rpo] == r1.P1 && r1.P1.Rpo < me { d = r1.P1.Rpo } @@ -588,8 +589,8 @@ func mergetemp(firstp *obj.Prog) { // Build list of all mergeable variables. var vars []*TempVar - for l := Curfn.Func.Dcl; l != nil; l = l.Next { - if n := l.N; canmerge(n) { + for _, n := range Curfn.Func.Dcl { + if canmerge(n) { v := &TempVar{} vars = append(vars, v) n.SetOpt(v) @@ -684,7 +685,7 @@ func mergetemp(firstp *obj.Prog) { // Traverse live range of each variable to set start, end. // Each flood uses a new value of gen so that we don't have - // to clear all the r->active words after each variable. + // to clear all the r.Active words after each variable. gen := uint32(0) for _, v := range vars { @@ -818,22 +819,15 @@ func mergetemp(firstp *obj.Prog) { } // Delete merged nodes from declaration list. - for lp := &Curfn.Func.Dcl; ; { - l := *lp - if l == nil { - break - } - - Curfn.Func.Dcl.End = l - n := l.N + dcl := make([]*Node, 0, len(Curfn.Func.Dcl)-nkill) + for _, n := range Curfn.Func.Dcl { v, _ := n.Opt().(*TempVar) if v != nil && (v.merge != nil || v.removed) { - *lp = l.Next continue } - - lp = &l.Next + dcl = append(dcl, n) } + Curfn.Func.Dcl = dcl // Clear aux structures. for _, v := range vars { @@ -910,7 +904,7 @@ func varkillwalk(v *TempVar, f0 *Flow, gen uint32) { // from memory without being rechecked. Other variables need to be checked on // each load. -var killed int // f->data is either nil or &killed +var killed int // f.Data is either nil or &killed func nilopt(firstp *obj.Prog) { g := Flowstart(firstp, nil) diff --git a/src/cmd/compile/internal/gc/racewalk.go b/src/cmd/compile/internal/gc/racewalk.go index ee4f3baffd..e1b3ae6879 100644 --- a/src/cmd/compile/internal/gc/racewalk.go +++ b/src/cmd/compile/internal/gc/racewalk.go @@ -50,7 +50,7 @@ func ispkgin(pkgs []string) bool { } func instrument(fn *Node) { - if ispkgin(omit_pkgs) || fn.Func.Norace { + if ispkgin(omit_pkgs) || fn.Func.Pragma&Norace != 0 { return } @@ -58,7 +58,7 @@ func instrument(fn *Node) { instrumentlist(fn.Nbody, nil) // nothing interesting for race detector in fn->enter - instrumentlist(fn.Func.Exit, nil) + instrumentslice(fn.Func.Exit.Slice(), nil) } if flag_race != 0 { @@ -71,18 +71,18 @@ func instrument(fn *Node) { nodpc.Type = Types[TUINTPTR] nodpc.Xoffset = int64(-Widthptr) nd := mkcall("racefuncenter", nil, nil, nodpc) - fn.Func.Enter = concat(list1(nd), fn.Func.Enter) + fn.Func.Enter.Set(append([]*Node{nd}, fn.Func.Enter.Slice()...)) nd = mkcall("racefuncexit", nil, nil) - fn.Func.Exit = list(fn.Func.Exit, nd) + fn.Func.Exit.Append(nd) } if Debug['W'] != 0 { s := fmt.Sprintf("after instrument %v", fn.Func.Nname.Sym) dumplist(s, fn.Nbody) s = fmt.Sprintf("enter %v", fn.Func.Nname.Sym) - dumplist(s, fn.Func.Enter) + dumpslice(s, fn.Func.Enter.Slice()) s = fmt.Sprintf("exit %v", fn.Func.Nname.Sym) - dumplist(s, fn.Func.Exit) + dumpslice(s, fn.Func.Exit.Slice()) } } @@ -100,6 +100,18 @@ func instrumentlist(l *NodeList, init **NodeList) { } } +func instrumentslice(l []*Node, init **NodeList) { + for i := range l { + var instr *NodeList + instrumentnode(&l[i], &instr, 0, 0) + if init == nil { + l[i].Ninit = concat(l[i].Ninit, instr) + } else { + *init = concat(*init, instr) + } + } +} + // walkexpr and walkstmt combined // walks the tree and adds calls to the // instrumentation code to top-level (statement) nodes' init diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index 37c3bc9e6d..43c6db0a00 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -491,16 +491,10 @@ func dextratype(sym *Sym, off int, t *Type, ptroff int) int { ot := off s := sym - if t.Sym != nil { - ot = dgostringptr(s, ot, t.Sym.Name) - if t != Types[t.Etype] && t != errortype { - ot = dgopkgpath(s, ot, t.Sym.Pkg) - } else { - ot = dgostringptr(s, ot, "") - } + if t.Sym != nil && t != Types[t.Etype] && t != errortype { + ot = dgopkgpath(s, ot, t.Sym.Pkg) } else { ot = dgostringptr(s, ot, "") - ot = dgostringptr(s, ot, "") } // slice header @@ -700,12 +694,14 @@ func dcommontype(s *Sym, ot int, t *Type) int { algsym = dalgsym(t) } - var sptr *Sym tptr := Ptrto(t) if !Isptr[t.Etype] && (t.Sym != nil || methods(tptr) != nil) { - sptr = dtypesym(tptr) - } else { - sptr = weaktypesym(tptr) + sptr := dtypesym(tptr) + r := obj.Addrel(Linksym(s)) + r.Off = 0 + r.Siz = 0 + r.Sym = sptr.Lsym + r.Type = obj.R_USETYPE } gcsym, useGCProg, ptrdata := dgcsym(t) @@ -724,7 +720,6 @@ func dcommontype(s *Sym, ot int, t *Type) int { // gcdata *byte // string *string // *uncommonType - // ptrToThis *rtype // } ot = duintptr(s, ot, uint64(t.Width)) ot = duintptr(s, ot, uint64(ptrdata)) @@ -763,12 +758,14 @@ func dcommontype(s *Sym, ot int, t *Type) int { } else { ot = dsymptr(s, ot, algsym, 0) } - ot = dsymptr(s, ot, gcsym, 0) + ot = dsymptr(s, ot, gcsym, 0) // gcdata p := Tconv(t, obj.FmtLeft|obj.FmtUnsigned) - //print("dcommontype: %s\n", p); - ot = dgostringptr(s, ot, p) // string + _, symdata := stringsym(p) // string + ot = dsymptr(s, ot, symdata, 0) + ot = duintxx(s, ot, uint64(len(p)), Widthint) + //fmt.Printf("dcommontype: %s\n", p) // skip pointer to extraType, // which follows the rest of this type structure. @@ -776,7 +773,6 @@ func dcommontype(s *Sym, ot int, t *Type) int { // otherwise linker will assume 0. ot += Widthptr - ot = dsymptr(s, ot, sptr, 0) // ptrto type return ot } @@ -1006,7 +1002,7 @@ ok: switch t.Etype { default: ot = dcommontype(s, ot, t) - xt = ot - 2*Widthptr + xt = ot - 1*Widthptr case TARRAY: if t.Bound >= 0 { @@ -1018,7 +1014,7 @@ ok: t2.Bound = -1 // slice s2 := dtypesym(t2) ot = dcommontype(s, ot, t) - xt = ot - 2*Widthptr + xt = ot - 1*Widthptr ot = dsymptr(s, ot, s1, 0) ot = dsymptr(s, ot, s2, 0) ot = duintptr(s, ot, uint64(t.Bound)) @@ -1027,7 +1023,7 @@ ok: s1 := dtypesym(t.Type) ot = dcommontype(s, ot, t) - xt = ot - 2*Widthptr + xt = ot - 1*Widthptr ot = dsymptr(s, ot, s1, 0) } @@ -1036,7 +1032,7 @@ ok: s1 := dtypesym(t.Type) ot = dcommontype(s, ot, t) - xt = ot - 2*Widthptr + xt = ot - 1*Widthptr ot = dsymptr(s, ot, s1, 0) ot = duintptr(s, ot, uint64(t.Chan)) @@ -1055,7 +1051,7 @@ ok: } ot = dcommontype(s, ot, t) - xt = ot - 2*Widthptr + xt = ot - 1*Widthptr ot = duint8(s, ot, uint8(obj.Bool2int(isddd))) // two slice headers: in and out. @@ -1093,7 +1089,7 @@ ok: // ../../../../runtime/type.go:/interfaceType ot = dcommontype(s, ot, t) - xt = ot - 2*Widthptr + xt = ot - 1*Widthptr ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint) ot = duintxx(s, ot, uint64(n), Widthint) ot = duintxx(s, ot, uint64(n), Widthint) @@ -1113,7 +1109,7 @@ ok: s3 := dtypesym(mapbucket(t)) s4 := dtypesym(hmap(t)) ot = dcommontype(s, ot, t) - xt = ot - 2*Widthptr + xt = ot - 1*Widthptr ot = dsymptr(s, ot, s1, 0) ot = dsymptr(s, ot, s2, 0) ot = dsymptr(s, ot, s3, 0) @@ -1150,7 +1146,7 @@ ok: s1 := dtypesym(t.Type) ot = dcommontype(s, ot, t) - xt = ot - 2*Widthptr + xt = ot - 1*Widthptr ot = dsymptr(s, ot, s1, 0) // ../../../../runtime/type.go:/structType @@ -1164,7 +1160,7 @@ ok: } ot = dcommontype(s, ot, t) - xt = ot - 2*Widthptr + xt = ot - 1*Widthptr ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint) ot = duintxx(s, ot, uint64(n), Widthint) ot = duintxx(s, ot, uint64(n), Widthint) @@ -1203,21 +1199,7 @@ ok: // we want be able to find. if t.Sym == nil { switch t.Etype { - case TPTR32, TPTR64: - // The ptrto field of the type data cannot be relied on when - // dynamic linking: a type T may be defined in a module that makes - // no use of pointers to that type, but another module can contain - // a package that imports the first one and does use *T pointers. - // The second module will end up defining type data for *T and a - // type.*T symbol pointing at it. It's important that calling - // .PtrTo() on the reflect.Type for T returns this type data and - // not some synthesized object, so we need reflect to be able to - // find it! - if !Ctxt.Flag_dynlink { - break - } - fallthrough - case TARRAY, TCHAN, TFUNC, TMAP: + case TPTR32, TPTR64, TARRAY, TCHAN, TFUNC, TMAP: slink := typelinksym(t) dsymptr(slink, 0, s, 0) ggloblsym(slink, int32(Widthptr), int16(dupok|obj.RODATA)) @@ -1377,7 +1359,7 @@ func dalgsym(t *Type) *Sym { // be multiples of four words. On 32-bit systems that's 16 bytes, and // all size classes >= 16 bytes are 16-byte aligned, so no real constraint. // On 64-bit systems, that's 32 bytes, and 32-byte alignment is guaranteed -// for size classes >= 256 bytes. On a 64-bit sytem, 256 bytes allocated +// for size classes >= 256 bytes. On a 64-bit system, 256 bytes allocated // is 32 pointers, the bits for which fit in 4 bytes. So maxPtrmaskBytes // must be >= 4. // diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go index 8af6ba24a2..07177812f6 100644 --- a/src/cmd/compile/internal/gc/sinit.go +++ b/src/cmd/compile/internal/gc/sinit.go @@ -475,7 +475,7 @@ func staticassign(l *Node, r *Node, out **NodeList) bool { break case OCLOSURE: - if r.Func.Cvars == nil { + if len(r.Func.Cvars.Slice()) == 0 { // Closures with no captured variables are globals, // so the assignment can be done at link time. n := *l diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index e81ca14571..4399470471 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -84,9 +84,9 @@ func buildssa(fn *Node) *ssa.Func { printssa := strings.HasSuffix(name, "_ssa") || strings.Contains(name, "_ssa.") || name == os.Getenv("GOSSAFUNC") if printssa { fmt.Println("generating SSA for", name) - dumplist("buildssa-enter", fn.Func.Enter) + dumpslice("buildssa-enter", fn.Func.Enter.Slice()) dumplist("buildssa-body", fn.Nbody) - dumplist("buildssa-exit", fn.Func.Exit) + dumpslice("buildssa-exit", fn.Func.Exit.Slice()) } var s state @@ -132,8 +132,7 @@ func buildssa(fn *Node) *ssa.Func { // Generate addresses of local declarations s.decladdrs = map[*Node]*ssa.Value{} - for d := fn.Func.Dcl; d != nil; d = d.Next { - n := d.N + for _, n := range fn.Func.Dcl { switch n.Class { case PPARAM: aux := s.lookupSymbol(n, &ssa.ArgSymbol{Typ: n.Type, Node: n}) @@ -159,12 +158,12 @@ func buildssa(fn *Node) *ssa.Func { } // Convert the AST-based IR to the SSA-based IR - s.stmtList(fn.Func.Enter) + s.stmts(fn.Func.Enter) s.stmtList(fn.Nbody) // fallthrough to exit if s.curBlock != nil { - s.stmtList(s.exitCode) + s.stmts(s.exitCode) m := s.mem() b := s.endBlock() b.Kind = ssa.BlockRet @@ -201,7 +200,7 @@ func buildssa(fn *Node) *ssa.Func { s.linkForwardReferences() // Don't carry reference this around longer than necessary - s.exitCode = nil + s.exitCode = Nodes{} // Main call to ssa package to compile function ssa.Compile(s.f) @@ -224,7 +223,7 @@ type state struct { fwdGotos []*Node // Code that must precede any return // (e.g., copying value of heap-escaped paramout back to true paramout) - exitCode *NodeList + exitCode Nodes // unlabeled break and continue statement tracking breakTo *ssa.Block // current target for plain break statement @@ -479,6 +478,12 @@ func (s *state) constInt(t ssa.Type, c int64) *ssa.Value { return s.constInt32(t, int32(c)) } +func (s *state) stmts(a Nodes) { + for _, x := range a.Slice() { + s.stmt(x) + } +} + // ssaStmtList converts the statement n to SSA and adds it to s. func (s *state) stmtList(l *NodeList) { for ; l != nil; l = l.Next { @@ -697,14 +702,14 @@ func (s *state) stmt(n *Node) { case ORETURN: s.stmtList(n.List) - s.stmtList(s.exitCode) + s.stmts(s.exitCode) m := s.mem() b := s.endBlock() b.Kind = ssa.BlockRet b.Control = m case ORETJMP: s.stmtList(n.List) - s.stmtList(s.exitCode) + s.stmts(s.exitCode) m := s.mem() b := s.endBlock() b.Kind = ssa.BlockRetJmp diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index b6a26489e6..6c558203d3 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -19,7 +19,6 @@ import ( type Error struct { lineno int - seq int msg string } @@ -49,35 +48,24 @@ func adderrorname(n *Node) { func adderr(line int, format string, args ...interface{}) { errors = append(errors, Error{ - seq: len(errors), lineno: line, msg: fmt.Sprintf("%v: %s\n", Ctxt.Line(line), fmt.Sprintf(format, args...)), }) } -// errcmp sorts errors by line, then seq, then message. -type errcmp []Error +// byLineno sorts errors by lineno. +type byLineno []Error -func (x errcmp) Len() int { return len(x) } -func (x errcmp) Swap(i, j int) { x[i], x[j] = x[j], x[i] } -func (x errcmp) Less(i, j int) bool { - a := &x[i] - b := &x[j] - if a.lineno != b.lineno { - return a.lineno < b.lineno - } - if a.seq != b.seq { - return a.seq < b.seq - } - return a.msg < b.msg -} +func (x byLineno) Len() int { return len(x) } +func (x byLineno) Less(i, j int) bool { return x[i].lineno < x[j].lineno } +func (x byLineno) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func Flusherrors() { bstdout.Flush() if len(errors) == 0 { return } - sort.Sort(errcmp(errors)) + sort.Stable(byLineno(errors)) for i := 0; i < len(errors); i++ { if i == 0 || errors[i].msg != errors[i-1].msg { fmt.Printf("%s", errors[i].msg) @@ -109,7 +97,7 @@ func yyerrorl(line int, format string, args ...interface{}) { } } -var yyerror_lastsyntax int +var yyerror_lastsyntax int32 func Yyerror(format string, args ...interface{}) { msg := fmt.Sprintf(format, args...) @@ -117,18 +105,12 @@ func Yyerror(format string, args ...interface{}) { nsyntaxerrors++ // only one syntax error per line - if int32(yyerror_lastsyntax) == lexlineno { + if yyerror_lastsyntax == lineno { return } - yyerror_lastsyntax = int(lexlineno) + yyerror_lastsyntax = lineno - // plain "syntax error" gets "near foo" added - if msg == "syntax error" { - yyerrorl(int(lexlineno), "syntax error near %s", lexbuf.String()) - return - } - - yyerrorl(int(lexlineno), "%s", msg) + yyerrorl(int(lineno), "%s", msg) return } @@ -256,9 +238,8 @@ func (pkg *Pkg) Lookup(name string) *Sym { } s := &Sym{ - Name: name, - Pkg: pkg, - Lexical: LNAME, + Name: name, + Pkg: pkg, } if name == "init" { initSyms = append(initSyms, s) @@ -367,162 +348,6 @@ func saveorignode(n *Node) { n.Orig = norig } -// ispaddedfield reports whether the given field -// is followed by padding. For the case where t is -// the last field, total gives the size of the enclosing struct. -func ispaddedfield(t *Type, total int64) bool { - if t.Etype != TFIELD { - Fatalf("ispaddedfield called non-field %v", t) - } - if t.Down == nil { - return t.Width+t.Type.Width != total - } - return t.Width+t.Type.Width != t.Down.Width -} - -func algtype1(t *Type, bad **Type) int { - if bad != nil { - *bad = nil - } - if t.Broke { - return AMEM - } - if t.Noalg { - return ANOEQ - } - - switch t.Etype { - // will be defined later. - case TANY, TFORW: - *bad = t - - return -1 - - case TINT8, - TUINT8, - TINT16, - TUINT16, - TINT32, - TUINT32, - TINT64, - TUINT64, - TINT, - TUINT, - TUINTPTR, - TBOOL, - TPTR32, - TPTR64, - TCHAN, - TUNSAFEPTR: - return AMEM - - case TFUNC, TMAP: - if bad != nil { - *bad = t - } - return ANOEQ - - case TFLOAT32: - return AFLOAT32 - - case TFLOAT64: - return AFLOAT64 - - case TCOMPLEX64: - return ACPLX64 - - case TCOMPLEX128: - return ACPLX128 - - case TSTRING: - return ASTRING - - case TINTER: - if isnilinter(t) { - return ANILINTER - } - return AINTER - - case TARRAY: - if Isslice(t) { - if bad != nil { - *bad = t - } - return ANOEQ - } - - a := algtype1(t.Type, bad) - if a == ANOEQ || a == AMEM { - if a == ANOEQ && bad != nil { - *bad = t - } - return a - } - - switch t.Bound { - case 0: - // We checked above that the element type is comparable. - return AMEM - case 1: - // Single-element array is same as its lone element. - return a - } - - return -1 // needs special compare - - case TSTRUCT: - if t.Type != nil && t.Type.Down == nil && !isblanksym(t.Type.Sym) { - // One-field struct is same as that one field alone. - return algtype1(t.Type.Type, bad) - } - - ret := AMEM - var a int - for t1 := t.Type; t1 != nil; t1 = t1.Down { - // All fields must be comparable. - a = algtype1(t1.Type, bad) - - if a == ANOEQ { - return ANOEQ - } - - // Blank fields, padded fields, fields with non-memory - // equality need special compare. - if a != AMEM || isblanksym(t1.Sym) || ispaddedfield(t1, t.Width) { - ret = -1 - continue - } - } - - return ret - } - - Fatalf("algtype1: unexpected type %v", t) - return 0 -} - -func algtype(t *Type) int { - a := algtype1(t, nil) - if a == AMEM { - switch t.Width { - case 0: - return AMEM0 - case 1: - return AMEM8 - case 2: - return AMEM16 - case 4: - return AMEM32 - case 8: - return AMEM64 - case 16: - return AMEM128 - } - } - - return a -} - func maptype(key *Type, val *Type) *Type { if key != nil { var bad *Type @@ -1561,8 +1386,8 @@ func frame(context int) { if Curfn != nil { fmt.Printf("--- %v frame ---\n", Curfn.Func.Nname.Sym) - for l := Curfn.Func.Dcl; l != nil; l = l.Next { - printframenode(l.N) + for _, ln := range Curfn.Func.Dcl { + printframenode(ln) } } } @@ -2414,457 +2239,6 @@ func hashmem(t *Type) *Node { return n } -func hashfor(t *Type) *Node { - var sym *Sym - - a := algtype1(t, nil) - switch a { - case AMEM: - Fatalf("hashfor with AMEM type") - - case AINTER: - sym = Pkglookup("interhash", Runtimepkg) - - case ANILINTER: - sym = Pkglookup("nilinterhash", Runtimepkg) - - case ASTRING: - sym = Pkglookup("strhash", Runtimepkg) - - case AFLOAT32: - sym = Pkglookup("f32hash", Runtimepkg) - - case AFLOAT64: - sym = Pkglookup("f64hash", Runtimepkg) - - case ACPLX64: - sym = Pkglookup("c64hash", Runtimepkg) - - case ACPLX128: - sym = Pkglookup("c128hash", Runtimepkg) - - default: - sym = typesymprefix(".hash", t) - } - - n := newname(sym) - n.Class = PFUNC - tfn := Nod(OTFUNC, nil, nil) - tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t)))) - tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR]))) - tfn.Rlist = list(tfn.Rlist, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR]))) - typecheck(&tfn, Etype) - n.Type = tfn.Type - return n -} - -// Generate a helper function to compute the hash of a value of type t. -func genhash(sym *Sym, t *Type) { - if Debug['r'] != 0 { - fmt.Printf("genhash %v %v\n", sym, t) - } - - lineno = 1 // less confusing than end of input - dclcontext = PEXTERN - markdcl() - - // func sym(p *T, h uintptr) uintptr - fn := Nod(ODCLFUNC, nil, nil) - - fn.Func.Nname = newname(sym) - fn.Func.Nname.Class = PFUNC - tfn := Nod(OTFUNC, nil, nil) - fn.Func.Nname.Name.Param.Ntype = tfn - - n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t))) - tfn.List = list(tfn.List, n) - np := n.Left - n = Nod(ODCLFIELD, newname(Lookup("h")), typenod(Types[TUINTPTR])) - tfn.List = list(tfn.List, n) - nh := n.Left - n = Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])) // return value - tfn.Rlist = list(tfn.Rlist, n) - - funchdr(fn) - typecheck(&fn.Func.Nname.Name.Param.Ntype, Etype) - - // genhash is only called for types that have equality but - // cannot be handled by the standard algorithms, - // so t must be either an array or a struct. - switch t.Etype { - default: - Fatalf("genhash %v", t) - - case TARRAY: - if Isslice(t) { - Fatalf("genhash %v", t) - } - - // An array of pure memory would be handled by the - // standard algorithm, so the element type must not be - // pure memory. - hashel := hashfor(t.Type) - - n := Nod(ORANGE, nil, Nod(OIND, np, nil)) - ni := newname(Lookup("i")) - ni.Type = Types[TINT] - n.List = list1(ni) - n.Colas = true - colasdefn(n.List, n) - ni = n.List.N - - // h = hashel(&p[i], h) - call := Nod(OCALL, hashel, nil) - - nx := Nod(OINDEX, np, ni) - nx.Bounded = true - na := Nod(OADDR, nx, nil) - na.Etype = 1 // no escape to heap - call.List = list(call.List, na) - call.List = list(call.List, nh) - n.Nbody = list(n.Nbody, Nod(OAS, nh, call)) - - fn.Nbody = list(fn.Nbody, n) - - // Walk the struct using memhash for runs of AMEM - // and calling specific hash functions for the others. - case TSTRUCT: - var first *Type - - offend := int64(0) - var size int64 - var call *Node - var nx *Node - var na *Node - var hashel *Node - for t1 := t.Type; ; t1 = t1.Down { - if t1 != nil && algtype1(t1.Type, nil) == AMEM && !isblanksym(t1.Sym) { - offend = t1.Width + t1.Type.Width - if first == nil { - first = t1 - } - - // If it's a memory field but it's padded, stop here. - if ispaddedfield(t1, t.Width) { - t1 = t1.Down - } else { - continue - } - } - - // Run memhash for fields up to this one. - if first != nil { - size = offend - first.Width // first->width is offset - hashel = hashmem(first.Type) - - // h = hashel(&p.first, size, h) - call = Nod(OCALL, hashel, nil) - - nx = Nod(OXDOT, np, newname(first.Sym)) // TODO: fields from other packages? - na = Nod(OADDR, nx, nil) - na.Etype = 1 // no escape to heap - call.List = list(call.List, na) - call.List = list(call.List, nh) - call.List = list(call.List, Nodintconst(size)) - fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call)) - - first = nil - } - - if t1 == nil { - break - } - if isblanksym(t1.Sym) { - continue - } - - // Run hash for this field. - if algtype1(t1.Type, nil) == AMEM { - hashel = hashmem(t1.Type) - - // h = memhash(&p.t1, h, size) - call = Nod(OCALL, hashel, nil) - - nx = Nod(OXDOT, np, newname(t1.Sym)) // TODO: fields from other packages? - na = Nod(OADDR, nx, nil) - na.Etype = 1 // no escape to heap - call.List = list(call.List, na) - call.List = list(call.List, nh) - call.List = list(call.List, Nodintconst(t1.Type.Width)) - fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call)) - } else { - hashel = hashfor(t1.Type) - - // h = hashel(&p.t1, h) - call = Nod(OCALL, hashel, nil) - - nx = Nod(OXDOT, np, newname(t1.Sym)) // TODO: fields from other packages? - na = Nod(OADDR, nx, nil) - na.Etype = 1 // no escape to heap - call.List = list(call.List, na) - call.List = list(call.List, nh) - fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call)) - } - } - } - - r := Nod(ORETURN, nil, nil) - r.List = list(r.List, nh) - fn.Nbody = list(fn.Nbody, r) - - if Debug['r'] != 0 { - dumplist("genhash body", fn.Nbody) - } - - funcbody(fn) - Curfn = fn - fn.Func.Dupok = true - typecheck(&fn, Etop) - typechecklist(fn.Nbody, Etop) - Curfn = nil - - // Disable safemode while compiling this code: the code we - // generate internally can refer to unsafe.Pointer. - // In this case it can happen if we need to generate an == - // for a struct containing a reflect.Value, which itself has - // an unexported field of type unsafe.Pointer. - old_safemode := safemode - - safemode = 0 - funccompile(fn) - safemode = old_safemode -} - -// eqfield returns the node -// p.field == q.field -func eqfield(p *Node, q *Node, field *Node) *Node { - nx := Nod(OXDOT, p, field) - ny := Nod(OXDOT, q, field) - ne := Nod(OEQ, nx, ny) - return ne -} - -func eqmemfunc(size int64, type_ *Type, needsize *int) *Node { - var fn *Node - - switch size { - default: - fn = syslook("memequal", 1) - *needsize = 1 - - case 1, 2, 4, 8, 16: - buf := fmt.Sprintf("memequal%d", int(size)*8) - fn = syslook(buf, 1) - *needsize = 0 - } - - substArgTypes(fn, type_, type_) - return fn -} - -// eqmem returns the node -// memequal(&p.field, &q.field [, size]) -func eqmem(p *Node, q *Node, field *Node, size int64) *Node { - var needsize int - - nx := Nod(OADDR, Nod(OXDOT, p, field), nil) - nx.Etype = 1 // does not escape - ny := Nod(OADDR, Nod(OXDOT, q, field), nil) - ny.Etype = 1 // does not escape - typecheck(&nx, Erv) - typecheck(&ny, Erv) - - call := Nod(OCALL, eqmemfunc(size, nx.Type.Type, &needsize), nil) - call.List = list(call.List, nx) - call.List = list(call.List, ny) - if needsize != 0 { - call.List = list(call.List, Nodintconst(size)) - } - - return call -} - -// geneq generates a helper function to -// check equality of two values of type t. -func geneq(sym *Sym, t *Type) { - if Debug['r'] != 0 { - fmt.Printf("geneq %v %v\n", sym, t) - } - - lineno = 1 // less confusing than end of input - dclcontext = PEXTERN - markdcl() - - // func sym(p, q *T) bool - fn := Nod(ODCLFUNC, nil, nil) - - fn.Func.Nname = newname(sym) - fn.Func.Nname.Class = PFUNC - tfn := Nod(OTFUNC, nil, nil) - fn.Func.Nname.Name.Param.Ntype = tfn - - n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t))) - tfn.List = list(tfn.List, n) - np := n.Left - n = Nod(ODCLFIELD, newname(Lookup("q")), typenod(Ptrto(t))) - tfn.List = list(tfn.List, n) - nq := n.Left - n = Nod(ODCLFIELD, nil, typenod(Types[TBOOL])) - tfn.Rlist = list(tfn.Rlist, n) - - funchdr(fn) - - // geneq is only called for types that have equality but - // cannot be handled by the standard algorithms, - // so t must be either an array or a struct. - switch t.Etype { - default: - Fatalf("geneq %v", t) - - case TARRAY: - if Isslice(t) { - Fatalf("geneq %v", t) - } - - // An array of pure memory would be handled by the - // standard memequal, so the element type must not be - // pure memory. Even if we unrolled the range loop, - // each iteration would be a function call, so don't bother - // unrolling. - nrange := Nod(ORANGE, nil, Nod(OIND, np, nil)) - - ni := newname(Lookup("i")) - ni.Type = Types[TINT] - nrange.List = list1(ni) - nrange.Colas = true - colasdefn(nrange.List, nrange) - ni = nrange.List.N - - // if p[i] != q[i] { return false } - nx := Nod(OINDEX, np, ni) - - nx.Bounded = true - ny := Nod(OINDEX, nq, ni) - ny.Bounded = true - - nif := Nod(OIF, nil, nil) - nif.Left = Nod(ONE, nx, ny) - r := Nod(ORETURN, nil, nil) - r.List = list(r.List, Nodbool(false)) - nif.Nbody = list(nif.Nbody, r) - nrange.Nbody = list(nrange.Nbody, nif) - fn.Nbody = list(fn.Nbody, nrange) - - // return true - ret := Nod(ORETURN, nil, nil) - ret.List = list(ret.List, Nodbool(true)) - fn.Nbody = list(fn.Nbody, ret) - - // Walk the struct using memequal for runs of AMEM - // and calling specific equality tests for the others. - // Skip blank-named fields. - case TSTRUCT: - var first *Type - - var conjuncts []*Node - offend := int64(0) - var size int64 - for t1 := t.Type; ; t1 = t1.Down { - if t1 != nil && algtype1(t1.Type, nil) == AMEM && !isblanksym(t1.Sym) { - offend = t1.Width + t1.Type.Width - if first == nil { - first = t1 - } - - // If it's a memory field but it's padded, stop here. - if ispaddedfield(t1, t.Width) { - t1 = t1.Down - } else { - continue - } - } - - // Run memequal for fields up to this one. - // TODO(rsc): All the calls to newname are wrong for - // cross-package unexported fields. - if first != nil { - if first.Down == t1 { - conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym))) - } else if first.Down.Down == t1 { - conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym))) - first = first.Down - if !isblanksym(first.Sym) { - conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym))) - } - } else { - // More than two fields: use memequal. - size = offend - first.Width // first->width is offset - conjuncts = append(conjuncts, eqmem(np, nq, newname(first.Sym), size)) - } - - first = nil - } - - if t1 == nil { - break - } - if isblanksym(t1.Sym) { - continue - } - - // Check this field, which is not just memory. - conjuncts = append(conjuncts, eqfield(np, nq, newname(t1.Sym))) - } - - var and *Node - switch len(conjuncts) { - case 0: - and = Nodbool(true) - case 1: - and = conjuncts[0] - default: - and = Nod(OANDAND, conjuncts[0], conjuncts[1]) - for _, conjunct := range conjuncts[2:] { - and = Nod(OANDAND, and, conjunct) - } - } - - ret := Nod(ORETURN, nil, nil) - ret.List = list(ret.List, and) - fn.Nbody = list(fn.Nbody, ret) - } - - if Debug['r'] != 0 { - dumplist("geneq body", fn.Nbody) - } - - funcbody(fn) - Curfn = fn - fn.Func.Dupok = true - typecheck(&fn, Etop) - typechecklist(fn.Nbody, Etop) - Curfn = nil - - // Disable safemode while compiling this code: the code we - // generate internally can refer to unsafe.Pointer. - // In this case it can happen if we need to generate an == - // for a struct containing a reflect.Value, which itself has - // an unexported field of type unsafe.Pointer. - old_safemode := safemode - safemode = 0 - - // Disable checknils while compiling this code. - // We are comparing a struct or an array, - // neither of which can be nil, and our comparisons - // are shallow. - Disable_checknil++ - - funccompile(fn) - - safemode = old_safemode - Disable_checknil-- -} - func ifacelookdot(s *Sym, t *Type, followptr *bool, ignorecase int) *Type { *followptr = false diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index b97cb3f807..ec91043dbe 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -149,11 +149,11 @@ type Param struct { // Func holds Node fields used only with function-like nodes. type Func struct { Shortname *Node - Enter *NodeList // for example, allocate and initialize memory for escaping parameters - Exit *NodeList - Cvars *NodeList // closure params - Dcl *NodeList // autodcl for this func/closure - Inldcl *NodeList // copy of dcl for use in inlining + Enter Nodes // for example, allocate and initialize memory for escaping parameters + Exit Nodes + Cvars Nodes // closure params + Dcl []*Node // autodcl for this func/closure + Inldcl *[]*Node // copy of dcl for use in inlining Closgen int Outerfunc *Node Fieldtrack []*Type @@ -169,18 +169,12 @@ type Func struct { Depth int32 Endlineno int32 + WBLineno int32 // line number of first write barrier - Norace bool // func must not have race detector annotations - Nosplit bool // func should not execute on separate stack - Noinline bool // func should not be inlined - Nowritebarrier bool // emit compiler error instead of write barrier - Nowritebarrierrec bool // error on write barrier in this or recursive callees - Dupok bool // duplicate definitions ok - Wrapper bool // is method wrapper - Needctxt bool // function uses context register (has closure variables) - Systemstack bool // must run on system stack - - WBLineno int32 // line number of first write barrier + Pragma Pragma // go:xxx function annotations + Dupok bool // duplicate definitions ok + Wrapper bool // is method wrapper + Needctxt bool // function uses context register (has closure variables) } type Op uint8 @@ -491,3 +485,55 @@ func count(l *NodeList) int { } return int(n) } + +// Nodes is a pointer to a slice of *Node. +// For fields that are not used in most nodes, this is used instead of +// a slice to save space. +type Nodes struct{ slice *[]*Node } + +// Slice returns the entries in Nodes as a slice. +// Changes to the slice entries (as in s[i] = n) will be reflected in +// the Nodes. +func (n *Nodes) Slice() []*Node { + if n.slice == nil { + return nil + } + return *n.slice +} + +// NodeList returns the entries in Nodes as a NodeList. +// Changes to the NodeList entries (as in l.N = n) will *not* be +// reflect in the Nodes. +// This wastes memory and should be used as little as possible. +func (n *Nodes) NodeList() *NodeList { + if n.slice == nil { + return nil + } + var ret *NodeList + for _, n := range *n.slice { + ret = list(ret, n) + } + return ret +} + +// Set sets Nodes to a slice. +// This takes ownership of the slice. +func (n *Nodes) Set(s []*Node) { + if len(s) == 0 { + n.slice = nil + } else { + n.slice = &s + } +} + +// Append appends entries to Nodes. +// If a slice is passed in, this will take ownership of it. +func (n *Nodes) Append(a ...*Node) { + if n.slice == nil { + if len(a) > 0 { + n.slice = &a + } + } else { + *n.slice = append(*n.slice, a...) + } +} diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 8fd6f85575..04455515e6 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -40,6 +40,12 @@ func typechecklist(l *NodeList, top int) { } } +func typecheckslice(l []*Node, top int) { + for i := range l { + typecheck(&l[i], top) + } +} + var _typekind = []string{ TINT: "int", TUINT: "uint", @@ -3433,9 +3439,9 @@ func typecheckfunc(n *Node) { addmethod(n.Func.Shortname.Sym, t, true, n.Func.Nname.Nointerface) } - for l := n.Func.Dcl; l != nil; l = l.Next { - if l.N.Op == ONAME && (l.N.Class == PPARAM || l.N.Class == PPARAMOUT) { - l.N.Name.Decldepth = 1 + for _, ln := range n.Func.Dcl { + if ln.Op == ONAME && (ln.Class == PPARAM || ln.Class == PPARAMOUT) { + ln.Name.Decldepth = 1 } } } diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 45b85b9b02..09888f8b9e 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -29,33 +29,34 @@ func walk(fn *Node) { // Final typecheck for any unused variables. // It's hard to be on the heap when not-used, but best to be consistent about &~PHEAP here and below. - for l := fn.Func.Dcl; l != nil; l = l.Next { - if l.N.Op == ONAME && l.N.Class&^PHEAP == PAUTO { - typecheck(&l.N, Erv|Easgn) + for i, ln := range fn.Func.Dcl { + if ln.Op == ONAME && ln.Class&^PHEAP == PAUTO { + typecheck(&ln, Erv|Easgn) + fn.Func.Dcl[i] = ln } } // Propagate the used flag for typeswitch variables up to the NONAME in it's definition. - for l := fn.Func.Dcl; l != nil; l = l.Next { - if l.N.Op == ONAME && l.N.Class&^PHEAP == PAUTO && l.N.Name.Defn != nil && l.N.Name.Defn.Op == OTYPESW && l.N.Used { - l.N.Name.Defn.Left.Used = true + for _, ln := range fn.Func.Dcl { + if ln.Op == ONAME && ln.Class&^PHEAP == PAUTO && ln.Name.Defn != nil && ln.Name.Defn.Op == OTYPESW && ln.Used { + ln.Name.Defn.Left.Used = true } } - for l := fn.Func.Dcl; l != nil; l = l.Next { - if l.N.Op != ONAME || l.N.Class&^PHEAP != PAUTO || l.N.Sym.Name[0] == '&' || l.N.Used { + for _, ln := range fn.Func.Dcl { + if ln.Op != ONAME || ln.Class&^PHEAP != PAUTO || ln.Sym.Name[0] == '&' || ln.Used { continue } - if defn := l.N.Name.Defn; defn != nil && defn.Op == OTYPESW { + if defn := ln.Name.Defn; defn != nil && defn.Op == OTYPESW { if defn.Left.Used { continue } lineno = defn.Left.Lineno - Yyerror("%v declared and not used", l.N.Sym) + Yyerror("%v declared and not used", ln.Sym) defn.Left.Used = true // suppress repeats } else { - lineno = l.N.Lineno - Yyerror("%v declared and not used", l.N.Sym) + lineno = ln.Lineno + Yyerror("%v declared and not used", ln.Sym) } } @@ -70,9 +71,9 @@ func walk(fn *Node) { } heapmoves() - if Debug['W'] != 0 && Curfn.Func.Enter != nil { + if Debug['W'] != 0 && len(Curfn.Func.Enter.Slice()) > 0 { s := fmt.Sprintf("enter %v", Curfn.Func.Nname.Sym) - dumplist(s, Curfn.Func.Enter) + dumpslice(s, Curfn.Func.Enter.Slice()) } } @@ -82,6 +83,12 @@ func walkstmtlist(l *NodeList) { } } +func walkstmtslice(l []*Node) { + for i := range l { + walkstmt(&l[i]) + } +} + func samelist(a *NodeList, b *NodeList) bool { for ; a != nil && b != nil; a, b = a.Next, b.Next { if a.N != b.N { @@ -92,11 +99,11 @@ func samelist(a *NodeList, b *NodeList) bool { } func paramoutheap(fn *Node) bool { - for l := fn.Func.Dcl; l != nil; l = l.Next { - switch l.N.Class { + for _, ln := range fn.Func.Dcl { + switch ln.Class { case PPARAMOUT, PPARAMOUT | PHEAP: - return l.N.Addrtaken + return ln.Addrtaken // stop early - parameters are over case PAUTO, @@ -290,13 +297,13 @@ func walkstmt(np **Node) { var rl *NodeList var cl Class - for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { - cl = ll.N.Class &^ PHEAP + for _, ln := range Curfn.Func.Dcl { + cl = ln.Class &^ PHEAP if cl == PAUTO { break } if cl == PPARAMOUT { - rl = list(rl, ll.N) + rl = list(rl, ln) } } @@ -319,7 +326,7 @@ func walkstmt(np **Node) { ll := ascompatee(n.Op, rl, n.List, &n.Ninit) n.List = reorder3(ll) for lr := n.List; lr != nil; lr = lr.Next { - lr.N = applywritebarrier(lr.N, &n.Ninit) + lr.N = applywritebarrier(lr.N) } break } @@ -587,9 +594,9 @@ opswitch: // transformclosure already did all preparation work. // Prepend captured variables to argument list. - n.List = concat(n.Left.Func.Enter, n.List) + n.List = concat(n.Left.Func.Enter.NodeList(), n.List) - n.Left.Func.Enter = nil + n.Left.Func.Enter.Set(nil) // Replace OCLOSURE with ONAME/PFUNC. n.Left = n.Left.Func.Closure.Func.Nname @@ -723,7 +730,7 @@ opswitch: r := convas(Nod(OAS, n.Left, n.Right), init) r.Dodata = n.Dodata n = r - n = applywritebarrier(n, init) + n = applywritebarrier(n) } case OAS2: @@ -734,7 +741,7 @@ opswitch: ll := ascompatee(OAS, n.List, n.Rlist, init) ll = reorder3(ll) for lr := ll; lr != nil; lr = lr.Next { - lr.N = applywritebarrier(lr.N, init) + lr.N = applywritebarrier(lr.N) } n = liststmt(ll) @@ -749,7 +756,7 @@ opswitch: ll := ascompatet(n.Op, n.List, &r.Type, 0, init) for lr := ll; lr != nil; lr = lr.Next { - lr.N = applywritebarrier(lr.N, init) + lr.N = applywritebarrier(lr.N) } n = liststmt(concat(list1(r), ll)) @@ -2132,7 +2139,7 @@ func needwritebarrier(l *Node, r *Node) bool { // TODO(rsc): Perhaps componentgen should run before this. -func applywritebarrier(n *Node, init **NodeList) *Node { +func applywritebarrier(n *Node) *Node { if n.Left != nil && n.Right != nil && needwritebarrier(n.Left, n.Right) { if Debug_wb > 1 { Warnl(int(n.Lineno), "marking %v for barrier", Nconv(n.Left, 0)) @@ -2541,12 +2548,12 @@ func vmatch1(l *Node, r *Node) bool { // walk through argin parameters. // generate and return code to allocate // copies of escaped parameters to the heap. -func paramstoheap(argin **Type, out int) *NodeList { +func paramstoheap(argin **Type, out int) []*Node { var savet Iter var v *Node var as *Node - var nn *NodeList + var nn []*Node for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) { v = t.Nname if v != nil && v.Sym != nil && v.Sym.Name[0] == '~' && v.Sym.Name[1] == 'r' { // unnamed result @@ -2559,7 +2566,7 @@ func paramstoheap(argin **Type, out int) *NodeList { // Defer might stop a panic and show the // return values as they exist at the time of panic. // Make sure to zero them on entry to the function. - nn = list(nn, Nod(OAS, nodarg(t, -1), nil)) + nn = append(nn, Nod(OAS, nodarg(t, -1), nil)) } if v == nil || v.Class&PHEAP == 0 { @@ -2573,13 +2580,13 @@ func paramstoheap(argin **Type, out int) *NodeList { if prealloc[v] == nil { prealloc[v] = callnew(v.Type) } - nn = list(nn, Nod(OAS, v.Name.Heapaddr, prealloc[v])) + nn = append(nn, Nod(OAS, v.Name.Heapaddr, prealloc[v])) if v.Class&^PHEAP != PPARAMOUT { as = Nod(OAS, v, v.Name.Param.Stackparam) v.Name.Param.Stackparam.Typecheck = 1 typecheck(&as, Etop) - as = applywritebarrier(as, &nn) - nn = list(nn, as) + as = applywritebarrier(as) + nn = append(nn, as) } } @@ -2587,17 +2594,17 @@ func paramstoheap(argin **Type, out int) *NodeList { } // walk through argout parameters copying back to stack -func returnsfromheap(argin **Type) *NodeList { +func returnsfromheap(argin **Type) []*Node { var savet Iter var v *Node - var nn *NodeList + var nn []*Node for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) { v = t.Nname if v == nil || v.Class != PHEAP|PPARAMOUT { continue } - nn = list(nn, Nod(OAS, v.Name.Param.Stackparam, v)) + nn = append(nn, Nod(OAS, v.Name.Param.Stackparam, v)) } return nn @@ -2610,11 +2617,11 @@ func heapmoves() { lno := lineno lineno = Curfn.Lineno nn := paramstoheap(getthis(Curfn.Type), 0) - nn = concat(nn, paramstoheap(getinarg(Curfn.Type), 0)) - nn = concat(nn, paramstoheap(Getoutarg(Curfn.Type), 1)) - Curfn.Func.Enter = concat(Curfn.Func.Enter, nn) + nn = append(nn, paramstoheap(getinarg(Curfn.Type), 0)...) + nn = append(nn, paramstoheap(Getoutarg(Curfn.Type), 1)...) + Curfn.Func.Enter.Append(nn...) lineno = Curfn.Func.Endlineno - Curfn.Func.Exit = returnsfromheap(Getoutarg(Curfn.Type)) + Curfn.Func.Exit.Append(returnsfromheap(Getoutarg(Curfn.Type))...) lineno = lno } diff --git a/src/cmd/compile/internal/mips64/ggen.go b/src/cmd/compile/internal/mips64/ggen.go index 8c285a2952..429eb351a2 100644 --- a/src/cmd/compile/internal/mips64/ggen.go +++ b/src/cmd/compile/internal/mips64/ggen.go @@ -12,8 +12,6 @@ import ( ) func defframe(ptxt *obj.Prog) { - var n *gc.Node - // fill in argument size, stack size ptxt.To.Type = obj.TYPE_TEXTSIZE @@ -30,8 +28,7 @@ func defframe(ptxt *obj.Prog) { lo := hi // iterate through declarations - they are sorted in decreasing xoffset order. - for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next { - n = l.N + for _, n := range gc.Curfn.Func.Dcl { if !n.Name.Needzero { continue } diff --git a/src/cmd/compile/internal/ppc64/ggen.go b/src/cmd/compile/internal/ppc64/ggen.go index 28fcecf8f4..5e50f9e0e8 100644 --- a/src/cmd/compile/internal/ppc64/ggen.go +++ b/src/cmd/compile/internal/ppc64/ggen.go @@ -12,8 +12,6 @@ import ( ) func defframe(ptxt *obj.Prog) { - var n *gc.Node - // fill in argument size, stack size ptxt.To.Type = obj.TYPE_TEXTSIZE @@ -30,8 +28,7 @@ func defframe(ptxt *obj.Prog) { lo := hi // iterate through declarations - they are sorted in decreasing xoffset order. - for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next { - n = l.N + for _, n := range gc.Curfn.Func.Dcl { if !n.Name.Needzero { continue } @@ -133,7 +130,7 @@ func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) { // The hardware will generate undefined result. // Also need to explicitly trap on division on zero, // the hardware will silently generate undefined result. - // DIVW will leave unpredicable result in higher 32-bit, + // DIVW will leave unpredictable result in higher 32-bit, // so always use DIVD/DIVDU. t := nl.Type diff --git a/src/cmd/compile/internal/x86/ggen.go b/src/cmd/compile/internal/x86/ggen.go index 139b199b57..480ae1c585 100644 --- a/src/cmd/compile/internal/x86/ggen.go +++ b/src/cmd/compile/internal/x86/ggen.go @@ -11,8 +11,6 @@ import ( ) func defframe(ptxt *obj.Prog) { - var n *gc.Node - // fill in argument size, stack size ptxt.To.Type = obj.TYPE_TEXTSIZE @@ -28,8 +26,7 @@ func defframe(ptxt *obj.Prog) { hi := int64(0) lo := hi ax := uint32(0) - for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next { - n = l.N + for _, n := range gc.Curfn.Func.Dcl { if !n.Name.Needzero { continue } diff --git a/src/cmd/compile/internal/x86/peep.go b/src/cmd/compile/internal/x86/peep.go index 63e64cb77c..239e9cc35f 100644 --- a/src/cmd/compile/internal/x86/peep.go +++ b/src/cmd/compile/internal/x86/peep.go @@ -284,7 +284,7 @@ func elimshortmov(g *gc.Graph) { } if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST { - // move or artihmetic into partial register. + // move or arithmetic into partial register. // from another register or constant can be movl. // we don't switch to 32-bit arithmetic if it can // change how the carry bit is set (and the carry bit is needed). diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 7f2f75341f..0b2ce271dc 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -6,6 +6,7 @@ package main import ( "bytes" + "encoding/json" "flag" "fmt" "os" @@ -463,6 +464,9 @@ var deptab = []struct { {"runtime/internal/sys", []string{ "zversion.go", }}, + {"go/build", []string{ + "zcgo.go", + }}, } // depsuffix records the allowed suffixes for source files. @@ -478,6 +482,7 @@ var gentab = []struct { }{ {"zdefaultcc.go", mkzdefaultcc}, {"zversion.go", mkzversion}, + {"zcgo.go", mkzcgo}, // not generated anymore, but delete the file if we see it {"enam.c", nil}, @@ -933,6 +938,7 @@ func usage() { "clean deletes all built files\n" + "env [-p] print environment (-p: include $PATH)\n" + "install [dir] install individual directory\n" + + "list [-json] list all supported platforms\n" + "test [-h] run Go test(s)\n" + "version print Go version\n" + "\n" + @@ -1061,9 +1067,13 @@ func cmdbootstrap() { } } -// Copied from go/build/build.go. // Cannot use go/build directly because cmd/dist for a new release // builds against an old release's go/build, which may be out of sync. +// To reduce duplication, we generate the list for go/build from this. +// +// We list all supported platforms in this list, so that this is the +// single point of truth for supported platforms. This list is used +// by 'go tool dist list'. var cgoEnabled = map[string]bool{ "darwin/386": true, "darwin/amd64": true, @@ -1072,19 +1082,31 @@ var cgoEnabled = map[string]bool{ "dragonfly/amd64": true, "freebsd/386": true, "freebsd/amd64": true, + "freebsd/arm": false, "linux/386": true, "linux/amd64": true, "linux/arm": true, "linux/arm64": true, + "linux/ppc64": false, "linux/ppc64le": true, + "linux/mips64": false, + "linux/mips64le": false, "android/386": true, "android/amd64": true, "android/arm": true, + "android/arm64": true, + "nacl/386": false, + "nacl/amd64p32": false, + "nacl/arm": false, "netbsd/386": true, "netbsd/amd64": true, "netbsd/arm": true, "openbsd/386": true, "openbsd/amd64": true, + "openbsd/arm": false, + "plan9/386": false, + "plan9/amd64": false, + "plan9/arm": false, "solaris/amd64": true, "windows/386": true, "windows/amd64": true, @@ -1128,7 +1150,7 @@ func defaulttarg() string { fatal("current directory %s is not under %s", pwd, real_src) } pwd = pwd[len(real_src):] - // guard againt xrealwd return the directory without the trailing / + // guard against xrealwd returning the directory without the trailing / pwd = strings.TrimPrefix(pwd, "/") return pwd @@ -1195,3 +1217,43 @@ func cmdversion() { xflagparse(0) xprintf("%s\n", findgoversion()) } + +// cmdlist lists all supported platforms. +func cmdlist() { + jsonFlag := flag.Bool("json", false, "produce JSON output") + xflagparse(0) + + var plats []string + for p := range cgoEnabled { + plats = append(plats, p) + } + sort.Strings(plats) + + if !*jsonFlag { + for _, p := range plats { + xprintf("%s\n", p) + } + return + } + + type jsonResult struct { + GOOS string + GOARCH string + CgoSupported bool + } + var results []jsonResult + for _, p := range plats { + fields := strings.Split(p, "/") + results = append(results, jsonResult{ + GOOS: fields[0], + GOARCH: fields[1], + CgoSupported: cgoEnabled[p]}) + } + out, err := json.MarshalIndent(results, "", "\t") + if err != nil { + fatal("json marshal error: %v", err) + } + if _, err := os.Stdout.Write(out); err != nil { + fatal("write failed: %v", err) + } +} diff --git a/src/cmd/dist/buildgo.go b/src/cmd/dist/buildgo.go index 437e9dd9a0..c0bdfad9b1 100644 --- a/src/cmd/dist/buildgo.go +++ b/src/cmd/dist/buildgo.go @@ -4,7 +4,10 @@ package main -import "fmt" +import ( + "bytes" + "fmt" +) /* * Helpers for building cmd/go and cmd/cgo. @@ -37,3 +40,28 @@ func mkzdefaultcc(dir, file string) { file = file[:i] + "c" + file[i:] writefile(out, file, writeSkipSame) } + +// mkzcgo writes zcgo.go for go/build package: +// +// package build +// var cgoEnabled = map[string]bool{} +// +// It is invoked to write go/build/zcgo.go. +func mkzcgo(dir, file string) { + var buf bytes.Buffer + + fmt.Fprintf(&buf, + "// auto generated by go tool dist\n"+ + "\n"+ + "package build\n"+ + "\n"+ + "var cgoEnabled = map[string]bool{\n") + for plat, hasCgo := range cgoEnabled { + if hasCgo { + fmt.Fprintf(&buf, "\t%q: true,\n", plat) + } + } + fmt.Fprintf(&buf, "}") + + writefile(buf.String(), file, writeSkipSame) +} diff --git a/src/cmd/dist/main.go b/src/cmd/dist/main.go index 1f19a7ca18..eaee28ada8 100644 --- a/src/cmd/dist/main.go +++ b/src/cmd/dist/main.go @@ -21,6 +21,7 @@ var cmdtab = []struct { {"clean", cmdclean}, {"env", cmdenv}, {"install", cmdinstall}, + {"list", cmdlist}, {"test", cmdtest}, {"version", cmdversion}, } diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 36c829d1b9..e268e1207e 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -441,6 +441,20 @@ func (t *tester) registerTests() { return nil }, }) + fortran := os.Getenv("FC") + if fortran == "" { + fortran, _ = exec.LookPath("gfortran") + } + if fortran != "" { + t.tests = append(t.tests, distTest{ + name: "cgo_fortran", + heading: "../misc/cgo/fortran", + fn: func(dt *distTest) error { + t.addCmd(dt, "misc/cgo/fortran", "./test.bash", fortran) + return nil + }, + }) + } } if t.cgoEnabled && t.goos != "android" && !t.iOS() { // TODO(crawshaw): reenable on android and iOS diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index e65aee4a27..89ab1c0dd4 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -1348,6 +1348,11 @@ func (b *builder) build(a *action) (err error) { return fmt.Errorf("can't build package %s because it contains Objective-C files (%s) but it's not using cgo nor SWIG", a.p.ImportPath, strings.Join(a.p.MFiles, ",")) } + // Same as above for Fortran files + if len(a.p.FFiles) > 0 && !a.p.usesCgo() && !a.p.usesSwig() { + return fmt.Errorf("can't build package %s because it contains Fortran files (%s) but it's not using cgo nor SWIG", + a.p.ImportPath, strings.Join(a.p.FFiles, ",")) + } defer func() { if err != nil && err != errPrintedOutput { err = fmt.Errorf("go build %s: %v", a.p.ImportPath, err) @@ -1437,7 +1442,7 @@ func (b *builder) build(a *action) (err error) { if a.cgo != nil && a.cgo.target != "" { cgoExe = a.cgo.target } - outGo, outObj, err := b.cgo(a.p, cgoExe, obj, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, cxxfiles, a.p.MFiles) + outGo, outObj, err := b.cgo(a.p, cgoExe, obj, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, cxxfiles, a.p.MFiles, a.p.FFiles) if err != nil { return err } @@ -2272,7 +2277,7 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool, // so that it can give good error messages about forward declarations. // Exceptions: a few standard packages have forward declarations for // pieces supplied behind-the-scenes by package runtime. - extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) + extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.FFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) if p.Standard { switch p.ImportPath { case "bytes", "net", "os", "runtime/pprof", "sync", "time": @@ -2623,6 +2628,7 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions usesCgo := false cxx := len(root.p.CXXFiles) > 0 || len(root.p.SwigCXXFiles) > 0 objc := len(root.p.MFiles) > 0 + fortran := len(root.p.FFiles) > 0 actionsSeen := make(map[*action]bool) // Make a pre-order depth-first traversal of the action graph, taking note of @@ -2697,6 +2703,9 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions if len(a.p.MFiles) > 0 { objc = true } + if len(a.p.FFiles) > 0 { + fortran = true + } } ldflags = append(ldflags, "-Wl,--whole-archive") @@ -2768,6 +2777,17 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions if objc { ldflags = append(ldflags, "-lobjc") } + if fortran { + fc := os.Getenv("FC") + if fc == "" { + fc = "gfortran" + } + // support gfortran out of the box and let others pass the correct link options + // via CGO_LDFLAGS + if strings.Contains(fc, "gfortran") { + ldflags = append(ldflags, "-lgfortran") + } + } } if err := b.run(".", root.p.ImportPath, nil, tools.linker(), "-o", out, ofiles, ldflags, buildGccgoflags); err != nil { @@ -2862,6 +2882,11 @@ func (b *builder) gxx(p *Package, out string, flags []string, cxxfile string) er return b.ccompile(p, out, flags, cxxfile, b.gxxCmd(p.Dir)) } +// gfortran runs the gfortran Fortran compiler to create an object from a single Fortran file. +func (b *builder) gfortran(p *Package, out string, flags []string, ffile string) error { + return b.ccompile(p, out, flags, ffile, b.gfortranCmd(p.Dir)) +} + // ccompile runs the given C or C++ compiler and creates an object from a single source file. func (b *builder) ccompile(p *Package, out string, flags []string, file string, compiler []string) error { file = mkAbs(p.Dir, file) @@ -2891,6 +2916,11 @@ func (b *builder) gxxCmd(objdir string) []string { return b.ccompilerCmd("CXX", defaultCXX, objdir) } +// gfortranCmd returns a gfortran command line prefix. +func (b *builder) gfortranCmd(objdir string) []string { + return b.ccompilerCmd("FC", "gfortran", objdir) +} + // ccompilerCmd returns a command line prefix for the given environment // variable and using the default command when the variable is empty. func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string { @@ -3009,8 +3039,8 @@ func envList(key, def string) []string { return strings.Fields(v) } -// Return the flags to use when invoking the C or C++ compilers, or cgo. -func (b *builder) cflags(p *Package, def bool) (cppflags, cflags, cxxflags, ldflags []string) { +// Return the flags to use when invoking the C, C++ or Fortran compilers, or cgo. +func (b *builder) cflags(p *Package, def bool) (cppflags, cflags, cxxflags, fflags, ldflags []string) { var defaults string if def { defaults = "-g -O2" @@ -3019,15 +3049,16 @@ func (b *builder) cflags(p *Package, def bool) (cppflags, cflags, cxxflags, ldfl cppflags = stringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS) cflags = stringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS) cxxflags = stringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS) + fflags = stringList(envList("CGO_FFLAGS", defaults), p.CgoFFLAGS) ldflags = stringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS) return } var cgoRe = regexp.MustCompile(`[/\\:]`) -func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) { - cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoLDFLAGS := b.cflags(p, true) - _, cgoexeCFLAGS, _, _ := b.cflags(p, false) +func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, gxxfiles, mfiles, ffiles []string) (outGo, outObj []string, err error) { + cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoFFLAGS, cgoLDFLAGS := b.cflags(p, true) + _, cgoexeCFLAGS, _, _, _ := b.cflags(p, false) cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...) cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...) // If we are compiling Objective-C code, then we need to link against libobjc @@ -3035,6 +3066,19 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi cgoLDFLAGS = append(cgoLDFLAGS, "-lobjc") } + // Likewise for Fortran, except there are many Fortran compilers. + // Support gfortran out of the box and let others pass the correct link options + // via CGO_LDFLAGS + if len(ffiles) > 0 { + fc := os.Getenv("FC") + if fc == "" { + fc = "gfortran" + } + if strings.Contains(fc, "gfortran") { + cgoLDFLAGS = append(cgoLDFLAGS, "-lgfortran") + } + } + if buildMSan && p.ImportPath != "runtime/cgo" { cgoCFLAGS = append([]string{"-fsanitize=memory"}, cgoCFLAGS...) cgoLDFLAGS = append([]string{"-fsanitize=memory"}, cgoLDFLAGS...) @@ -3202,6 +3246,17 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi outObj = append(outObj, ofile) } + fflags := stringList(cgoCPPFLAGS, cgoFFLAGS) + for _, file := range ffiles { + // Append .o to the file, just in case the pkg has file.c and file.f + ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o" + if err := b.gfortran(p, ofile, fflags, file); err != nil { + return nil, nil, err + } + linkobj = append(linkobj, ofile) + outObj = append(outObj, ofile) + } + linkobj = append(linkobj, p.SysoFiles...) dynobj := obj + "_cgo_.o" pie := (goarch == "arm" && goos == "linux") || goos == "android" @@ -3395,7 +3450,7 @@ func (b *builder) swigIntSize(obj string) (intsize string, err error) { // Run SWIG on one SWIG input file. func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx bool, intgosize string) (outGo, outC string, err error) { - cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true) + cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _, _ := b.cflags(p, true) var cflags []string if cxx { cflags = stringList(cgoCPPFLAGS, pcCFLAGS, cgoCXXFLAGS) diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go index a298049a9d..9d4b94acf1 100644 --- a/src/cmd/go/get.go +++ b/src/cmd/go/get.go @@ -119,6 +119,14 @@ func runGet(cmd *Command, args []string) { delete(packageCache, name) } + // In order to rebuild packages information completely, + // we need to clear commands cache. Command packages are + // referring to evicted packages from the package cache. + // This leads to duplicated loads of the standard packages. + for name := range cmdCache { + delete(cmdCache, name) + } + args = importPaths(args) packagesForBuild(args) diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index 51931769d5..928224cee6 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -2122,7 +2122,7 @@ func TestIssue7108(t *testing.T) { // cmd/go: go test -a foo does not rebuild regexp. func TestIssue6844(t *testing.T) { if testing.Short() { - t.Skip("don't rebuild the standard libary in short mode") + t.Skip("don't rebuild the standard library in short mode") } tg := testgo(t) @@ -2763,6 +2763,10 @@ func TestCgoConsistentResults(t *testing.T) { if !canCgo { t.Skip("skipping because cgo not enabled") } + if runtime.GOOS == "solaris" { + // See https://golang.org/issue/13247 + t.Skip("skipping because Solaris builds are known to be inconsistent; see #13247") + } tg := testgo(t) defer tg.cleanup() @@ -2785,3 +2789,15 @@ func TestCgoConsistentResults(t *testing.T) { t.Error("building cgotest twice did not produce the same output") } } + +// Issue 14444: go get -u .../ duplicate loads errors +func TestGoGetUpdateAllDoesNotTryToLoadDuplicates(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + + tg := testgo(t) + defer tg.cleanup() + tg.makeTempdir() + tg.setenv("GOPATH", tg.path(".")) + tg.run("get", "-u", ".../") + tg.grepStderrNot("duplicate loads of", "did not remove old packages from cache") +} diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go index 8f741a636b..d2f1265985 100644 --- a/src/cmd/go/list.go +++ b/src/cmd/go/list.go @@ -51,6 +51,7 @@ syntax of package template. The default output is equivalent to -f CXXFiles []string // .cc, .cxx and .cpp source files MFiles []string // .m source files HFiles []string // .h, .hh, .hpp and .hxx source files + FFiles []string // .f, .F, .for and .f90 Fortran source files SFiles []string // .s source files SwigFiles []string // .swig files SwigCXXFiles []string // .swigcxx files @@ -60,6 +61,7 @@ syntax of package template. The default output is equivalent to -f CgoCFLAGS []string // cgo: flags for C compiler CgoCPPFLAGS []string // cgo: flags for C preprocessor CgoCXXFLAGS []string // cgo: flags for C++ compiler + CgoFFLAGS []string // cgo: flags for Fortran compiler CgoLDFLAGS []string // cgo: flags for linker CgoPkgConfig []string // cgo: pkg-config names diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index 6b5ead2b8c..ccff783c29 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -50,6 +50,7 @@ type Package struct { CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files MFiles []string `json:",omitempty"` // .m source files HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx source files + FFiles []string `json:",omitempty"` // .f, .F, .for and .f90 Fortran source files SFiles []string `json:",omitempty"` // .s source files SwigFiles []string `json:",omitempty"` // .swig files SwigCXXFiles []string `json:",omitempty"` // .swigcxx files @@ -59,6 +60,7 @@ type Package struct { CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler CgoCPPFLAGS []string `json:",omitempty"` // cgo: flags for C preprocessor CgoCXXFLAGS []string `json:",omitempty"` // cgo: flags for C++ compiler + CgoFFLAGS []string `json:",omitempty"` // cgo: flags for Fortran compiler CgoLDFLAGS []string `json:",omitempty"` // cgo: flags for linker CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names @@ -161,6 +163,7 @@ func (p *Package) copyBuild(pp *build.Package) { p.CXXFiles = pp.CXXFiles p.MFiles = pp.MFiles p.HFiles = pp.HFiles + p.FFiles = pp.FFiles p.SFiles = pp.SFiles p.SwigFiles = pp.SwigFiles p.SwigCXXFiles = pp.SwigCXXFiles @@ -909,6 +912,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package p.CXXFiles, p.MFiles, p.HFiles, + p.FFiles, p.SFiles, p.SysoFiles, p.SwigFiles, @@ -1216,7 +1220,7 @@ var isGoRelease = strings.HasPrefix(runtime.Version(), "go1") // an explicit data comparison. Specifically, we build a list of the // inputs to the build, compute its SHA1 hash, and record that as the // ``build ID'' in the generated object. At the next build, we can -// recompute the buid ID and compare it to the one in the generated +// recompute the build ID and compare it to the one in the generated // object. If they differ, the list of inputs has changed, so the object // is out of date and must be rebuilt. // @@ -1495,7 +1499,7 @@ func isStale(p *Package) bool { // to test for write access, and then skip GOPATH roots we don't have write // access to. But hopefully we can just use the mtimes always. - srcs := stringList(p.GoFiles, p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.SFiles, p.CgoFiles, p.SysoFiles, p.SwigFiles, p.SwigCXXFiles) + srcs := stringList(p.GoFiles, p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.CgoFiles, p.SysoFiles, p.SwigFiles, p.SwigCXXFiles) for _, src := range srcs { if olderThan(filepath.Join(p.Dir, src)) { return true diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index 995ba146f5..1d39a72197 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -1206,11 +1206,11 @@ func (b *builder) notest(a *action) error { return nil } -// isTestMain tells whether fn is a TestMain(m *testing.M) function. -func isTestMain(fn *ast.FuncDecl) bool { - if fn.Name.String() != "TestMain" || - fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || - fn.Type.Params == nil || +// isTestFunc tells whether fn has the type of a testing function. arg +// specifies the parameter type we look for: B, M or T. +func isTestFunc(fn *ast.FuncDecl, arg string) bool { + if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || + fn.Type.Params.List == nil || len(fn.Type.Params.List) != 1 || len(fn.Type.Params.List[0].Names) > 1 { return false @@ -1222,10 +1222,11 @@ func isTestMain(fn *ast.FuncDecl) bool { // We can't easily check that the type is *testing.M // because we don't know how testing has been imported, // but at least check that it's *M or *something.M. - if name, ok := ptr.X.(*ast.Ident); ok && name.Name == "M" { + // Same applies for B and T. + if name, ok := ptr.X.(*ast.Ident); ok && name.Name == arg { return true } - if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == "M" { + if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == arg { return true } return false @@ -1344,16 +1345,24 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error { } name := n.Name.String() switch { - case isTestMain(n): + case name == "TestMain" && isTestFunc(n, "M"): if t.TestMain != nil { return errors.New("multiple definitions of TestMain") } t.TestMain = &testFunc{pkg, name, ""} *doImport, *seen = true, true case isTest(name, "Test"): + err := checkTestFunc(n, "T") + if err != nil { + return err + } t.Tests = append(t.Tests, testFunc{pkg, name, ""}) *doImport, *seen = true, true case isTest(name, "Benchmark"): + err := checkTestFunc(n, "B") + if err != nil { + return err + } t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, ""}) *doImport, *seen = true, true } @@ -1372,6 +1381,15 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error { return nil } +func checkTestFunc(fn *ast.FuncDecl, arg string) error { + if !isTestFunc(fn, arg) { + name := fn.Name.String() + pos := testFileSet.Position(fn.Pos()) + return fmt.Errorf("%s: wrong signature for %s, must be: func %s(%s *testing.%s)", pos, name, name, strings.ToLower(arg), arg) + } + return nil +} + type byOrder []*doc.Example func (x byOrder) Len() int { return len(x) } diff --git a/src/cmd/internal/goobj/read.go b/src/cmd/internal/goobj/read.go index 7fdaf557c8..dff6224d85 100644 --- a/src/cmd/internal/goobj/read.go +++ b/src/cmd/internal/goobj/read.go @@ -530,7 +530,7 @@ func (r *objReader) parseArchive() error { return errCorruptArchive } switch name { - case "__.SYMDEF", "__.GOSYMDEF", "__.PKGDEF": + case "__.PKGDEF": r.skip(size) default: oldLimit := r.limit diff --git a/src/cmd/internal/obj/arm/obj5.go b/src/cmd/internal/obj/arm/obj5.go index 1a51dc3b88..c5af929b14 100644 --- a/src/cmd/internal/obj/arm/obj5.go +++ b/src/cmd/internal/obj/arm/obj5.go @@ -55,7 +55,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) { } } - // Replace TLS register fetches on older ARM procesors. + // Replace TLS register fetches on older ARM processors. switch p.As { // Treat MRC 15, 0, , C13, C0, 3 specially. case AMRC: diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index 2e6df2c2f8..4ae819178d 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -444,6 +444,11 @@ const ( R_PLT1 R_PLT2 R_USEFIELD + // R_USETYPE resolves to an *rtype, but no relocation is created. The + // linker uses this as a signal that the pointed-to type information + // should be linked into the final binary, even if there are no other + // direct references. (This is used for types reachable by reflection.) + R_USETYPE R_POWER_TOC R_GOTPCREL // R_JMPMIPS (only used on mips64) resolves to non-PC-relative target address @@ -572,6 +577,7 @@ type Link struct { Debugpcln int32 Flag_shared int32 Flag_dynlink bool + Flag_optimize bool Bso *Biobuf Pathname string Windows int32 @@ -617,6 +623,10 @@ type Link struct { Data *LSym Etext *LSym Edata *LSym + + // Cache of Progs + allocIdx int + progs [10000]Prog } func (ctxt *Link) Diag(format string, args ...interface{}) { diff --git a/src/cmd/internal/obj/mips/obj0.go b/src/cmd/internal/obj/mips/obj0.go index a3ccad2764..fccff707be 100644 --- a/src/cmd/internal/obj/mips/obj0.go +++ b/src/cmd/internal/obj/mips/obj0.go @@ -1234,7 +1234,7 @@ func markregused(ctxt *obj.Link, s *Sch) { } /* - * test to see if 2 instrictions can be + * test to see if two instructions can be * interchanged without changing semantics */ func depend(ctxt *obj.Link, sa, sb *Sch) bool { diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index bae64f4a29..33330b472e 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -116,6 +116,12 @@ func Writeobjdirect(ctxt *Link, b *Biobuf) { } func Flushplist(ctxt *Link) { + flushplist(ctxt, ctxt.Debugasm == 0) +} +func FlushplistNoFree(ctxt *Link) { + flushplist(ctxt, false) +} +func flushplist(ctxt *Link, freeProgs bool) { var flag int var s *LSym var p *Prog @@ -295,21 +301,34 @@ func Flushplist(ctxt *Link) { for s := text; s != nil; s = s.Next { mkfwd(s) linkpatch(ctxt, s) - ctxt.Arch.Follow(ctxt, s) + if ctxt.Flag_optimize { + ctxt.Arch.Follow(ctxt, s) + } ctxt.Arch.Preprocess(ctxt, s) ctxt.Arch.Assemble(ctxt, s) fieldtrack(ctxt, s) linkpcln(ctxt, s) + if freeProgs { + s.Text = nil + s.Etext = nil + } } // Add to running list in ctxt. - if ctxt.Etext == nil { - ctxt.Text = text - } else { - ctxt.Etext.Next = text + if text != nil { + if ctxt.Text == nil { + ctxt.Text = text + } else { + ctxt.Etext.Next = text + } + ctxt.Etext = etext } - ctxt.Etext = etext ctxt.Plist = nil + ctxt.Plast = nil + ctxt.Curp = nil + if freeProgs { + ctxt.freeProgs() + } } func Writeobjfile(ctxt *Link, b *Biobuf) { diff --git a/src/cmd/internal/obj/pass.go b/src/cmd/internal/obj/pass.go index 14c9b6aaba..f10fc60413 100644 --- a/src/cmd/internal/obj/pass.go +++ b/src/cmd/internal/obj/pass.go @@ -202,12 +202,14 @@ func linkpatch(ctxt *Link, sym *LSym) { p.Pcond = q } - for p := sym.Text; p != nil; p = p.Link { - if p.Pcond != nil { - p.Pcond = brloop(ctxt, p.Pcond) + if ctxt.Flag_optimize { + for p := sym.Text; p != nil; p = p.Link { if p.Pcond != nil { - if p.To.Type == TYPE_BRANCH { - p.To.Offset = p.Pcond.Pc + p.Pcond = brloop(ctxt, p.Pcond) + if p.Pcond != nil { + if p.To.Type == TYPE_BRANCH { + p.To.Offset = p.Pcond.Pc + } } } } diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go index dd5297edc5..d9935b3d51 100644 --- a/src/cmd/internal/obj/sym.go +++ b/src/cmd/internal/obj/sym.go @@ -110,6 +110,7 @@ func Linknew(arch *LinkArch) *Link { ctxt.Goarm = Getgoarm() } + ctxt.Flag_optimize = true return ctxt } diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go index 459361cf48..b6e644372f 100644 --- a/src/cmd/internal/obj/util.go +++ b/src/cmd/internal/obj/util.go @@ -287,6 +287,10 @@ func CConv(s uint8) string { } func (p *Prog) String() string { + if p == nil { + return "" + } + if p.Ctxt == nil { return "" } @@ -325,10 +329,23 @@ func (p *Prog) String() string { } func (ctxt *Link) NewProg() *Prog { - p := new(Prog) // should be the only call to this; all others should use ctxt.NewProg + var p *Prog + if i := ctxt.allocIdx; i < len(ctxt.progs) { + p = &ctxt.progs[i] + ctxt.allocIdx = i + 1 + } else { + p = new(Prog) // should be the only call to this; all others should use ctxt.NewProg + } p.Ctxt = ctxt return p } +func (ctxt *Link) freeProgs() { + s := ctxt.progs[:ctxt.allocIdx] + for i := range s { + s[i] = Prog{} + } + ctxt.allocIdx = 0 +} func (ctxt *Link) Line(n int) string { return ctxt.LineHist.LineString(n) diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go index 1955aa560d..3f8426ae38 100644 --- a/src/cmd/internal/obj/x86/obj6.go +++ b/src/cmd/internal/obj/x86/obj6.go @@ -189,7 +189,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) { } } - // Rewrite 0 to $0 in 3rd argment to CMPPS etc. + // Rewrite 0 to $0 in 3rd argument to CMPPS etc. // That's what the tables expect. switch p.As { case ACMPPD, ACMPPS, ACMPSD, ACMPSS: diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go index 74c2249702..ca55512575 100644 --- a/src/cmd/link/internal/arm/asm.go +++ b/src/cmd/link/internal/arm/asm.go @@ -109,7 +109,7 @@ func gentext() { } // Preserve highest 8 bits of a, and do addition to lower 24-bit -// of a and b; used to adjust ARM branch intruction's target +// of a and b; used to adjust ARM branch instruction's target func braddoff(a int32, b int32) int32 { return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b)) } diff --git a/src/cmd/link/internal/arm/obj.go b/src/cmd/link/internal/arm/obj.go index 10eb723bb9..29a58c6fd2 100644 --- a/src/cmd/link/internal/arm/obj.go +++ b/src/cmd/link/internal/arm/obj.go @@ -156,7 +156,7 @@ func archinit() { } case obj.Hdarwin: /* apple MACH */ - ld.Debug['w'] = 1 // disable DWARF generataion + ld.Debug['w'] = 1 // disable DWARF generation ld.Machoinit() ld.HEADR = ld.INITIAL_MACHO_HEADR if ld.INITTEXT == -1 { diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index 0e5a2d0a63..be8afa5d26 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -162,7 +162,7 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int { rs := r.Xsym // ld64 has a bug handling MACHO_ARM64_RELOC_UNSIGNED with !extern relocation. - // see cmd/internal/ld/data.go for details. The workarond is that don't use !extern + // see cmd/internal/ld/data.go for details. The workaround is that don't use !extern // UNSIGNED relocation at all. if rs.Type == obj.SHOSTOBJ || r.Type == obj.R_CALLARM64 || r.Type == obj.R_ADDRARM64 || r.Type == obj.R_ADDR { if rs.Dynid < 0 { diff --git a/src/cmd/link/internal/ld/dwarf_defs.go b/src/cmd/link/internal/ld/dwarf_defs.go index 61389d9218..c52879cbbe 100644 --- a/src/cmd/link/internal/ld/dwarf_defs.go +++ b/src/cmd/link/internal/ld/dwarf_defs.go @@ -116,7 +116,7 @@ const ( DW_CHILDREN_yes = 0x01 ) -// Not from the spec, but logicaly belongs here +// Not from the spec, but logically belongs here const ( DW_CLS_ADDRESS = 0x01 + iota DW_CLS_BLOCK diff --git a/src/cmd/link/internal/ld/ldelf.go b/src/cmd/link/internal/ld/ldelf.go index bea3f2dcc3..9eeca218d3 100644 --- a/src/cmd/link/internal/ld/ldelf.go +++ b/src/cmd/link/internal/ld/ldelf.go @@ -1066,9 +1066,9 @@ func readelfsym(elfobj *ElfObj, i int, sym *ElfSym, needSym int) (err error) { } if needSym != 0 { - // local names and hidden visiblity global names are unique - // and should only reference by its index, not name, so we - // don't bother to add them into hash table + // local names and hidden global names are unique + // and should only be referenced by their index, not name, so we + // don't bother to add them into the hash table s = linknewsym(Ctxt, sym.name, Ctxt.Version) s.Type |= obj.SHIDDEN diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index bdfa0563c3..461ebf8db1 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -255,10 +255,7 @@ var coutbuf struct { f *os.File } -const ( - symname = "__.GOSYMDEF" - pkgname = "__.PKGDEF" -) +const pkgname = "__.PKGDEF" var ( // Set if we see an object compiled by the host compiler that is not @@ -781,7 +778,7 @@ func objfile(lib *Library) { return } - /* skip over optional __.GOSYMDEF and process __.PKGDEF */ + /* process __.PKGDEF */ off := obj.Boffset(f) var arhdr ArHdr @@ -792,15 +789,6 @@ func objfile(lib *Library) { goto out } - if strings.HasPrefix(arhdr.name, symname) { - off += l - l = nextar(f, off, &arhdr) - if l <= 0 { - Diag("%s: short read on archive file symbol header", lib.File) - goto out - } - } - if !strings.HasPrefix(arhdr.name, pkgname) { Diag("%s: cannot find package header", lib.File) goto out @@ -829,7 +817,7 @@ func objfile(lib *Library) { * the individual symbols that are unused. * * loading every object will also make it possible to - * load foreign objects not referenced by __.GOSYMDEF. + * load foreign objects not referenced by __.PKGDEF. */ for { l = nextar(f, off, &arhdr) diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go index 1c7f3a0d82..010e7da0ee 100644 --- a/src/cmd/link/internal/ld/macho.go +++ b/src/cmd/link/internal/ld/macho.go @@ -569,7 +569,7 @@ func Asmbmacho() { if Linkmode == LinkInternal { // For lldb, must say LC_VERSION_MIN_MACOSX or else // it won't know that this Mach-O binary is from OS X - // (could be iOS or WatchOS intead). + // (could be iOS or WatchOS instead). // Go on iOS uses linkmode=external, and linkmode=external // adds this itself. So we only need this code for linkmode=internal // and we can assume OS X. diff --git a/src/cmd/pprof/internal/report/report.go b/src/cmd/pprof/internal/report/report.go index 0265e2384c..b6694f559f 100644 --- a/src/cmd/pprof/internal/report/report.go +++ b/src/cmd/pprof/internal/report/report.go @@ -1340,7 +1340,7 @@ const ( addressOrder ) -// sort reoders the entries in a report based on the specified +// sort reorders the entries in a report based on the specified // ordering criteria. The result is sorted in decreasing order for // numeric quantities, alphabetically for text, and increasing for // addresses. diff --git a/src/cmd/pprof/internal/report/source.go b/src/cmd/pprof/internal/report/source.go index 73ae1b4ea2..908be21424 100644 --- a/src/cmd/pprof/internal/report/source.go +++ b/src/cmd/pprof/internal/report/source.go @@ -408,7 +408,7 @@ func getMissingFunctionSource(filename string, asm map[int]nodes, start, end int return fnodes, filename } -// adjustSourcePath adjusts the pathe for a source file by trimmming +// adjustSourcePath adjusts the path for a source file by trimming // known prefixes and searching for the file on all parents of the // current working dir. func adjustSourcePath(path string) (*os.File, string, error) { diff --git a/src/cmd/vet/doc.go b/src/cmd/vet/doc.go index 53db6dde93..bb3238fc9e 100644 --- a/src/cmd/vet/doc.go +++ b/src/cmd/vet/doc.go @@ -85,12 +85,12 @@ Flag: -copylocks Locks that are erroneously passed by value. -Documentation examples +Tests, benchmarks and documentation examples -Flag: -example +Flag: -tests -Mistakes involving example tests, including examples with incorrect names or -function signatures, or that document identifiers not in the package. +Mistakes involving tests including functions with incorrect names or signatures +and example tests that document identifiers not in the package. Methods diff --git a/src/cmd/vet/example.go b/src/cmd/vet/example.go deleted file mode 100644 index 797c3ceec8..0000000000 --- a/src/cmd/vet/example.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2015 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 ( - "go/ast" - "go/types" - "strings" - "unicode" - "unicode/utf8" -) - -func init() { - register("example", - "check for common mistaken usages of documentation examples", - checkExample, - funcDecl) -} - -func isExampleSuffix(s string) bool { - r, size := utf8.DecodeRuneInString(s) - return size > 0 && unicode.IsLower(r) -} - -// checkExample walks the documentation example functions checking for common -// mistakes of misnamed functions, failure to map functions to existing -// identifiers, etc. -func checkExample(f *File, node ast.Node) { - if !strings.HasSuffix(f.name, "_test.go") { - return - } - var ( - pkg = f.pkg - pkgName = pkg.typesPkg.Name() - scopes = []*types.Scope{pkg.typesPkg.Scope()} - lookup = func(name string) types.Object { - for _, scope := range scopes { - if o := scope.Lookup(name); o != nil { - return o - } - } - return nil - } - ) - if strings.HasSuffix(pkgName, "_test") { - // Treat 'package foo_test' as an alias for 'package foo'. - var ( - basePkg = strings.TrimSuffix(pkgName, "_test") - pkg = f.pkg - ) - for _, p := range pkg.typesPkg.Imports() { - if p.Name() == basePkg { - scopes = append(scopes, p.Scope()) - break - } - } - } - fn, ok := node.(*ast.FuncDecl) - if !ok { - // Ignore non-functions. - return - } - var ( - fnName = fn.Name.Name - report = func(format string, args ...interface{}) { f.Badf(node.Pos(), format, args...) } - ) - if fn.Recv != nil || !strings.HasPrefix(fnName, "Example") { - // Ignore methods and types not named "Example". - return - } - if params := fn.Type.Params; len(params.List) != 0 { - report("%s should be niladic", fnName) - } - if results := fn.Type.Results; results != nil && len(results.List) != 0 { - report("%s should return nothing", fnName) - } - if fnName == "Example" { - // Nothing more to do. - return - } - if filesRun && !includesNonTest { - // The coherence checks between a test and the package it tests - // will report false positives if no non-test files have - // been provided. - return - } - var ( - exName = strings.TrimPrefix(fnName, "Example") - elems = strings.SplitN(exName, "_", 3) - ident = elems[0] - obj = lookup(ident) - ) - if ident != "" && obj == nil { - // Check ExampleFoo and ExampleBadFoo. - report("%s refers to unknown identifier: %s", fnName, ident) - // Abort since obj is absent and no subsequent checks can be performed. - return - } - if elemCnt := strings.Count(exName, "_"); elemCnt == 0 { - // Nothing more to do. - return - } - mmbr := elems[1] - if ident == "" { - // Check Example_suffix and Example_BadSuffix. - if residual := strings.TrimPrefix(exName, "_"); !isExampleSuffix(residual) { - report("%s has malformed example suffix: %s", fnName, residual) - } - return - } - if !isExampleSuffix(mmbr) { - // Check ExampleFoo_Method and ExampleFoo_BadMethod. - if obj, _, _ := types.LookupFieldOrMethod(obj.Type(), true, obj.Pkg(), mmbr); obj == nil { - report("%s refers to unknown field or method: %s.%s", fnName, ident, mmbr) - } - } - if len(elems) == 3 && !isExampleSuffix(elems[2]) { - // Check ExampleFoo_Method_suffix and ExampleFoo_Method_Badsuffix. - report("%s has malformed example suffix: %s", fnName, elems[2]) - } - return -} diff --git a/src/cmd/vet/testdata/examples_test.go b/src/cmd/vet/testdata/tests_test.go similarity index 59% rename from src/cmd/vet/testdata/examples_test.go rename to src/cmd/vet/testdata/tests_test.go index 9c53672a7d..f5bbc3922a 100644 --- a/src/cmd/vet/testdata/examples_test.go +++ b/src/cmd/vet/testdata/tests_test.go @@ -1,7 +1,9 @@ -// Test of examples. - package testdata +import ( + "testing" +) + // Buf is a ... type Buf []byte @@ -46,3 +48,27 @@ func ExamplePuffer() // ERROR "ExamplePuffer refers to unknown identifier: Puffe func ExamplePuffer_Append() // ERROR "ExamplePuffer_Append refers to unknown identifier: Puffer" func ExamplePuffer_suffix() // ERROR "ExamplePuffer_suffix refers to unknown identifier: Puffer" + +func nonTest() {} // OK because it doesn't start with "Test". + +func (Buf) TesthasReceiver() {} // OK because it has a receiver. + +func TestOKSuffix(*testing.T) {} // OK because first char after "Test" is Uppercase. + +func TestÜnicodeWorks(*testing.T) {} // OK because the first char after "Test" is Uppercase. + +func TestbadSuffix(*testing.T) {} // ERROR "first letter after 'Test' must not be lowercase" + +func TestemptyImportBadSuffix(*T) {} // ERROR "first letter after 'Test' must not be lowercase" + +func Test(*testing.T) {} // OK "Test" on its own is considered a test. + +func Testify() {} // OK because it takes no parameters. + +func TesttooManyParams(*testing.T, string) {} // OK because it takes too many parameters. + +func TesttooManyNames(a, b *testing.T) {} // OK because it takes too many names. + +func TestnoTParam(string) {} // OK because it doesn't take a *testing.T + +func BenchmarkbadSuffix(*testing.B) {} // ERROR "first letter after 'Benchmark' must not be lowercase" diff --git a/src/cmd/vet/tests.go b/src/cmd/vet/tests.go new file mode 100644 index 0000000000..52ad334098 --- /dev/null +++ b/src/cmd/vet/tests.go @@ -0,0 +1,182 @@ +// Copyright 2015 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 ( + "go/ast" + "go/types" + "strings" + "unicode" + "unicode/utf8" +) + +func init() { + register("tests", + "check for common mistaken usages of tests/documentation examples", + checkTestFunctions, + funcDecl) +} + +func isExampleSuffix(s string) bool { + r, size := utf8.DecodeRuneInString(s) + return size > 0 && unicode.IsLower(r) +} + +func isTestSuffix(name string) bool { + if len(name) == 0 { + // "Test" is ok. + return true + } + r, _ := utf8.DecodeRuneInString(name) + return !unicode.IsLower(r) +} + +func isTestParam(typ ast.Expr, wantType string) bool { + ptr, ok := typ.(*ast.StarExpr) + if !ok { + // Not a pointer. + return false + } + // No easy way of making sure it's a *testing.T or *testing.B: + // ensure the name of the type matches. + if name, ok := ptr.X.(*ast.Ident); ok { + return name.Name == wantType + } + if sel, ok := ptr.X.(*ast.SelectorExpr); ok { + return sel.Sel.Name == wantType + } + return false +} + +func lookup(name string, scopes []*types.Scope) types.Object { + for _, scope := range scopes { + if o := scope.Lookup(name); o != nil { + return o + } + } + return nil +} + +func extendedScope(pkg *Package) []*types.Scope { + scopes := []*types.Scope{pkg.typesPkg.Scope()} + + pkgName := pkg.typesPkg.Name() + if strings.HasPrefix(pkgName, "_test") { + basePkg := strings.TrimSuffix(pkgName, "_test") + for _, p := range pkg.typesPkg.Imports() { + if p.Name() == basePkg { + scopes = append(scopes, p.Scope()) + break + } + } + } + return scopes +} + +func checkExample(fn *ast.FuncDecl, pkg *Package, report reporter) { + fnName := fn.Name.Name + if params := fn.Type.Params; len(params.List) != 0 { + report("%s should be niladic", fnName) + } + if results := fn.Type.Results; results != nil && len(results.List) != 0 { + report("%s should return nothing", fnName) + } + + if filesRun && !includesNonTest { + // The coherence checks between a test and the package it tests + // will report false positives if no non-test files have + // been provided. + return + } + + if fnName == "Example" { + // Nothing more to do. + return + } + + var ( + exName = strings.TrimPrefix(fnName, "Example") + elems = strings.SplitN(exName, "_", 3) + ident = elems[0] + obj = lookup(ident, extendedScope(pkg)) + ) + if ident != "" && obj == nil { + // Check ExampleFoo and ExampleBadFoo. + report("%s refers to unknown identifier: %s", fnName, ident) + // Abort since obj is absent and no subsequent checks can be performed. + return + } + if len(elems) < 2 { + // Nothing more to do. + return + } + + if ident == "" { + // Check Example_suffix and Example_BadSuffix. + if residual := strings.TrimPrefix(exName, "_"); !isExampleSuffix(residual) { + report("%s has malformed example suffix: %s", fnName, residual) + } + return + } + + mmbr := elems[1] + if !isExampleSuffix(mmbr) { + // Check ExampleFoo_Method and ExampleFoo_BadMethod. + if obj, _, _ := types.LookupFieldOrMethod(obj.Type(), true, obj.Pkg(), mmbr); obj == nil { + report("%s refers to unknown field or method: %s.%s", fnName, ident, mmbr) + } + } + if len(elems) == 3 && !isExampleSuffix(elems[2]) { + // Check ExampleFoo_Method_suffix and ExampleFoo_Method_Badsuffix. + report("%s has malformed example suffix: %s", fnName, elems[2]) + } +} + +func checkTest(fn *ast.FuncDecl, prefix string, report reporter) { + // Want functions with 0 results and 1 parameter. + if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || + fn.Type.Params == nil || + len(fn.Type.Params.List) != 1 || + len(fn.Type.Params.List[0].Names) > 1 { + return + } + + // The param must look like a *testing.T or *testing.B. + if !isTestParam(fn.Type.Params.List[0].Type, prefix[:1]) { + return + } + + if !isTestSuffix(fn.Name.Name[len(prefix):]) { + report("%s has malformed name: first letter after '%s' must not be lowercase", fn.Name.Name, prefix) + } +} + +type reporter func(format string, args ...interface{}) + +// checkTestFunctions walks Test, Benchmark and Example functions checking +// malformed names, wrong signatures and examples documenting inexistent +// identifiers. +func checkTestFunctions(f *File, node ast.Node) { + if !strings.HasSuffix(f.name, "_test.go") { + return + } + + fn, ok := node.(*ast.FuncDecl) + if !ok || fn.Recv != nil { + // Ignore non-functions or functions with receivers. + return + } + + report := func(format string, args ...interface{}) { f.Badf(node.Pos(), format, args...) } + + switch { + case strings.HasPrefix(fn.Name.Name, "Example"): + checkExample(fn, f.pkg, report) + case strings.HasPrefix(fn.Name.Name, "Test"): + checkTest(fn, "Test", report) + case strings.HasPrefix(fn.Name.Name, "Benchmark"): + checkTest(fn, "Benchmark", report) + } +} diff --git a/src/crypto/ecdsa/ecdsa_test.go b/src/crypto/ecdsa/ecdsa_test.go index 62a3fcc496..5e588b9258 100644 --- a/src/crypto/ecdsa/ecdsa_test.go +++ b/src/crypto/ecdsa/ecdsa_test.go @@ -130,7 +130,7 @@ func testNonceSafety(t *testing.T, c elliptic.Curve, tag string) { } if r0.Cmp(r1) == 0 { - t.Errorf("%s: the nonce used for two diferent messages was the same", tag) + t.Errorf("%s: the nonce used for two different messages was the same", tag) } } diff --git a/src/crypto/md5/md5block_arm.s b/src/crypto/md5/md5block_arm.s index f1f0f67e55..54d02b743a 100644 --- a/src/crypto/md5/md5block_arm.s +++ b/src/crypto/md5/md5block_arm.s @@ -46,7 +46,7 @@ loop: BEQ aligned // aligned detected - skip copy // Copy the unaligned source data into the aligned temporary buffer - // memove(to=4(R13), from=8(R13), n=12(R13)) - Corrupts all registers + // memmove(to=4(R13), from=8(R13), n=12(R13)) - Corrupts all registers MOVW $buf, Rtable // to MOVW $64, Rc0 // n MOVM.IB [Rtable,Rdata,Rc0], (R13) diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go index ee32fa0bd6..5e48fc9cd9 100644 --- a/src/crypto/rand/rand.go +++ b/src/crypto/rand/rand.go @@ -11,8 +11,9 @@ import "io" // Reader is a global, shared instance of a cryptographically // strong pseudo-random generator. // -// On Unix-like systems, Reader reads from /dev/urandom. // On Linux, Reader uses getrandom(2) if available, /dev/urandom otherwise. +// On OpenBSD, Reader uses getentropy(2). +// On other Unix-like systems, Reader reads from /dev/urandom. // On Windows systems, Reader uses the CryptGenRandom API. var Reader io.Reader diff --git a/src/crypto/rand/rand_openbsd.go b/src/crypto/rand/rand_openbsd.go new file mode 100644 index 0000000000..405c091060 --- /dev/null +++ b/src/crypto/rand/rand_openbsd.go @@ -0,0 +1,28 @@ +// 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 rand + +import ( + "internal/syscall/unix" +) + +func init() { + altGetRandom = getRandomOpenBSD +} + +func getRandomOpenBSD(p []byte) (ok bool) { + // getentropy(2) returns a maximum of 256 bytes per call + for i := 0; i < len(p); i += 256 { + end := i + 256 + if len(p) < end { + end = len(p) + } + err := unix.GetEntropy(p[i:end]) + if err != nil { + return false + } + } + return true +} diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go index 3c996acf87..b129922926 100644 --- a/src/crypto/tls/handshake_client.go +++ b/src/crypto/tls/handshake_client.go @@ -16,6 +16,7 @@ import ( "io" "net" "strconv" + "strings" ) type clientHandshakeState struct { @@ -49,20 +50,13 @@ func (c *Conn) clientHandshake() error { return errors.New("tls: NextProtos values too large") } - sni := c.config.ServerName - // IP address literals are not permitted as SNI values. See - // https://tools.ietf.org/html/rfc6066#section-3. - if net.ParseIP(sni) != nil { - sni = "" - } - hello := &clientHelloMsg{ vers: c.config.maxVersion(), compressionMethods: []uint8{compressionNone}, random: make([]byte, 32), ocspStapling: true, scts: true, - serverName: sni, + serverName: hostnameInSNI(c.config.ServerName), supportedCurves: c.config.curvePreferences(), supportedPoints: []uint8{pointFormatUncompressed}, nextProtoNeg: len(c.config.NextProtos) > 0, @@ -665,3 +659,23 @@ func mutualProtocol(protos, preferenceProtos []string) (string, bool) { return protos[0], true } + +// hostnameInSNI converts name into an approriate hostname for SNI. +// Literal IP addresses and absolute FQDNs are not permitted as SNI values. +// See https://tools.ietf.org/html/rfc6066#section-3. +func hostnameInSNI(name string) string { + host := name + if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' { + host = host[1 : len(host)-1] + } + if i := strings.LastIndex(host, "%"); i > 0 { + host = host[:i] + } + if net.ParseIP(host) != nil { + return "" + } + if len(name) > 0 && name[len(name)-1] == '.' { + name = name[:len(name)-1] + } + return name +} diff --git a/src/crypto/tls/handshake_client_test.go b/src/crypto/tls/handshake_client_test.go index f78cc46935..9b6c4328a5 100644 --- a/src/crypto/tls/handshake_client_test.go +++ b/src/crypto/tls/handshake_client_test.go @@ -448,7 +448,7 @@ func TestClientResumption(t *testing.T) { t.Fatalf("%s resumed: %v, expected: %v", test, hs.DidResume, didResume) } if didResume && (hs.PeerCertificates == nil || hs.VerifiedChains == nil) { - t.Fatalf("expected non-nil certificates after resumption. Got peerCertificates: %#v, verifedCertificates: %#v", hs.PeerCertificates, hs.VerifiedChains) + t.Fatalf("expected non-nil certificates after resumption. Got peerCertificates: %#v, verifiedCertificates: %#v", hs.PeerCertificates, hs.VerifiedChains) } } @@ -618,14 +618,35 @@ func TestHandshakClientSCTs(t *testing.T) { runClientTestTLS12(t, test) } -func TestNoIPAddressesInSNI(t *testing.T) { - for _, ipLiteral := range []string{"1.2.3.4", "::1"} { +var hostnameInSNITests = []struct { + in, out string +}{ + // Opaque string + {"", ""}, + {"localhost", "localhost"}, + {"foo, bar, baz and qux", "foo, bar, baz and qux"}, + + // DNS hostname + {"golang.org", "golang.org"}, + {"golang.org.", "golang.org"}, + + // Literal IPv4 address + {"1.2.3.4", ""}, + + // Literal IPv6 address + {"::1", ""}, + {"::1%lo0", ""}, // with zone identifier + {"[::1]", ""}, // as per RFC 5952 we allow the [] style as IPv6 literal + {"[::1%lo0]", ""}, +} + +func TestHostnameInSNI(t *testing.T) { + for _, tt := range hostnameInSNITests { c, s := net.Pipe() - go func() { - client := Client(c, &Config{ServerName: ipLiteral}) - client.Handshake() - }() + go func(host string) { + Client(c, &Config{ServerName: host, InsecureSkipVerify: true}).Handshake() + }(tt.in) var header [5]byte if _, err := io.ReadFull(s, header[:]); err != nil { @@ -637,10 +658,20 @@ func TestNoIPAddressesInSNI(t *testing.T) { if _, err := io.ReadFull(s, record[:]); err != nil { t.Fatal(err) } + + c.Close() s.Close() - if bytes.Index(record, []byte(ipLiteral)) != -1 { - t.Errorf("IP literal %q found in ClientHello: %x", ipLiteral, record) + var m clientHelloMsg + if !m.unmarshal(record) { + t.Errorf("unmarshaling ClientHello for %q failed", tt.in) + continue + } + if tt.in != tt.out && m.serverName == tt.in { + t.Errorf("prohibited %q found in ClientHello: %x", tt.in, record) + } + if m.serverName != tt.out { + t.Errorf("expected %q not found in ClientHello: %x", tt.out, record) } } } diff --git a/src/crypto/x509/example_test.go b/src/crypto/x509/example_test.go index 29e7c21397..97c2ea2442 100644 --- a/src/crypto/x509/example_test.go +++ b/src/crypto/x509/example_test.go @@ -5,8 +5,12 @@ package x509_test import ( + "crypto/dsa" + "crypto/ecdsa" + "crypto/rsa" "crypto/x509" "encoding/pem" + "fmt" ) func ExampleCertificate_Verify() { @@ -89,3 +93,42 @@ yE+vPxsiUkvQHdO2fojCkY8jg70jxM+gu59tPDNbw3Uh/2Ij310FgTHsnGQMyA== panic("failed to verify certificate: " + err.Error()) } } + +func ExampleParsePKIXPublicKey() { + const pubPEM = ` +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlRuRnThUjU8/prwYxbty +WPT9pURI3lbsKMiB6Fn/VHOKE13p4D8xgOCADpdRagdT6n4etr9atzDKUSvpMtR3 +CP5noNc97WiNCggBjVWhs7szEe8ugyqF23XwpHQ6uV1LKH50m92MbOWfCtjU9p/x +qhNpQQ1AZhqNy5Gevap5k8XzRmjSldNAFZMY7Yv3Gi+nyCwGwpVtBUwhuLzgNFK/ +yDtw2WcWmUU7NuC8Q6MWvPebxVtCfVp/iQU6q60yyt6aGOBkhAX0LpKAEhKidixY +nP9PNVBvxgu3XZ4P36gZV6+ummKdBVnc3NqwBLu5+CcdRdusmHPHd5pHf4/38Z3/ +6qU2a/fPvWzceVTEgZ47QjFMTCTmCwNt29cvi7zZeQzjtwQgn4ipN9NibRH/Ax/q +TbIzHfrJ1xa2RteWSdFjwtxi9C20HUkjXSeI4YlzQMH0fPX6KCE7aVePTOnB69I/ +a9/q96DiXZajwlpq3wFctrs1oXqBp5DVrCIj8hU2wNgB7LtQ1mCtsYz//heai0K9 +PhE4X6hiE0YmeAZjR0uHl8M/5aW9xCoJ72+12kKpWAa0SFRWLy6FejNYCYpkupVJ +yecLk/4L1W0l6jQQZnWErXZYe0PNFcmwGXy1Rep83kfBRNKRy5tvocalLlwXLdUk +AIU+2GKjyT3iMuzZxxFxPFMCAwEAAQ== +-----END PUBLIC KEY-----` + + block, _ := pem.Decode([]byte(pubPEM)) + if block == nil { + panic("failed to parse PEM block containing the public key") + } + + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + panic("failed to parse DER encoded public key: " + err.Error()) + } + + switch pub := pub.(type) { + case *rsa.PublicKey: + fmt.Println("pub is of type RSA:", pub) + case *dsa.PublicKey: + fmt.Println("pub is of type DSA:", pub) + case *ecdsa.PublicKey: + fmt.Println("pub is of type ECDSA:", pub) + default: + panic("unknown type of public key") + } +} diff --git a/src/crypto/x509/root_darwin_test.go b/src/crypto/x509/root_darwin_test.go index cc6d23c505..8b6b1516ae 100644 --- a/src/crypto/x509/root_darwin_test.go +++ b/src/crypto/x509/root_darwin_test.go @@ -57,6 +57,6 @@ func TestSystemRoots(t *testing.T) { } if have < want { - t.Errorf("insufficent overlap between cgo and non-cgo roots; want at least %d, have %d", want, have) + t.Errorf("insufficient overlap between cgo and non-cgo roots; want at least %d, have %d", want, have) } } diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go index d9288bb30e..dc793cadea 100644 --- a/src/crypto/x509/x509.go +++ b/src/crypto/x509/x509.go @@ -36,6 +36,12 @@ type pkixPublicKey struct { // ParsePKIXPublicKey parses a DER encoded public key. These values are // typically found in PEM blocks with "BEGIN PUBLIC KEY". +// +// Supported key types include RSA, DSA, and ECDSA. Unknown key +// types result in an error. +// +// On success, pub will be of type *rsa.PublicKey, *dsa.PublicKey, +// or *ecdsa.PublicKey. func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error) { var pki publicKeyInfo if rest, err := asn1.Unmarshal(derBytes, &pki); err != nil { diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go index 8ec70d99b0..68f17fb6b0 100644 --- a/src/database/sql/sql_test.go +++ b/src/database/sql/sql_test.go @@ -1591,7 +1591,7 @@ func TestStmtCloseOrder(t *testing.T) { _, err := db.Query("SELECT|non_existent|name|") if err == nil { - t.Fatal("Quering non-existent table should fail") + t.Fatal("Querying non-existent table should fail") } } diff --git a/src/debug/dwarf/entry.go b/src/debug/dwarf/entry.go index 5ca86679fa..6b140c60c7 100644 --- a/src/debug/dwarf/entry.go +++ b/src/debug/dwarf/entry.go @@ -243,7 +243,7 @@ type Field struct { Class Class } -// A Class is the DWARF 4 class of an attibute value. +// A Class is the DWARF 4 class of an attribute value. // // In general, a given attribute's value may take on one of several // possible classes defined by DWARF, each of which leads to a diff --git a/src/debug/gosym/pclntab_test.go b/src/debug/gosym/pclntab_test.go index 8d4aa547a0..355bb0433e 100644 --- a/src/debug/gosym/pclntab_test.go +++ b/src/debug/gosym/pclntab_test.go @@ -42,7 +42,7 @@ func dotest(t *testing.T) { if err := cmd.Run(); err != nil { t.Fatal(err) } - cmd = exec.Command("go", "tool", "link", "-H", "linux", "-E", "main", + cmd = exec.Command("go", "tool", "link", "-H", "linux", "-o", pclinetestBinary, pclinetestBinary+".o") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr diff --git a/src/encoding/base32/base32_test.go b/src/encoding/base32/base32_test.go index 5a68f06e1c..66a48a3f6f 100644 --- a/src/encoding/base32/base32_test.go +++ b/src/encoding/base32/base32_test.go @@ -171,7 +171,7 @@ func TestDecodeCorrupt(t *testing.T) { _, err := StdEncoding.Decode(dbuf, []byte(tc.input)) if tc.offset == -1 { if err != nil { - t.Error("Decoder wrongly detected coruption in", tc.input) + t.Error("Decoder wrongly detected corruption in", tc.input) } continue } diff --git a/src/encoding/base64/base64_test.go b/src/encoding/base64/base64_test.go index fc6a1ea654..eebf113212 100644 --- a/src/encoding/base64/base64_test.go +++ b/src/encoding/base64/base64_test.go @@ -221,7 +221,7 @@ func TestDecodeCorrupt(t *testing.T) { _, err := StdEncoding.Decode(dbuf, []byte(tc.input)) if tc.offset == -1 { if err != nil { - t.Error("Decoder wrongly detected coruption in", tc.input) + t.Error("Decoder wrongly detected corruption in", tc.input) } continue } diff --git a/src/encoding/csv/reader.go b/src/encoding/csv/reader.go index a6bb780bf2..816ed26754 100644 --- a/src/encoding/csv/reader.go +++ b/src/encoding/csv/reader.go @@ -99,6 +99,8 @@ var ( // non-doubled quote may appear in a quoted field. // // If TrimLeadingSpace is true, leading white space in a field is ignored. +// If the field delimiter is white space, TrimLeadingSpace will trim the +// delimiter. type Reader struct { Comma rune // field delimiter (set to ',' by NewReader) Comment rune // comment character for start of line diff --git a/src/encoding/json/bench_test.go b/src/encoding/json/bench_test.go index ed89d1156e..8a514e58cd 100644 --- a/src/encoding/json/bench_test.go +++ b/src/encoding/json/bench_test.go @@ -158,7 +158,7 @@ func BenchmarkCodeUnmarshal(b *testing.B) { for i := 0; i < b.N; i++ { var r codeResponse if err := Unmarshal(codeJSON, &r); err != nil { - b.Fatal("Unmmarshal:", err) + b.Fatal("Unmarshal:", err) } } b.SetBytes(int64(len(codeJSON))) @@ -173,7 +173,7 @@ func BenchmarkCodeUnmarshalReuse(b *testing.B) { var r codeResponse for i := 0; i < b.N; i++ { if err := Unmarshal(codeJSON, &r); err != nil { - b.Fatal("Unmmarshal:", err) + b.Fatal("Unmarshal:", err) } } } diff --git a/src/expvar/expvar.go b/src/expvar/expvar.go index 24c2d6b29a..1d51bc97f9 100644 --- a/src/expvar/expvar.go +++ b/src/expvar/expvar.go @@ -258,7 +258,8 @@ func Publish(name string, v Var) { sort.Strings(varKeys) } -// Get retrieves a named exported variable. +// Get retrieves a named exported variable. It returns nil if the name has +// not been registered. func Get(name string) Var { mutex.RLock() defer mutex.RUnlock() diff --git a/src/expvar/expvar_test.go b/src/expvar/expvar_test.go index 8bc633e4a9..385fea81ad 100644 --- a/src/expvar/expvar_test.go +++ b/src/expvar/expvar_test.go @@ -26,6 +26,14 @@ func RemoveAll() { varKeys = nil } +func TestNil(t *testing.T) { + RemoveAll() + val := Get("missing") + if val != nil { + t.Errorf("got %v, want nil", val) + } +} + func TestInt(t *testing.T) { RemoveAll() reqs := NewInt("requests") diff --git a/src/fmt/fmt_test.go b/src/fmt/fmt_test.go index 1d9d015f4a..05187af29e 100644 --- a/src/fmt/fmt_test.go +++ b/src/fmt/fmt_test.go @@ -674,6 +674,10 @@ var fmtTests = []struct { // Make sure we can handle very large widths. {"%0100f", -1.0, zeroFill("-", 99, "1.000000")}, + // Use spaces instead of zero if padding to the right. + {"%0-5s", "abc", "abc "}, + {"%-05.1f", 1.0, "1.0 "}, + // Complex fmt used to leave the plus flag set for future entries in the array // causing +2+0i and +3+0i instead of 2+0i and 3+0i. {"%v", []complex64{1, 2, 3}, "[(1+0i) (2+0i) (3+0i)]"}, @@ -884,6 +888,14 @@ func TestReorder(t *testing.T) { } } +func BenchmarkSprintfPadding(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + Sprintf("%16f", 1.0) + } + }) +} + func BenchmarkSprintfEmpty(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { @@ -931,6 +943,13 @@ func BenchmarkSprintfFloat(b *testing.B) { } }) } +func BenchmarkSprintfBoolean(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + Sprintf("%t", true) + } + }) +} func BenchmarkManyArgs(b *testing.B) { b.RunParallel(func(pb *testing.PB) { diff --git a/src/fmt/format.go b/src/fmt/format.go index c811cc6a3d..f7ac047229 100644 --- a/src/fmt/format.go +++ b/src/fmt/format.go @@ -23,16 +23,6 @@ const ( unsigned = false ) -var padZeroBytes = make([]byte, nByte) -var padSpaceBytes = make([]byte, nByte) - -func init() { - for i := 0; i < nByte; i++ { - padZeroBytes[i] = '0' - padSpaceBytes[i] = ' ' - } -} - // flags placed in a separate struct for easy clearing. type fmtFlags struct { widPresent bool @@ -72,84 +62,74 @@ func (f *fmt) init(buf *buffer) { f.clearflags() } -// computePadding computes left and right padding widths (only one will be non-zero). -func (f *fmt) computePadding(width int) (padding []byte, leftWidth, rightWidth int) { - left := !f.minus - w := f.wid - if w < 0 { - left = false - w = -w - } - w -= width - if w > 0 { - if left && f.zero { - return padZeroBytes, w, 0 - } - if left { - return padSpaceBytes, w, 0 - } else { - // can't be zero padding on the right - return padSpaceBytes, 0, w - } - } - return -} - // writePadding generates n bytes of padding. -func (f *fmt) writePadding(n int, padding []byte) { - for n > 0 { - m := n - if m > nByte { - m = nByte - } - f.buf.Write(padding[0:m]) - n -= m +func (f *fmt) writePadding(n int) { + if n <= 0 { // No padding bytes needed. + return } + buf := *f.buf + oldLen := len(buf) + newLen := oldLen + n + // Make enough room for padding. + if newLen > cap(buf) { + buf = make(buffer, cap(buf)*2+n) + copy(buf, *f.buf) + } + // Decide which byte the padding should be filled with. + padByte := byte(' ') + if f.zero { + padByte = byte('0') + } + // Fill padding with padByte. + padding := buf[oldLen:newLen] + for i := range padding { + padding[i] = padByte + } + *f.buf = buf[:newLen] } -// pad appends b to f.buf, padded on left (w > 0) or right (w < 0 or f.minus). +// pad appends b to f.buf, padded on left (!f.minus) or right (f.minus). func (f *fmt) pad(b []byte) { if !f.widPresent || f.wid == 0 { f.buf.Write(b) return } - padding, left, right := f.computePadding(utf8.RuneCount(b)) - if left > 0 { - f.writePadding(left, padding) - } - f.buf.Write(b) - if right > 0 { - f.writePadding(right, padding) + width := f.wid - utf8.RuneCount(b) + if !f.minus { + // left padding + f.writePadding(width) + f.buf.Write(b) + } else { + // right padding + f.buf.Write(b) + f.writePadding(width) } } -// padString appends s to buf, padded on left (w > 0) or right (w < 0 or f.minus). +// padString appends s to f.buf, padded on left (!f.minus) or right (f.minus). func (f *fmt) padString(s string) { if !f.widPresent || f.wid == 0 { f.buf.WriteString(s) return } - padding, left, right := f.computePadding(utf8.RuneCountInString(s)) - if left > 0 { - f.writePadding(left, padding) - } - f.buf.WriteString(s) - if right > 0 { - f.writePadding(right, padding) + width := f.wid - utf8.RuneCountInString(s) + if !f.minus { + // left padding + f.writePadding(width) + f.buf.WriteString(s) + } else { + // right padding + f.buf.WriteString(s) + f.writePadding(width) } } -var ( - trueBytes = []byte("true") - falseBytes = []byte("false") -) - // fmt_boolean formats a boolean. func (f *fmt) fmt_boolean(v bool) { if v { - f.pad(trueBytes) + f.padString("true") } else { - f.pad(falseBytes) + f.padString("false") } } @@ -526,5 +506,5 @@ func (f *fmt) fmt_complex(r, j float64, size int, verb rune) { f.space = oldSpace f.plus = oldPlus f.wid = oldWid - f.buf.Write(irparenBytes) + f.buf.WriteString("i)") } diff --git a/src/fmt/print.go b/src/fmt/print.go index ebfa13e4d3..b59599da65 100644 --- a/src/fmt/print.go +++ b/src/fmt/print.go @@ -13,24 +13,24 @@ import ( "unicode/utf8" ) -// Some constants in the form of bytes, to avoid string overhead. -// Needlessly fastidious, I suppose. -var ( - commaSpaceBytes = []byte(", ") - nilAngleBytes = []byte("") - nilParenBytes = []byte("(nil)") - nilBytes = []byte("nil") - mapBytes = []byte("map[") - percentBangBytes = []byte("%!") - missingBytes = []byte("(MISSING)") - badIndexBytes = []byte("(BADINDEX)") - panicBytes = []byte("(PANIC=") - extraBytes = []byte("%!(EXTRA ") - irparenBytes = []byte("i)") - bytesBytes = []byte("[]byte{") - badWidthBytes = []byte("%!(BADWIDTH)") - badPrecBytes = []byte("%!(BADPREC)") - noVerbBytes = []byte("%!(NOVERB)") +// Strings for use with buffer.WriteString. +// This is less overhead than using buffer.Write with byte arrays. +const ( + commaSpaceString = ", " + nilAngleString = "" + nilParenString = "(nil)" + nilString = "nil" + mapString = "map[" + percentBangString = "%!" + missingString = "(MISSING)" + badIndexString = "(BADINDEX)" + panicString = "(PANIC=" + extraString = "%!(EXTRA " + bytesString = "[]byte{" + badWidthString = "%!(BADWIDTH)" + badPrecString = "%!(BADPREC)" + noVerbString = "%!(NOVERB)" + invReflectString = "" ) // State represents the printer state passed to custom formatters. @@ -75,25 +75,22 @@ type GoStringer interface { // Use simple []byte instead of bytes.Buffer to avoid large dependency. type buffer []byte -func (b *buffer) Write(p []byte) (n int, err error) { +func (b *buffer) Write(p []byte) { *b = append(*b, p...) - return len(p), nil } -func (b *buffer) WriteString(s string) (n int, err error) { +func (b *buffer) WriteString(s string) { *b = append(*b, s...) - return len(s), nil } -func (b *buffer) WriteByte(c byte) error { +func (b *buffer) WriteByte(c byte) { *b = append(*b, c) - return nil } -func (bp *buffer) WriteRune(r rune) error { +func (bp *buffer) WriteRune(r rune) { if r < utf8.RuneSelf { *bp = append(*bp, byte(r)) - return nil + return } b := *bp @@ -103,7 +100,6 @@ func (bp *buffer) WriteRune(r rune) error { } w := utf8.EncodeRune(b[n:n+utf8.UTFMax], r) *bp = b[:n+w] - return nil } type pp struct { @@ -169,14 +165,11 @@ func (p *pp) Flag(b int) bool { return false } -func (p *pp) add(c rune) { - p.buf.WriteRune(c) -} - // Implement Write so we can call Fprintf on a pp (through State), for // recursive use in custom verbs. func (p *pp) Write(b []byte) (ret int, err error) { - return p.buf.Write(b) + p.buf.Write(b) + return len(b), nil } // These routines end in 'f' and take a format string. @@ -309,7 +302,7 @@ func parsenum(s string, start, end int) (num int, isnum bool, newi int) { func (p *pp) unknownType(v reflect.Value) { if !v.IsValid() { - p.buf.Write(nilAngleBytes) + p.buf.WriteString(nilAngleString) return } p.buf.WriteByte('?') @@ -319,23 +312,22 @@ func (p *pp) unknownType(v reflect.Value) { func (p *pp) badVerb(verb rune) { p.erroring = true - p.add('%') - p.add('!') - p.add(verb) - p.add('(') + p.buf.WriteString(percentBangString) + p.buf.WriteRune(verb) + p.buf.WriteByte('(') switch { case p.arg != nil: p.buf.WriteString(reflect.TypeOf(p.arg).String()) - p.add('=') + p.buf.WriteByte('=') p.printArg(p.arg, 'v', 0) case p.value.IsValid(): p.buf.WriteString(p.value.Type().String()) - p.add('=') + p.buf.WriteByte('=') p.printValue(p.value, 'v', 0) default: - p.buf.Write(nilAngleBytes) + p.buf.WriteString(nilAngleString) } - p.add(')') + p.buf.WriteByte(')') p.erroring = false } @@ -538,12 +530,12 @@ func (p *pp) fmtBytes(v []byte, verb rune, typ reflect.Type, depth int) { p.buf.WriteString("[]byte(nil)") } else { p.buf.WriteString(typ.String()) - p.buf.Write(nilParenBytes) + p.buf.WriteString(nilParenString) } return } if typ == nil { - p.buf.Write(bytesBytes) + p.buf.WriteString(bytesString) } else { p.buf.WriteString(typ.String()) p.buf.WriteByte('{') @@ -554,7 +546,7 @@ func (p *pp) fmtBytes(v []byte, verb rune, typ reflect.Type, depth int) { for i, c := range v { if i > 0 { if p.fmt.sharpV { - p.buf.Write(commaSpaceBytes) + p.buf.WriteString(commaSpaceString) } else { p.buf.WriteByte(' ') } @@ -605,18 +597,17 @@ func (p *pp) fmtPointer(value reflect.Value, verb rune) { } if p.fmt.sharpV { - p.add('(') + p.buf.WriteByte('(') p.buf.WriteString(value.Type().String()) - p.add(')') - p.add('(') + p.buf.WriteString(")(") if u == 0 { - p.buf.Write(nilBytes) + p.buf.WriteString(nilString) } else { p.fmt0x64(uint64(u), true) } - p.add(')') + p.buf.WriteByte(')') } else if verb == 'v' && u == 0 { - p.buf.Write(nilAngleBytes) + p.buf.WriteString(nilAngleString) } else { if use0x64 { p.fmt0x64(uint64(u), !p.fmt.sharp) @@ -637,7 +628,7 @@ func (p *pp) catchPanic(arg interface{}, verb rune) { // Stringer that fails to guard against nil or a nil pointer for a // value receiver, and in either case, "" is a nice result. if v := reflect.ValueOf(arg); v.Kind() == reflect.Ptr && v.IsNil() { - p.buf.Write(nilAngleBytes) + p.buf.WriteString(nilAngleString) return } // Otherwise print a concise panic message. Most of the time the panic @@ -647,9 +638,9 @@ func (p *pp) catchPanic(arg interface{}, verb rune) { panic(err) } p.fmt.clearflags() // We are done, and for this output we want default behavior. - p.buf.Write(percentBangBytes) - p.add(verb) - p.buf.Write(panicBytes) + p.buf.WriteString(percentBangString) + p.buf.WriteRune(verb) + p.buf.WriteString(panicString) p.panicking = true p.printArg(err, 'v', 0) p.panicking = false @@ -741,7 +732,7 @@ func (p *pp) printArg(arg interface{}, verb rune, depth int) (wasString bool) { if arg == nil { if verb == 'T' || verb == 'v' { - p.fmt.pad(nilAngleBytes) + p.fmt.padString(nilAngleString) } else { p.badVerb(verb) } @@ -817,7 +808,7 @@ func (p *pp) printArg(arg interface{}, verb rune, depth int) (wasString bool) { func (p *pp) printValue(value reflect.Value, verb rune, depth int) (wasString bool) { if !value.IsValid() { if verb == 'T' || verb == 'v' { - p.buf.Write(nilAngleBytes) + p.buf.WriteString(nilAngleString) } else { p.badVerb(verb) } @@ -858,7 +849,7 @@ func (p *pp) printReflectValue(value reflect.Value, verb rune, depth int) (wasSt BigSwitch: switch f := value; f.Kind() { case reflect.Invalid: - p.buf.WriteString("") + p.buf.WriteString(invReflectString) case reflect.Bool: p.fmtBool(f.Bool(), verb) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: @@ -883,18 +874,18 @@ BigSwitch: if p.fmt.sharpV { p.buf.WriteString(f.Type().String()) if f.IsNil() { - p.buf.WriteString("(nil)") + p.buf.WriteString(nilParenString) break } p.buf.WriteByte('{') } else { - p.buf.Write(mapBytes) + p.buf.WriteString(mapString) } keys := f.MapKeys() for i, key := range keys { if i > 0 { if p.fmt.sharpV { - p.buf.Write(commaSpaceBytes) + p.buf.WriteString(commaSpaceString) } else { p.buf.WriteByte(' ') } @@ -912,13 +903,13 @@ BigSwitch: if p.fmt.sharpV { p.buf.WriteString(value.Type().String()) } - p.add('{') + p.buf.WriteByte('{') v := f t := v.Type() for i := 0; i < v.NumField(); i++ { if i > 0 { if p.fmt.sharpV { - p.buf.Write(commaSpaceBytes) + p.buf.WriteString(commaSpaceString) } else { p.buf.WriteByte(' ') } @@ -937,9 +928,9 @@ BigSwitch: if !value.IsValid() { if p.fmt.sharpV { p.buf.WriteString(f.Type().String()) - p.buf.Write(nilParenBytes) + p.buf.WriteString(nilParenString) } else { - p.buf.Write(nilAngleBytes) + p.buf.WriteString(nilAngleString) } } else { wasString = p.printValue(value, verb, depth+1) @@ -982,7 +973,7 @@ BigSwitch: for i := 0; i < f.Len(); i++ { if i > 0 { if p.fmt.sharpV { - p.buf.Write(commaSpaceBytes) + p.buf.WriteString(commaSpaceString) } else { p.buf.WriteByte(' ') } @@ -1149,7 +1140,7 @@ func (p *pp) doPrintf(format string, a []interface{}) { p.fmt.wid, p.fmt.widPresent, argNum = intFromArg(a, argNum) if !p.fmt.widPresent { - p.buf.Write(badWidthBytes) + p.buf.WriteString(badWidthString) } // We have a negative width, so take its value and ensure @@ -1182,7 +1173,7 @@ func (p *pp) doPrintf(format string, a []interface{}) { p.fmt.precPresent = false } if !p.fmt.precPresent { - p.buf.Write(badPrecBytes) + p.buf.WriteString(badPrecString) } afterIndex = false } else { @@ -1199,7 +1190,7 @@ func (p *pp) doPrintf(format string, a []interface{}) { } if i >= end { - p.buf.Write(noVerbBytes) + p.buf.WriteString(noVerbString) continue } c, w := utf8.DecodeRuneInString(format[i:]) @@ -1210,14 +1201,14 @@ func (p *pp) doPrintf(format string, a []interface{}) { continue } if !p.goodArgNum { - p.buf.Write(percentBangBytes) - p.add(c) - p.buf.Write(badIndexBytes) + p.buf.WriteString(percentBangString) + p.buf.WriteRune(c) + p.buf.WriteString(badIndexString) continue } else if argNum >= len(a) { // out of operands - p.buf.Write(percentBangBytes) - p.add(c) - p.buf.Write(missingBytes) + p.buf.WriteString(percentBangString) + p.buf.WriteRune(c) + p.buf.WriteString(missingString) continue } arg := a[argNum] @@ -1235,6 +1226,12 @@ func (p *pp) doPrintf(format string, a []interface{}) { p.fmt.plusV = true } } + + // Use space padding instead of zero padding to the right. + if p.fmt.minus { + p.fmt.zero = false + } + p.printArg(arg, c, 0) } @@ -1242,7 +1239,7 @@ func (p *pp) doPrintf(format string, a []interface{}) { // out of order, in which case it's too expensive to detect if they've all // been used and arguably OK if they're not. if !p.reordered && argNum < len(a) { - p.buf.Write(extraBytes) + p.buf.WriteString(extraString) for ; argNum < len(a); argNum++ { arg := a[argNum] if arg != nil { @@ -1251,7 +1248,7 @@ func (p *pp) doPrintf(format string, a []interface{}) { } p.printArg(arg, 'v', 0) if argNum+1 < len(a) { - p.buf.Write(commaSpaceBytes) + p.buf.WriteString(commaSpaceString) } } p.buf.WriteByte(')') diff --git a/src/fmt/scan.go b/src/fmt/scan.go index 4618ed4a82..fa63e4983a 100644 --- a/src/fmt/scan.go +++ b/src/fmt/scan.go @@ -15,14 +15,6 @@ import ( "unicode/utf8" ) -// runeUnreader is the interface to something that can unread runes. -// If the object provided to Scan does not satisfy this interface, -// a local buffer will be used to back up the input, but its contents -// will be lost when Scan returns. -type runeUnreader interface { - UnreadRune() error -} - // ScanState represents the scanner state passed to custom scanners. // Scanners may do rune-at-a-time scanning or ask the ScanState // to discover the next space-delimited token. @@ -163,12 +155,10 @@ const eof = -1 // ss is the internal implementation of ScanState. type ss struct { - rr io.RuneReader // where to read input - buf buffer // token accumulator - peekRune rune // one-rune lookahead - prevRune rune // last rune returned by ReadRune - count int // runes consumed so far. - atEOF bool // already read EOF + rs io.RuneScanner // where to read input + buf buffer // token accumulator + count int // runes consumed so far. + atEOF bool // already read EOF ssave } @@ -191,23 +181,17 @@ func (s *ss) Read(buf []byte) (n int, err error) { } func (s *ss) ReadRune() (r rune, size int, err error) { - if s.peekRune >= 0 { - s.count++ - r = s.peekRune - size = utf8.RuneLen(r) - s.prevRune = r - s.peekRune = -1 - return - } - if s.atEOF || s.nlIsEnd && s.prevRune == '\n' || s.count >= s.argLimit { + if s.atEOF || s.count >= s.argLimit { err = io.EOF return } - r, size, err = s.rr.ReadRune() + r, size, err = s.rs.ReadRune() if err == nil { s.count++ - s.prevRune = r + if s.nlIsEnd && r == '\n' { + s.atEOF = true + } } else if err == io.EOF { s.atEOF = true } @@ -246,12 +230,8 @@ func (s *ss) mustReadRune() (r rune) { } func (s *ss) UnreadRune() error { - if u, ok := s.rr.(runeUnreader); ok { - u.UnreadRune() - } else { - s.peekRune = s.prevRune - } - s.prevRune = -1 + s.rs.UnreadRune() + s.atEOF = false s.count-- return nil } @@ -326,13 +306,14 @@ func (s *ss) SkipSpace() { } // readRune is a structure to enable reading UTF-8 encoded code points -// from an io.Reader. It is used if the Reader given to the scanner does -// not already implement io.RuneReader. +// from an io.Reader. It is used if the Reader given to the scanner does +// not already implement io.RuneScanner. type readRune struct { - reader io.Reader - buf [utf8.UTFMax]byte // used only inside ReadRune - pending int // number of bytes in pendBuf; only >0 for bad UTF-8 - pendBuf [utf8.UTFMax]byte // bytes left over + reader io.Reader + buf [utf8.UTFMax]byte // used only inside ReadRune + pending int // number of bytes in pendBuf; only >0 for bad UTF-8 + pendBuf [utf8.UTFMax]byte // bytes left over + peekRune rune // if >=0 next rune; when <0 is ^(previous Rune) } // readByte returns the next byte from the input, which may be @@ -344,33 +325,35 @@ func (r *readRune) readByte() (b byte, err error) { r.pending-- return } - n, err := io.ReadFull(r.reader, r.pendBuf[0:1]) - if n != 1 { - return 0, err + _, err = r.reader.Read(r.pendBuf[:1]) + if err != nil { + return } return r.pendBuf[0], err } -// unread saves the bytes for the next read. -func (r *readRune) unread(buf []byte) { - copy(r.pendBuf[r.pending:], buf) - r.pending += len(buf) -} - // ReadRune returns the next UTF-8 encoded code point from the // io.Reader inside r. func (r *readRune) ReadRune() (rr rune, size int, err error) { + if r.peekRune >= 0 { + rr = r.peekRune + r.peekRune = ^r.peekRune + size = utf8.RuneLen(rr) + return + } r.buf[0], err = r.readByte() if err != nil { - return 0, 0, err + return } if r.buf[0] < utf8.RuneSelf { // fast check for common ASCII case rr = rune(r.buf[0]) size = 1 // Known to be 1. + // Flip the bits of the rune so it's available to UnreadRune. + r.peekRune = ^rr return } var n int - for n = 1; !utf8.FullRune(r.buf[0:n]); n++ { + for n = 1; !utf8.FullRune(r.buf[:n]); n++ { r.buf[n], err = r.readByte() if err != nil { if err == io.EOF { @@ -380,13 +363,25 @@ func (r *readRune) ReadRune() (rr rune, size int, err error) { return } } - rr, size = utf8.DecodeRune(r.buf[0:n]) - if size < n { // an error - r.unread(r.buf[size:n]) + rr, size = utf8.DecodeRune(r.buf[:n]) + if size < n { // an error, save the bytes for the next read + copy(r.pendBuf[r.pending:], r.buf[size:n]) + r.pending += n - size } + // Flip the bits of the rune so it's available to UnreadRune. + r.peekRune = ^rr return } +func (r *readRune) UnreadRune() error { + if r.peekRune >= 0 { + return errors.New("fmt: scanning called UnreadRune with no rune available") + } + // Reverse bit flip of previously read rune to obtain valid >=0 state. + r.peekRune = ^r.peekRune + return nil +} + var ssFree = sync.Pool{ New: func() interface{} { return new(ss) }, } @@ -394,15 +389,13 @@ var ssFree = sync.Pool{ // newScanState allocates a new ss struct or grab a cached one. func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) { s = ssFree.Get().(*ss) - if rr, ok := r.(io.RuneReader); ok { - s.rr = rr + if rs, ok := r.(io.RuneScanner); ok { + s.rs = rs } else { - s.rr = &readRune{reader: r} + s.rs = &readRune{reader: r, peekRune: -1} } s.nlIsSpace = nlIsSpace s.nlIsEnd = nlIsEnd - s.prevRune = -1 - s.peekRune = -1 s.atEOF = false s.limit = hugeWid s.argLimit = hugeWid @@ -424,7 +417,7 @@ func (s *ss) free(old ssave) { return } s.buf = s.buf[:0] - s.rr = nil + s.rs = nil ssFree.Put(s) } @@ -846,7 +839,7 @@ func (s *ss) quotedString() string { return string(s.buf) case '"': // Double-quoted: Include the quotes and let strconv.Unquote do the backslash escapes. - s.buf.WriteRune(quote) + s.buf.WriteByte('"') for { r := s.mustReadRune() s.buf.WriteRune(r) @@ -1142,7 +1135,7 @@ func (s *ss) advance(format string) (i int) { } // doScanf does the real work when scanning with a format string. -// At the moment, it handles only pointers to basic types. +// At the moment, it handles only pointers to basic types. func (s *ss) doScanf(format string, a []interface{}) (numProcessed int, err error) { defer errorHandler(&err) end := len(format) - 1 diff --git a/src/fmt/scan_test.go b/src/fmt/scan_test.go index ce6f08659a..3302a7ca2d 100644 --- a/src/fmt/scan_test.go +++ b/src/fmt/scan_test.go @@ -1001,6 +1001,18 @@ func BenchmarkScanRecursiveInt(b *testing.B) { } } +func BenchmarkScanRecursiveIntReaderWrapper(b *testing.B) { + b.ResetTimer() + ints := makeInts(intCount) + var r RecursiveInt + for i := b.N - 1; i >= 0; i-- { + buf := newReader(string(ints)) + b.StartTimer() + Fscan(buf, &r) + b.StopTimer() + } +} + // Issue 9124. // %x on bytes couldn't handle non-space bytes terminating the scan. func TestHexBytes(t *testing.T) { diff --git a/src/go/build/build.go b/src/go/build/build.go index c1b70bcdd7..d3b83b9f46 100644 --- a/src/go/build/build.go +++ b/src/go/build/build.go @@ -256,34 +256,6 @@ func (ctxt *Context) SrcDirs() []string { // if set, or else the compiled code's GOARCH, GOOS, and GOROOT. var Default Context = defaultContext() -// Also known to cmd/dist/build.go. -var cgoEnabled = map[string]bool{ - "darwin/386": true, - "darwin/amd64": true, - "darwin/arm": true, - "darwin/arm64": true, - "dragonfly/amd64": true, - "freebsd/386": true, - "freebsd/amd64": true, - "freebsd/arm": true, - "linux/386": true, - "linux/amd64": true, - "linux/arm": true, - "linux/arm64": true, - "linux/ppc64le": true, - "android/386": true, - "android/amd64": true, - "android/arm": true, - "netbsd/386": true, - "netbsd/amd64": true, - "netbsd/arm": true, - "openbsd/386": true, - "openbsd/amd64": true, - "solaris/amd64": true, - "windows/386": true, - "windows/amd64": true, -} - func defaultContext() Context { var c Context @@ -386,6 +358,7 @@ type Package struct { CXXFiles []string // .cc, .cpp and .cxx source files MFiles []string // .m (Objective-C) source files HFiles []string // .h, .hh, .hpp and .hxx source files + FFiles []string // .f, .F, .for and .f90 Fortran source files SFiles []string // .s source files SwigFiles []string // .swig files SwigCXXFiles []string // .swigcxx files @@ -395,6 +368,7 @@ type Package struct { CgoCFLAGS []string // Cgo CFLAGS directives CgoCPPFLAGS []string // Cgo CPPFLAGS directives CgoCXXFLAGS []string // Cgo CXXFLAGS directives + CgoFFLAGS []string // Cgo FFLAGS directives CgoLDFLAGS []string // Cgo LDFLAGS directives CgoPkgConfig []string // Cgo pkg-config directives @@ -731,6 +705,9 @@ Found: case ".h", ".hh", ".hpp", ".hxx": p.HFiles = append(p.HFiles, name) continue + case ".f", ".F", ".for", ".f90": + p.FFiles = append(p.FFiles, name) + continue case ".s": p.SFiles = append(p.SFiles, name) continue @@ -1045,7 +1022,7 @@ func (ctxt *Context) matchFile(dir, name string, returnImports bool, allTags map } switch ext { - case ".go", ".c", ".cc", ".cxx", ".cpp", ".m", ".s", ".h", ".hh", ".hpp", ".hxx", ".S", ".swig", ".swigcxx": + case ".go", ".c", ".cc", ".cxx", ".cpp", ".m", ".s", ".h", ".hh", ".hpp", ".hxx", ".f", ".F", ".f90", ".S", ".swig", ".swigcxx": // tentatively okay - read to make sure case ".syso": // binary, no reading @@ -1236,6 +1213,8 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) di.CgoCPPFLAGS = append(di.CgoCPPFLAGS, args...) case "CXXFLAGS": di.CgoCXXFLAGS = append(di.CgoCXXFLAGS, args...) + case "FFLAGS": + di.CgoFFLAGS = append(di.CgoFFLAGS, args...) case "LDFLAGS": di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...) case "pkg-config": @@ -1251,7 +1230,7 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) // the result is safe for the shell. func expandSrcDir(str string, srcdir string) (string, bool) { // "\" delimited paths cause safeCgoName to fail - // so convert native paths with a different delimeter + // so convert native paths with a different delimiter // to "/" before starting (eg: on windows). srcdir = filepath.ToSlash(srcdir) diff --git a/src/go/build/build_test.go b/src/go/build/build_test.go index 7312af08b5..9a473f41ec 100644 --- a/src/go/build/build_test.go +++ b/src/go/build/build_test.go @@ -155,15 +155,15 @@ func TestShouldBuild(t *testing.T) { t.Errorf("shouldBuild(file1) = false, want true") } if !reflect.DeepEqual(m, want1) { - t.Errorf("shoudBuild(file1) tags = %v, want %v", m, want1) + t.Errorf("shouldBuild(file1) tags = %v, want %v", m, want1) } m = map[string]bool{} if ctx.shouldBuild([]byte(file2), m) { - t.Errorf("shouldBuild(file2) = true, want fakse") + t.Errorf("shouldBuild(file2) = true, want false") } if !reflect.DeepEqual(m, want2) { - t.Errorf("shoudBuild(file2) tags = %v, want %v", m, want2) + t.Errorf("shouldBuild(file2) tags = %v, want %v", m, want2) } m = map[string]bool{} @@ -172,7 +172,7 @@ func TestShouldBuild(t *testing.T) { t.Errorf("shouldBuild(file3) = false, want true") } if !reflect.DeepEqual(m, want3) { - t.Errorf("shoudBuild(file3) tags = %v, want %v", m, want3) + t.Errorf("shouldBuild(file3) tags = %v, want %v", m, want3) } } diff --git a/src/go/internal/gcimporter/exportdata.go b/src/go/internal/gcimporter/exportdata.go index 18bea415ae..4c0d2fe761 100644 --- a/src/go/internal/gcimporter/exportdata.go +++ b/src/go/internal/gcimporter/exportdata.go @@ -52,33 +52,11 @@ func FindExportData(r *bufio.Reader) (hdr string, err error) { if string(line) == "!\n" { // Archive file. Scan to __.PKGDEF. var name string - var size int - if name, size, err = readGopackHeader(r); err != nil { + if name, _, err = readGopackHeader(r); err != nil { return } - // Optional leading __.GOSYMDEF or __.SYMDEF. - // Read and discard. - if name == "__.SYMDEF" || name == "__.GOSYMDEF" { - const block = 4096 - tmp := make([]byte, block) - for size > 0 { - n := size - if n > block { - n = block - } - if _, err = io.ReadFull(r, tmp[:n]); err != nil { - return - } - size -= n - } - - if name, _, err = readGopackHeader(r); err != nil { - return - } - } - - // First real entry should be __.PKGDEF. + // First entry should be __.PKGDEF. if name != "__.PKGDEF" { err = errors.New("go archive is missing __.PKGDEF") return diff --git a/src/go/internal/gcimporter/gcimporter.go b/src/go/internal/gcimporter/gcimporter.go index 052277f4fe..b2848c3023 100644 --- a/src/go/internal/gcimporter/gcimporter.go +++ b/src/go/internal/gcimporter/gcimporter.go @@ -451,7 +451,7 @@ func (p *parser) parseMapType(parent *types.Package) types.Type { // For qualified names, the returned package is nil (and not created if // it doesn't exist yet) unless materializePkg is set (which creates an // unnamed package with valid package path). In the latter case, a -// subequent import clause is expected to provide a name for the package. +// subsequent import clause is expected to provide a name for the package. // func (p *parser) parseName(parent *types.Package, materializePkg bool) (pkg *types.Package, name string) { pkg = parent diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index f3a26032ee..d3ef7db31e 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -1597,23 +1597,19 @@ func (p *parser) parseBinaryExpr(lhs bool, prec1 int) ast.Expr { } x := p.parseUnaryExpr(lhs) - for _, prec := p.tokPrec(); prec >= prec1; prec-- { - for { - op, oprec := p.tokPrec() - if oprec != prec { - break - } - pos := p.expect(op) - if lhs { - p.resolve(x) - lhs = false - } - y := p.parseBinaryExpr(false, prec+1) - x = &ast.BinaryExpr{X: p.checkExpr(x), OpPos: pos, Op: op, Y: p.checkExpr(y)} + for { + op, oprec := p.tokPrec() + if oprec < prec1 { + return x } + pos := p.expect(op) + if lhs { + p.resolve(x) + lhs = false + } + y := p.parseBinaryExpr(false, oprec+1) + x = &ast.BinaryExpr{X: p.checkExpr(x), OpPos: pos, Op: op, Y: p.checkExpr(y)} } - - return x } // If lhs is set and the result is an identifier, it is not resolved. diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go index 803264fb58..0082be9e2e 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -366,7 +366,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b } else { // an untyped non-constant argument may appear if // it contains a (yet untyped non-constant) shift - // epression: convert it to complex128 which will + // expression: convert it to complex128 which will // result in an error (shift of complex value) check.convertUntyped(x, Typ[Complex128]) // x should be invalid now, but be conservative and check diff --git a/src/go/types/testdata/const0.src b/src/go/types/testdata/const0.src index 716a5907ce..a61717887e 100644 --- a/src/go/types/testdata/const0.src +++ b/src/go/types/testdata/const0.src @@ -239,7 +239,7 @@ const ( type A [iota /* ERROR "cannot use iota" */ ]int -// constant expressions with operands accross different +// constant expressions with operands across different // constant declarations must use the right iota values const ( _c0 = iota diff --git a/src/html/template/template_test.go b/src/html/template/template_test.go index 6f70d67de9..46df1f8d49 100644 --- a/src/html/template/template_test.go +++ b/src/html/template/template_test.go @@ -13,7 +13,7 @@ func TestTemplateClone(t *testing.T) { t.Fatal(err) } if len(clone.Templates()) != len(orig.Templates()) { - t.Fatalf("Invalid lenth of t.Clone().Templates()") + t.Fatalf("Invalid length of t.Clone().Templates()") } const want = "stuff" diff --git a/src/image/gif/reader.go b/src/image/gif/reader.go index 6a133124ad..72b52e36e5 100644 --- a/src/image/gif/reader.go +++ b/src/image/gif/reader.go @@ -210,7 +210,7 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error { // for an image". In practice, though, giflib (a widely used C // library) does not enforce this, so we also accept lzwr returning // io.ErrUnexpectedEOF (meaning that the encoded stream hit io.EOF - // before the LZW decoder saw an explict end code), provided that + // before the LZW decoder saw an explicit end code), provided that // the io.ReadFull call above successfully read len(m.Pix) bytes. // See https://golang.org/issue/9856 for an example GIF. if n, err := lzwr.Read(d.tmp[:1]); n != 0 || (err != io.EOF && err != io.ErrUnexpectedEOF) { diff --git a/src/internal/syscall/unix/getentropy_openbsd.go b/src/internal/syscall/unix/getentropy_openbsd.go new file mode 100644 index 0000000000..fd3dabc3f2 --- /dev/null +++ b/src/internal/syscall/unix/getentropy_openbsd.go @@ -0,0 +1,25 @@ +// 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 unix + +import ( + "syscall" + "unsafe" +) + +// getentropy(2)'s syscall number, from /usr/src/sys/kern/syscalls.master +const entropyTrap uintptr = 7 + +// GetEntropy calls the OpenBSD getentropy system call. +func GetEntropy(p []byte) error { + _, _, errno := syscall.Syscall(entropyTrap, + uintptr(unsafe.Pointer(&p[0])), + uintptr(len(p)), + 0) + if errno != 0 { + return errno + } + return nil +} diff --git a/src/internal/syscall/windows/registry/registry_test.go b/src/internal/syscall/windows/registry/registry_test.go index a63c42022d..56069d7684 100644 --- a/src/internal/syscall/windows/registry/registry_test.go +++ b/src/internal/syscall/windows/registry/registry_test.go @@ -213,7 +213,7 @@ func enumerateValues(t *testing.T, k registry.Key) { } } for n, v := range haveNames { - t.Errorf("value %s (%v) is found while enumerating, but has not been cretaed", n, v) + t.Errorf("value %s (%v) is found while enumerating, but has not been created", n, v) } } @@ -335,7 +335,7 @@ func testGetValue(t *testing.T, k registry.Key, test ValueTest, size int) { // read data with short buffer gotsize, gottype, err = k.GetValue(test.Name, make([]byte, size-1)) if err == nil { - t.Errorf("GetValue(%s, [%d]byte) should fail, but suceeded", test.Name, size-1) + t.Errorf("GetValue(%s, [%d]byte) should fail, but succeeded", test.Name, size-1) return } if err != registry.ErrShortBuffer { diff --git a/src/make.bash b/src/make.bash index 2531ca4bb3..21cc29730d 100755 --- a/src/make.bash +++ b/src/make.bash @@ -44,6 +44,9 @@ # This is used by cgo. Default is CXX, or, if that is not set, # "g++" or "clang++". # +# FC: Command line to run to compile Fortran code for GOARCH. +# This is used by cgo. Default is "gfortran". +# # GO_DISTFLAGS: extra flags to provide to "dist bootstrap". set -e diff --git a/src/make.bat b/src/make.bat index 0efdcc576c..a64777ee91 100644 --- a/src/make.bat +++ b/src/make.bat @@ -31,6 +31,9 @@ :: :: CC_FOR_TARGET: Command line to run compile C code for GOARCH. :: This is used by cgo. Default is CC. +:: +:: FC: Command line to run to compile Fortran code. +:: This is used by cgo. Default is "gfortran". @echo off diff --git a/src/math/big/float.go b/src/math/big/float.go index b1c748c9a5..6460620bde 100644 --- a/src/math/big/float.go +++ b/src/math/big/float.go @@ -1427,7 +1427,7 @@ func (z *Float) Add(x, y *Float) *Float { } if x.form == finite && y.form == finite { - // x + y (commom case) + // x + y (common case) z.neg = x.neg if x.neg == y.neg { // x + y == x + y diff --git a/src/math/big/floatconv.go b/src/math/big/floatconv.go index 37d5c06a6f..a884df6fe1 100644 --- a/src/math/big/floatconv.go +++ b/src/math/big/floatconv.go @@ -85,7 +85,7 @@ func (z *Float) scan(r io.ByteScanner, base int) (f *Float, b int, err error) { if fcount < 0 { // The mantissa has a "decimal" point ddd.dddd; and // -fcount is the number of digits to the right of '.'. - // Adjust relevant exponent accodingly. + // Adjust relevant exponent accordingly. d := int64(fcount) switch b { case 10: diff --git a/src/net/dnsclient.go b/src/net/dnsclient.go index 5dc2a0368c..5094ac4f1b 100644 --- a/src/net/dnsclient.go +++ b/src/net/dnsclient.go @@ -161,7 +161,7 @@ func isDomainName(s string) bool { return ok } -// absDomainName returns an absoulte domain name which ends with a +// absDomainName returns an absolute domain name which ends with a // trailing dot to match pure Go reverse resolver and all other lookup // routines. // See golang.org/issue/12189. diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go index d7f00c784d..4a5c438e46 100644 --- a/src/net/dnsclient_unix_test.go +++ b/src/net/dnsclient_unix_test.go @@ -67,7 +67,7 @@ var specialDomainNameTests = []struct { // Name resolution APIs and libraries should recognize the // followings as special and should not send any queries. - // Though, we test those names here for verifying nagative + // Though, we test those names here for verifying negative // answers at DNS query-response interaction level. {"localhost.", dnsTypeALL, dnsRcodeNameError}, {"invalid.", dnsTypeALL, dnsRcodeNameError}, @@ -355,7 +355,13 @@ func TestGoLookupIPWithResolverConfig(t *testing.T) { } addrs, err := goLookupIP(tt.name) if err != nil { - if err, ok := err.(*DNSError); !ok || (err.Name != tt.error.(*DNSError).Name || err.Server != tt.error.(*DNSError).Server || err.IsTimeout != tt.error.(*DNSError).IsTimeout) { + // This test uses external network connectivity. + // We need to take care with errors on both + // DNS message exchange layer and DNS + // transport layer because goLookupIP may fail + // when the IP connectivty on node under test + // gets lost during its run. + if err, ok := err.(*DNSError); !ok || tt.error != nil && (err.Name != tt.error.(*DNSError).Name || err.Server != tt.error.(*DNSError).Server || err.IsTimeout != tt.error.(*DNSError).IsTimeout) { t.Errorf("got %v; want %v", err, tt.error) } continue @@ -398,7 +404,7 @@ func TestGoLookupIPOrderFallbackToFile(t *testing.T) { for _, order := range []hostLookupOrder{hostLookupFilesDNS, hostLookupDNSFiles} { name := fmt.Sprintf("order %v", order) - // First ensure that we get an error when contacting a non-existant host. + // First ensure that we get an error when contacting a non-existent host. _, err := goLookupIPOrder("notarealhost", order) if err == nil { t.Errorf("%s: expected error while looking up name not in hosts file", name) diff --git a/src/net/fd_poll_runtime.go b/src/net/fd_poll_runtime.go index 8522ccebfb..5897e3d68c 100644 --- a/src/net/fd_poll_runtime.go +++ b/src/net/fd_poll_runtime.go @@ -120,7 +120,13 @@ func (fd *netFD) setWriteDeadline(t time.Time) error { } func setDeadlineImpl(fd *netFD, t time.Time, mode int) error { - d := runtimeNano() + int64(t.Sub(time.Now())) + diff := int64(t.Sub(time.Now())) + d := runtimeNano() + diff + if d <= 0 && diff > 0 { + // If the user has a deadline in the future, but the delay calculation + // overflows, then set the deadline to the maximum possible value. + d = 1<<63 - 1 + } if t.IsZero() { d = 0 } diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go index 8939dc8baf..e4fed26803 100644 --- a/src/net/http/client_test.go +++ b/src/net/http/client_test.go @@ -273,7 +273,7 @@ func TestClientRedirects(t *testing.T) { t.Fatal("didn't see redirect") } if lastReq.Cancel != cancel { - t.Errorf("expected lastReq to have the cancel channel set on the inital req") + t.Errorf("expected lastReq to have the cancel channel set on the initial req") } checkErr = errors.New("no redirects allowed") diff --git a/src/net/http/cookie.go b/src/net/http/cookie.go index 648709dd99..1ea0e9397a 100644 --- a/src/net/http/cookie.go +++ b/src/net/http/cookie.go @@ -223,7 +223,7 @@ func readCookies(h Header, filter string) []*Cookie { return cookies } -// validCookieDomain returns wheter v is a valid cookie domain-value. +// validCookieDomain returns whether v is a valid cookie domain-value. func validCookieDomain(v string) bool { if isCookieDomainName(v) { return true diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go index 4e19b3e71f..f778acb2b5 100644 --- a/src/net/http/h2_bundle.go +++ b/src/net/http/h2_bundle.go @@ -288,7 +288,7 @@ func http2configureTransport(t1 *Transport) (*http2Transport, error) { } // registerHTTPSProtocol calls Transport.RegisterProtocol but -// convering panics into errors. +// converting panics into errors. func http2registerHTTPSProtocol(t *Transport, rt RoundTripper) (err error) { defer func() { if e := recover(); e != nil { @@ -1410,7 +1410,7 @@ type http2PriorityFrame struct { http2PriorityParam } -// PriorityParam are the stream prioritzation parameters. +// PriorityParam are the stream prioritization parameters. type http2PriorityParam struct { // StreamDep is a 31-bit stream identifier for the // stream that this stream depends on. Zero means no @@ -2850,7 +2850,7 @@ type http2stream struct { weight uint8 state http2streamState sentReset bool // only true once detached from streams map - gotReset bool // only true once detacted from streams map + gotReset bool // only true once detached from streams map gotTrailerHeader bool // HEADER frame for trailers was seen trailer Header // accumulated trailers @@ -4391,7 +4391,7 @@ const http2TrailerPrefix = "Trailer:" // trailers. That worked for a while, until we found the first major // user of Trailers in the wild: gRPC (using them only over http2), // and gRPC libraries permit setting trailers mid-stream without -// predeclarnig them. So: change of plans. We still permit the old +// predeclaring them. So: change of plans. We still permit the old // way, but we also permit this hack: if a Header() key begins with // "Trailer:", the suffix of that key is a Trailer. Because ':' is an // invalid token byte anyway, there is no ambiguity. (And it's already @@ -4605,7 +4605,7 @@ type http2Transport struct { // send in the initial settings frame. It is how many bytes // of response headers are allow. Unlike the http2 spec, zero here // means to use a default limit (currently 10MB). If you actually - // want to advertise an ulimited value to the peer, Transport + // want to advertise an unlimited value to the peer, Transport // interprets the highest possible value here (0xffffffff or 1<<32-1) // to mean no limit. MaxHeaderListSize uint32 @@ -5012,7 +5012,7 @@ const http2maxAllocFrameSize = 512 << 10 // frameBuffer returns a scratch buffer suitable for writing DATA frames. // They're capped at the min of the peer's max frame size or 512KB // (kinda arbitrarily), but definitely capped so we don't allocate 4GB -// bufers. +// buffers. func (cc *http2ClientConn) frameScratchBuffer() []byte { cc.mu.Lock() size := cc.maxFrameSize @@ -6544,7 +6544,7 @@ func (ws *http2writeScheduler) take() (wm http2frameWriteMsg, ok bool) { return ws.takeFrom(q.streamID(), q) } -// zeroCanSend is defered from take. +// zeroCanSend is deferred from take. func (ws *http2writeScheduler) zeroCanSend() { for i := range ws.canSend { ws.canSend[i] = nil diff --git a/src/net/http/httptest/server_test.go b/src/net/http/httptest/server_test.go index c9606f2419..61470c3822 100644 --- a/src/net/http/httptest/server_test.go +++ b/src/net/http/httptest/server_test.go @@ -53,7 +53,7 @@ func TestGetAfterClose(t *testing.T) { res, err = http.Get(ts.URL) if err == nil { body, _ := ioutil.ReadAll(res.Body) - t.Fatalf("Unexected response after close: %v, %v, %s", res.Status, res.Header, body) + t.Fatalf("Unexpected response after close: %v, %v, %s", res.Status, res.Header, body) } } diff --git a/src/net/http/request.go b/src/net/http/request.go index 8cdab02af5..c3e43bb99d 100644 --- a/src/net/http/request.go +++ b/src/net/http/request.go @@ -521,7 +521,7 @@ func cleanHost(in string) string { return in } -// removeZone removes IPv6 zone identifer from host. +// removeZone removes IPv6 zone identifier from host. // E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080" func removeZone(host string) string { if !strings.HasPrefix(host, "[") { diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go index 0ecdf85a56..3d215ff538 100644 --- a/src/net/http/request_test.go +++ b/src/net/http/request_test.go @@ -99,7 +99,7 @@ type parseContentTypeTest struct { var parseContentTypeTests = []parseContentTypeTest{ {false, stringMap{"Content-Type": {"text/plain"}}}, - // Empty content type is legal - shoult be treated as + // Empty content type is legal - should be treated as // application/octet-stream (RFC 2616, section 7.2.1) {false, stringMap{}}, {true, stringMap{"Content-Type": {"text/plain; boundary="}}}, diff --git a/src/net/http/requestwrite_test.go b/src/net/http/requestwrite_test.go index cfb95b0a80..9b70fcbf8a 100644 --- a/src/net/http/requestwrite_test.go +++ b/src/net/http/requestwrite_test.go @@ -573,7 +573,7 @@ func TestRequestWriteClosesBody(t *testing.T) { "Transfer-Encoding: chunked\r\n\r\n" + // TODO: currently we don't buffer before chunking, so we get a // single "m" chunk before the other chunks, as this was the 1-byte - // read from our MultiReader where we stiched the Body back together + // read from our MultiReader where we stitched the Body back together // after sniffing whether the Body was 0 bytes or not. chunk("m") + chunk("y body") + diff --git a/src/net/http/server.go b/src/net/http/server.go index e2d8d277e0..773dd59c6b 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -1374,7 +1374,7 @@ func (c *conn) setState(nc net.Conn, state ConnState) { // badRequestError is a literal string (used by in the server in HTML, // unescaped) to tell the user why their request was bad. It should -// be plain text without user info or other embeddded errors. +// be plain text without user info or other embedded errors. type badRequestError string func (e badRequestError) Error() string { return "Bad Request: " + string(e) } diff --git a/src/net/http/transfer.go b/src/net/http/transfer.go index 6e59af8f6f..7ff4953a14 100644 --- a/src/net/http/transfer.go +++ b/src/net/http/transfer.go @@ -558,21 +558,19 @@ func fixLength(isResponse bool, status int, requestMethod string, header Header, func shouldClose(major, minor int, header Header, removeCloseHeader bool) bool { if major < 1 { return true - } else if major == 1 && minor == 0 { - vv := header["Connection"] - if headerValuesContainsToken(vv, "close") || !headerValuesContainsToken(vv, "keep-alive") { - return true - } - return false - } else { - if headerValuesContainsToken(header["Connection"], "close") { - if removeCloseHeader { - header.Del("Connection") - } - return true - } } - return false + + conv := header["Connection"] + hasClose := headerValuesContainsToken(conv, "close") + if major == 1 && minor == 0 { + return hasClose || !headerValuesContainsToken(conv, "keep-alive") + } + + if hasClose && removeCloseHeader { + header.Del("Connection") + } + + return hasClose } // Parse the trailer header diff --git a/src/net/http/transport.go b/src/net/http/transport.go index 03e9162b14..f622f6f983 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -982,7 +982,7 @@ func (k connectMethodKey) String() string { // (but may be used for non-keep-alive requests as well) type persistConn struct { // alt optionally specifies the TLS NextProto RoundTripper. - // This is used for HTTP/2 today and future protocol laters. + // This is used for HTTP/2 today and future protocols later. // If it's non-nil, the rest of the fields are unused. alt RoundTripper diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index d9da078fa0..e8a4623556 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -853,7 +853,7 @@ func TestTransportExpect100Continue(t *testing.T) { {path: "/100", body: []byte("hello"), sent: 5, status: 200}, // Got 100 followed by 200, entire body is sent. {path: "/200", body: []byte("hello"), sent: 0, status: 200}, // Got 200 without 100. body isn't sent. {path: "/500", body: []byte("hello"), sent: 0, status: 500}, // Got 500 without 100. body isn't sent. - {path: "/keepalive", body: []byte("hello"), sent: 0, status: 500}, // Althogh without Connection:close, body isn't sent. + {path: "/keepalive", body: []byte("hello"), sent: 0, status: 500}, // Although without Connection:close, body isn't sent. {path: "/timeout", body: []byte("hello"), sent: 5, status: 200}, // Timeout exceeded and entire body is sent. } diff --git a/src/net/interface_bsd.go b/src/net/interface_bsd.go index 208f37f9fd..e0814798e7 100644 --- a/src/net/interface_bsd.go +++ b/src/net/interface_bsd.go @@ -161,7 +161,7 @@ func newAddr(ifi *Interface, m *syscall.InterfaceAddrMessage) (*IPNet, error) { case *syscall.SockaddrInet6: ifa.IP = make(IP, IPv6len) copy(ifa.IP, sa.Addr[:]) - // NOTE: KAME based IPv6 protcol stack usually embeds + // NOTE: KAME based IPv6 protocol stack usually embeds // the interface index in the interface-local or // link-local address as the kernel-internal form. if ifa.IP.IsLinkLocalUnicast() { diff --git a/src/net/interface_darwin.go b/src/net/interface_darwin.go index b7a333849d..b3719d6092 100644 --- a/src/net/interface_darwin.go +++ b/src/net/interface_darwin.go @@ -49,7 +49,7 @@ func newMulticastAddr(ifi *Interface, m *syscall.InterfaceMulticastAddrMessage) case *syscall.SockaddrInet6: ifma := IPAddr{IP: make(IP, IPv6len)} copy(ifma.IP, sa.Addr[:]) - // NOTE: KAME based IPv6 protcol stack usually embeds + // NOTE: KAME based IPv6 protocol stack usually embeds // the interface index in the interface-local or // link-local address as the kernel-internal form. if ifma.IP.IsInterfaceLocalMulticast() || ifma.IP.IsLinkLocalMulticast() { diff --git a/src/net/interface_freebsd.go b/src/net/interface_freebsd.go index c42d90b740..44ef2c1a2a 100644 --- a/src/net/interface_freebsd.go +++ b/src/net/interface_freebsd.go @@ -49,7 +49,7 @@ func newMulticastAddr(ifi *Interface, m *syscall.InterfaceMulticastAddrMessage) case *syscall.SockaddrInet6: ifma := IPAddr{IP: make(IP, IPv6len)} copy(ifma.IP, sa.Addr[:]) - // NOTE: KAME based IPv6 protcol stack usually embeds + // NOTE: KAME based IPv6 protocol stack usually embeds // the interface index in the interface-local or // link-local address as the kernel-internal form. if ifma.IP.IsInterfaceLocalMulticast() || ifma.IP.IsLinkLocalMulticast() { diff --git a/src/net/interface_test.go b/src/net/interface_test.go index 7bdd924150..6721f72bb5 100644 --- a/src/net/interface_test.go +++ b/src/net/interface_test.go @@ -102,7 +102,7 @@ func TestInterfaces(t *testing.T) { } // Test the existence of connected unicast routes for // IPv6. We can assume the existence of ::1/128 when - // at least one looopback interface is installed. + // at least one loopback interface is installed. if supportsIPv6 && stats.loop > 0 && stats.uni6 == 0 { t.Errorf("num IPv6 unicast routes = 0; want >0; summary: %+v", stats) } @@ -155,7 +155,7 @@ func TestInterfaceAddrs(t *testing.T) { } // Test the existence of connected unicast routes for IPv6. // We can assume the existence of ::1/128 when at least one - // looopback interface is installed. + // loopback interface is installed. if supportsIPv6 && stats.loop > 0 && stats.uni6 == 0 { t.Errorf("num IPv6 unicast routes = 0; want >0; summary: %+v", stats) } diff --git a/src/net/interface_unix_test.go b/src/net/interface_unix_test.go index 93b3b79afd..dd20de4ac7 100644 --- a/src/net/interface_unix_test.go +++ b/src/net/interface_unix_test.go @@ -56,7 +56,7 @@ func TestPointToPointInterface(t *testing.T) { for i := 0; i < 3; i++ { ti := &testInterface{local: local, remote: remote} if err := ti.setPointToPoint(5963 + i); err != nil { - t.Skipf("test requries external command: %v", err) + t.Skipf("test requires external command: %v", err) } if err := ti.setup(); err != nil { t.Fatal(err) diff --git a/src/net/ip_test.go b/src/net/ip_test.go index 3d95a73c09..2b24baf58e 100644 --- a/src/net/ip_test.go +++ b/src/net/ip_test.go @@ -379,8 +379,8 @@ var splitJoinTests = []struct { {"", "0", ":0"}, {"google.com", "https%foo", "google.com:https%foo"}, // Go 1.0 behavior - {"127.0.0.1", "", "127.0.0.1:"}, // Go 1.0 behaviour - {"www.google.com", "", "www.google.com:"}, // Go 1.0 behaviour + {"127.0.0.1", "", "127.0.0.1:"}, // Go 1.0 behavior + {"www.google.com", "", "www.google.com:"}, // Go 1.0 behavior } var splitFailureTests = []struct { diff --git a/src/net/ipraw_test.go b/src/net/iprawsock_test.go similarity index 100% rename from src/net/ipraw_test.go rename to src/net/iprawsock_test.go diff --git a/src/net/ipsock_posix.go b/src/net/ipsock_posix.go index 2bddd46a15..d512fc3e57 100644 --- a/src/net/ipsock_posix.go +++ b/src/net/ipsock_posix.go @@ -61,7 +61,7 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { // Some released versions of DragonFly BSD pretend to // accept IPV6_V6ONLY=0 successfully, but the state // still stays IPV6_V6ONLY=1. Eventually DragonFly BSD - // stops preteding, but the transition period would + // stops pretending, but the transition period would // cause unpredictable behavior and we need to avoid // it. // diff --git a/src/net/listen_test.go b/src/net/listen_test.go index 51ffe67238..0fc8889628 100644 --- a/src/net/listen_test.go +++ b/src/net/listen_test.go @@ -157,7 +157,7 @@ var dualStackTCPListenerTests = []struct { network2, address2 string // second listener xerr error // expected error value, nil or other }{ - // Test cases and expected results for the attemping 2nd listen on the same port + // Test cases and expected results for the attempting 2nd listen on the same port // 1st listen 2nd listen darwin freebsd linux openbsd // ------------------------------------------------------------------------------------ // "tcp" "" "tcp" "" - - - - @@ -216,9 +216,12 @@ var dualStackTCPListenerTests = []struct { // TestDualStackTCPListener tests both single and double listen // to a test listener with various address families, different // listening address and same port. +// +// On DragonFly BSD, we expect the kernel version of node under test +// to be greater than or equal to 4.4. func TestDualStackTCPListener(t *testing.T) { switch runtime.GOOS { - case "dragonfly", "nacl", "plan9": // re-enable on dragonfly once the new IP control block management has landed + case "nacl", "plan9": t.Skipf("not supported on %s", runtime.GOOS) } if !supportsIPv4 || !supportsIPv6 { @@ -301,11 +304,14 @@ var dualStackUDPListenerTests = []struct { } // TestDualStackUDPListener tests both single and double listen -// to a test listener with various address families, differnet +// to a test listener with various address families, different // listening address and same port. +// +// On DragonFly BSD, we expect the kernel version of node under test +// to be greater than or equal to 4.4. func TestDualStackUDPListener(t *testing.T) { switch runtime.GOOS { - case "dragonfly", "nacl", "plan9": // re-enable on dragonfly once the new IP control block management has landed + case "nacl", "plan9": t.Skipf("not supported on %s", runtime.GOOS) } if !supportsIPv4 || !supportsIPv6 { diff --git a/src/net/main_conf_test.go b/src/net/main_conf_test.go new file mode 100644 index 0000000000..ba91e8b17d --- /dev/null +++ b/src/net/main_conf_test.go @@ -0,0 +1,38 @@ +// Copyright 2015 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. + +// +build !nacl,!plan9,!windows + +package net + +// forceGoDNS forces the resolver configuration to use the pure Go resolver +// and returns a fixup function to restore the old settings. +func forceGoDNS() func() { + c := systemConf() + oldGo := c.netGo + oldCgo := c.netCgo + fixup := func() { + c.netGo = oldGo + c.netCgo = oldCgo + } + c.netGo = true + c.netCgo = false + return fixup +} + +// forceCgoDNS forces the resolver configuration to use the cgo resolver +// and returns a fixup function to restore the old settings. +// (On non-Unix systems forceCgoDNS returns nil.) +func forceCgoDNS() func() { + c := systemConf() + oldGo := c.netGo + oldCgo := c.netCgo + fixup := func() { + c.netGo = oldGo + c.netCgo = oldCgo + } + c.netGo = false + c.netCgo = true + return fixup +} diff --git a/src/net/non_unix_test.go b/src/net/main_noconf_test.go similarity index 78% rename from src/net/non_unix_test.go rename to src/net/main_noconf_test.go index db3427e7cb..ce978365dc 100644 --- a/src/net/non_unix_test.go +++ b/src/net/main_noconf_test.go @@ -8,7 +8,7 @@ package net import "runtime" -// See unix_test.go for what these (don't) do. +// See main_conf_test.go for what these (don't) do. func forceGoDNS() func() { switch runtime.GOOS { case "plan9", "windows": @@ -18,5 +18,5 @@ func forceGoDNS() func() { } } -// See unix_test.go for what these (don't) do. +// See main_conf_test.go for what these (don't) do. func forceCgoDNS() func() { return nil } diff --git a/src/net/net_windows_test.go b/src/net/net_windows_test.go index df39032721..ab21798969 100644 --- a/src/net/net_windows_test.go +++ b/src/net/net_windows_test.go @@ -315,7 +315,7 @@ func TestInterfacesWithNetsh(t *testing.T) { } func netshInterfaceIPv4ShowAddress(name string, netshOutput []byte) []string { - // adress information is listed like: + // Address information is listed like: // //Configuration for interface "Local Area Connection" // DHCP enabled: Yes @@ -378,7 +378,7 @@ func netshInterfaceIPv4ShowAddress(name string, netshOutput []byte) []string { } func netshInterfaceIPv6ShowAddress(name string, netshOutput []byte) []string { - // adress information is listed like: + // Address information is listed like: // //Address ::1 Parameters //--------------------------------------------------------- diff --git a/src/net/sendfile_windows.go b/src/net/sendfile_windows.go index f3f3b54b88..eafb372f7c 100644 --- a/src/net/sendfile_windows.go +++ b/src/net/sendfile_windows.go @@ -18,7 +18,7 @@ import ( // // if handled == false, sendFile performed no work. // -// Note that sendfile for windows does not suppport >2GB file. +// Note that sendfile for windows does not support >2GB file. func sendFile(fd *netFD, r io.Reader) (written int64, err error, handled bool) { var n int64 = 0 // by default, copy until EOF diff --git a/src/net/tcp_test.go b/src/net/tcpsock_test.go similarity index 100% rename from src/net/tcp_test.go rename to src/net/tcpsock_test.go diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go index 91bbb57304..109afd3cea 100644 --- a/src/net/textproto/reader.go +++ b/src/net/textproto/reader.go @@ -581,18 +581,14 @@ func CanonicalMIMEHeaderKey(s string) string { const toLower = 'a' - 'A' // validHeaderFieldByte reports whether b is a valid byte in a header -// field key. This is actually stricter than RFC 7230, which says: +// field name. RFC 7230 says: +// header-field = field-name ":" OWS field-value OWS +// field-name = token // tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / // "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA // token = 1*tchar -// TODO: revisit in Go 1.6+ and possibly expand this. But note that many -// servers have historically dropped '_' to prevent ambiguities when mapping -// to CGI environment variables. func validHeaderFieldByte(b byte) bool { - return ('A' <= b && b <= 'Z') || - ('a' <= b && b <= 'z') || - ('0' <= b && b <= '9') || - b == '-' + return int(b) < len(isTokenTable) && isTokenTable[b] } // canonicalMIMEHeaderKey is like CanonicalMIMEHeaderKey but is @@ -682,3 +678,85 @@ func init() { commonHeader[v] = v } } + +// isTokenTable is a copy of net/http/lex.go's isTokenTable. +// See https://httpwg.github.io/specs/rfc7230.html#rule.token.separators +var isTokenTable = [127]bool{ + '!': true, + '#': true, + '$': true, + '%': true, + '&': true, + '\'': true, + '*': true, + '+': true, + '-': true, + '.': true, + '0': true, + '1': true, + '2': true, + '3': true, + '4': true, + '5': true, + '6': true, + '7': true, + '8': true, + '9': true, + 'A': true, + 'B': true, + 'C': true, + 'D': true, + 'E': true, + 'F': true, + 'G': true, + 'H': true, + 'I': true, + 'J': true, + 'K': true, + 'L': true, + 'M': true, + 'N': true, + 'O': true, + 'P': true, + 'Q': true, + 'R': true, + 'S': true, + 'T': true, + 'U': true, + 'W': true, + 'V': true, + 'X': true, + 'Y': true, + 'Z': true, + '^': true, + '_': true, + '`': true, + 'a': true, + 'b': true, + 'c': true, + 'd': true, + 'e': true, + 'f': true, + 'g': true, + 'h': true, + 'i': true, + 'j': true, + 'k': true, + 'l': true, + 'm': true, + 'n': true, + 'o': true, + 'p': true, + 'q': true, + 'r': true, + 's': true, + 't': true, + 'u': true, + 'v': true, + 'w': true, + 'x': true, + 'y': true, + 'z': true, + '|': true, + '~': true, +} diff --git a/src/net/textproto/reader_test.go b/src/net/textproto/reader_test.go index 9c71594362..8a07adf4d3 100644 --- a/src/net/textproto/reader_test.go +++ b/src/net/textproto/reader_test.go @@ -25,6 +25,12 @@ var canonicalHeaderKeyTests = []canonicalHeaderKeyTest{ {"user-agent", "User-Agent"}, {"USER-AGENT", "User-Agent"}, + // Other valid tchar bytes in tokens: + {"foo-bar_baz", "Foo-Bar_baz"}, + {"foo-bar$baz", "Foo-Bar$baz"}, + {"foo-bar~baz", "Foo-Bar~baz"}, + {"foo-bar*baz", "Foo-Bar*baz"}, + // Non-ASCII or anything with spaces or non-token chars is unchanged: {"üser-agenT", "üser-agenT"}, {"a B", "a B"}, diff --git a/src/net/timeout_test.go b/src/net/timeout_test.go index 98e3164fb9..d80e478c77 100644 --- a/src/net/timeout_test.go +++ b/src/net/timeout_test.go @@ -26,6 +26,8 @@ var dialTimeoutTests = []struct { {-5 * time.Second, 0, -5 * time.Second, 100 * time.Millisecond}, {0, -5 * time.Second, -5 * time.Second, 100 * time.Millisecond}, {-5 * time.Second, 5 * time.Second, -5 * time.Second, 100 * time.Millisecond}, // timeout over deadline + {-1 << 63, 0, time.Second, 100 * time.Millisecond}, + {0, -1 << 63, time.Second, 100 * time.Millisecond}, {50 * time.Millisecond, 0, 100 * time.Millisecond, time.Second}, {0, 50 * time.Millisecond, 100 * time.Millisecond, time.Second}, @@ -99,6 +101,54 @@ func TestDialTimeout(t *testing.T) { } } +var dialTimeoutMaxDurationTests = []struct { + timeout time.Duration + delta time.Duration // for deadline +}{ + // Large timeouts that will overflow an int64 unix nanos. + {1<<63 - 1, 0}, + {0, 1<<63 - 1}, +} + +func TestDialTimeoutMaxDuration(t *testing.T) { + t.Parallel() + + ln, err := newLocalListener("tcp") + if err != nil { + t.Fatal(err) + } + defer ln.Close() + + for i, tt := range dialTimeoutMaxDurationTests { + ch := make(chan error) + max := time.NewTimer(100 * time.Millisecond) + defer max.Stop() + go func() { + d := Dialer{Timeout: tt.timeout} + if tt.delta != 0 { + d.Deadline = time.Now().Add(tt.delta) + } + c, err := d.Dial(ln.Addr().Network(), ln.Addr().String()) + if err == nil { + c.Close() + } + ch <- err + }() + + select { + case <-max.C: + t.Fatalf("#%d: Dial didn't return in an expected time", i) + case err := <-ch: + if perr := parseDialError(err); perr != nil { + t.Error(perr) + } + if err != nil { + t.Errorf("#%d: %v", i, err) + } + } + } +} + var acceptTimeoutTests = []struct { timeout time.Duration xerrs [2]error // expected errors in transition diff --git a/src/net/udp_test.go b/src/net/udpsock_test.go similarity index 99% rename from src/net/udp_test.go rename to src/net/udpsock_test.go index b25f96a3fd..12d1256f0a 100644 --- a/src/net/udp_test.go +++ b/src/net/udpsock_test.go @@ -356,7 +356,7 @@ func TestUDPZeroByteBuffer(t *testing.T) { switch err { case nil: // ReadFrom succeeds default: // Read may timeout, it depends on the platform - if nerr, ok := err.(Error); (!ok || !nerr.Timeout()) && runtime.GOOS != "windows" { // Windows retruns WSAEMSGSIZ + if nerr, ok := err.(Error); (!ok || !nerr.Timeout()) && runtime.GOOS != "windows" { // Windows returns WSAEMSGSIZ t.Fatal(err) } } diff --git a/src/net/unix_test.go b/src/net/unixsock_test.go similarity index 93% rename from src/net/unix_test.go rename to src/net/unixsock_test.go index f0c583068e..f5e069a121 100644 --- a/src/net/unix_test.go +++ b/src/net/unixsock_test.go @@ -440,34 +440,3 @@ func TestUnixUnlink(t *testing.T) { t.Fatal("closing unix listener did not remove unix socket") } } - -// forceGoDNS forces the resolver configuration to use the pure Go resolver -// and returns a fixup function to restore the old settings. -func forceGoDNS() func() { - c := systemConf() - oldGo := c.netGo - oldCgo := c.netCgo - fixup := func() { - c.netGo = oldGo - c.netCgo = oldCgo - } - c.netGo = true - c.netCgo = false - return fixup -} - -// forceCgoDNS forces the resolver configuration to use the cgo resolver -// and returns a fixup function to restore the old settings. -// (On non-Unix systems forceCgoDNS returns nil.) -func forceCgoDNS() func() { - c := systemConf() - oldGo := c.netGo - oldCgo := c.netCgo - fixup := func() { - c.netGo = oldGo - c.netCgo = oldCgo - } - c.netGo = false - c.netCgo = true - return fixup -} diff --git a/src/net/url/url.go b/src/net/url/url.go index b3513a85a3..48119f4a5d 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -307,14 +307,15 @@ func escape(s string, mode encoding) string { // construct a URL struct directly and set the Opaque field instead of Path. // These still work as well. type URL struct { - Scheme string - Opaque string // encoded opaque data - User *Userinfo // username and password information - Host string // host or host:port - Path string - RawPath string // encoded path hint (Go 1.5 and later only; see EscapedPath method) - RawQuery string // encoded query values, without '?' - Fragment string // fragment for references, without '#' + Scheme string + Opaque string // encoded opaque data + User *Userinfo // username and password information + Host string // host or host:port + Path string + RawPath string // encoded path hint (Go 1.5 and later only; see EscapedPath method) + ForceQuery bool // append a query ('?') even if RawQuery is empty + RawQuery string // encoded query values, without '?' + Fragment string // fragment for references, without '#' } // User returns a Userinfo containing the provided username @@ -459,7 +460,12 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) { } url.Scheme = strings.ToLower(url.Scheme) - rest, url.RawQuery = split(rest, "?", true) + if strings.HasSuffix(rest, "?") { + url.ForceQuery = true + rest = rest[:len(rest)-1] + } else { + rest, url.RawQuery = split(rest, "?", true) + } if !strings.HasPrefix(rest, "/") { if url.Scheme != "" { @@ -684,7 +690,7 @@ func (u *URL) String() string { } buf.WriteString(path) } - if u.RawQuery != "" { + if u.ForceQuery || u.RawQuery != "" { buf.WriteByte('?') buf.WriteString(u.RawQuery) } @@ -913,7 +919,7 @@ func (u *URL) RequestURI() string { result = u.Scheme + ":" + result } } - if u.RawQuery != "" { + if u.ForceQuery || u.RawQuery != "" { result += "?" + u.RawQuery } return result diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go index d3f8487bd7..a3088ec0a3 100644 --- a/src/net/url/url_test.go +++ b/src/net/url/url_test.go @@ -72,6 +72,17 @@ var urltests = []URLTest{ }, "ftp://john%20doe@www.google.com/", }, + // empty query + { + "http://www.google.com/?", + &URL{ + Scheme: "http", + Host: "www.google.com", + Path: "/", + ForceQuery: true, + }, + "", + }, // query { "http://www.google.com/?q=go+language", @@ -874,11 +885,13 @@ var resolveReferenceTests = []struct { // Absolute URL references {"http://foo.com?a=b", "https://bar.com/", "https://bar.com/"}, {"http://foo.com/", "https://bar.com/?a=b", "https://bar.com/?a=b"}, + {"http://foo.com/", "https://bar.com/?", "https://bar.com/?"}, {"http://foo.com/bar", "mailto:foo@example.com", "mailto:foo@example.com"}, // Path-absolute references {"http://foo.com/bar", "/baz", "http://foo.com/baz"}, {"http://foo.com/bar?a=b#f", "/baz", "http://foo.com/baz"}, + {"http://foo.com/bar?a=b", "/baz?", "http://foo.com/baz?"}, {"http://foo.com/bar?a=b", "/baz?c=d", "http://foo.com/baz?c=d"}, // Scheme-relative @@ -1217,6 +1230,15 @@ var requritests = []RequestURITest{ }, "//foo", }, + { + &URL{ + Scheme: "http", + Host: "example.com", + Path: "/foo", + ForceQuery: true, + }, + "/foo?", + }, } func TestRequestURI(t *testing.T) { diff --git a/src/os/exec/exec_test.go b/src/os/exec/exec_test.go index 52b4724ab0..a69b5176d5 100644 --- a/src/os/exec/exec_test.go +++ b/src/os/exec/exec_test.go @@ -341,7 +341,7 @@ func TestExtraFilesFDShuffle(t *testing.T) { // // We want to test that FDs in the child do not get overwritten // by one another as this shuffle occurs. The original implementation - // was buggy in that in some data dependent cases it would ovewrite + // was buggy in that in some data dependent cases it would overwrite // stderr in the child with one of the ExtraFile members. // Testing for this case is difficult because it relies on using // the same FD values as that case. In particular, an FD of 3 diff --git a/src/os/exec/lp_windows_test.go b/src/os/exec/lp_windows_test.go index 8e1d4239bf..042e5a1389 100644 --- a/src/os/exec/lp_windows_test.go +++ b/src/os/exec/lp_windows_test.go @@ -117,7 +117,7 @@ func createEnv(dir, PATH, PATHEXT string) []string { } // createFiles copies srcPath file into multiply files. -// It uses dir as preifx for all destination files. +// It uses dir as prefix for all destination files. func createFiles(t *testing.T, dir string, files []string, srcPath string) { for _, f := range files { installProg(t, filepath.Join(dir, f), srcPath) @@ -431,7 +431,7 @@ var commandTests = []commandTest{ }, { // LookPath(`a.exe`) will find `.\a.exe`, but prefixing that with - // dir `p\a.exe` will refer to not existant file + // dir `p\a.exe` will refer to a non-existent file files: []string{`a.exe`, `p\not_important_file`}, dir: `p`, arg0: `a.exe`, @@ -440,7 +440,7 @@ var commandTests = []commandTest{ }, { // like above, but making test succeed by installing file - // in refered destination (so LookPath(`a.exe`) will still + // in referred destination (so LookPath(`a.exe`) will still // find `.\a.exe`, but we successfully execute `p\a.exe`) files: []string{`a.exe`, `p\a.exe`}, dir: `p`, diff --git a/src/os/os_test.go b/src/os/os_test.go index 945724b2b2..b2f45b48f8 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -47,10 +47,10 @@ var sysdir = func() *sysDir { switch runtime.GOOS { case "android": return &sysDir{ - "/system/framework", + "/system/lib", []string{ - "ext.jar", - "framework.jar", + "libmedia.so", + "libpowermanager.so", }, } case "darwin": @@ -538,7 +538,7 @@ func TestReaddirStatFailures(t *testing.T) { return s } - if got, want := names(mustReadDir("inital readdir")), + if got, want := names(mustReadDir("initial readdir")), []string{"good1", "good2", "x"}; !reflect.DeepEqual(got, want) { t.Errorf("initial readdir got %q; want %q", got, want) } diff --git a/src/os/str.go b/src/os/str.go index d3e03e9849..cba9fa3e8d 100644 --- a/src/os/str.go +++ b/src/os/str.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. -// Simple converions to avoid depending on strconv. +// Simple conversions to avoid depending on strconv. package os diff --git a/src/os/user/lookup_windows.go b/src/os/user/lookup_windows.go index 99c325ff01..9fb3c5546f 100644 --- a/src/os/user/lookup_windows.go +++ b/src/os/user/lookup_windows.go @@ -61,7 +61,7 @@ func lookupFullName(domain, username, domainAndUser string) (string, error) { if err == nil { return name, nil } - // domain worked neigher as a domain nor as a server + // domain worked neither as a domain nor as a server // could be domain server unavailable // pretend username is fullname return username, nil diff --git a/src/path/filepath/path_windows.go b/src/path/filepath/path_windows.go index ef6e7ca93f..41c57df738 100644 --- a/src/path/filepath/path_windows.go +++ b/src/path/filepath/path_windows.go @@ -121,7 +121,7 @@ func join(elem []string) string { // joinNonEmpty is like join, but it assumes that the first element is non-empty. func joinNonEmpty(elem []string) string { if len(elem[0]) == 2 && elem[0][1] == ':' { - // First element is drive leter without terminating slash. + // First element is drive letter without terminating slash. // Keep path relative to current directory on that drive. return Clean(elem[0] + strings.Join(elem[1:], string(Separator))) } diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index c80df34077..2eb4f343f9 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -4465,7 +4465,7 @@ func TestFieldByIndexNil(t *testing.T) { // off the stack into the frame will store an *Inner there, and then if a garbage collection // happens to scan that argument frame before it is discarded, it will scan the *Inner // memory as if it were an *Outer. If the two have different memory layouts, the -// collection will intepret the memory incorrectly. +// collection will interpret the memory incorrectly. // // One such possible incorrect interpretation is to treat two arbitrary memory words // (Inner.P1 and Inner.P2 below) as an interface (Outer.R below). Because interpreting @@ -5007,3 +5007,24 @@ func TestChanAlloc(t *testing.T) { // a limitation of escape analysis. If that is ever fixed the // allocs < 0.5 condition will trigger and this test should be fixed. } + +type nameTest struct { + v interface{} + want string +} + +var nameTests = []nameTest{ + {int32(0), "int32"}, + {D1{}, "D1"}, + {[]D1{}, ""}, + {(chan D1)(nil), ""}, + {(func() D1)(nil), ""}, +} + +func TestNames(t *testing.T) { + for _, test := range nameTests { + if got := TypeOf(test.v).Name(); got != test.want { + t.Errorf("%T Name()=%q, want %q", test.v, got, test.want) + } + } +} diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go index 26a648e193..a6b0fda737 100644 --- a/src/reflect/export_test.go +++ b/src/reflect/export_test.go @@ -48,7 +48,7 @@ func TypeLinks() []string { var r []string for _, m := range typelinks() { for _, t := range m { - r = append(r, *t.string) + r = append(r, t.string) } } return r diff --git a/src/reflect/type.go b/src/reflect/type.go index 003c610cb1..1367ba34e7 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -254,9 +254,8 @@ type rtype struct { kind uint8 // enumeration for C alg *typeAlg // algorithm table gcdata *byte // garbage collection data - string *string // string form; unnecessary but undeniably useful + string string // string form; unnecessary but undeniably useful *uncommonType // (relatively) uncommon fields - ptrToThis *rtype // type for pointer to this type, if used in binary or has methods } // a copy of runtime.typeAlg @@ -284,7 +283,6 @@ type method struct { // Using a pointer to this struct reduces the overall size required // to describe an unnamed type with no methods. type uncommonType struct { - name *string // name of type pkgPath *string // import path; nil for built-in types like int, string methods []method // methods associated with type } @@ -453,14 +451,7 @@ func (t *uncommonType) PkgPath() string { return *t.pkgPath } -func (t *uncommonType) Name() string { - if t == nil || t.name == nil { - return "" - } - return *t.name -} - -func (t *rtype) String() string { return *t.string } +func (t *rtype) String() string { return t.string } func (t *rtype) Size() uintptr { return t.size } @@ -558,8 +549,34 @@ func (t *rtype) PkgPath() string { return t.uncommonType.PkgPath() } +func hasPrefix(s, prefix string) bool { + return len(s) >= len(prefix) && s[:len(prefix)] == prefix +} + func (t *rtype) Name() string { - return t.uncommonType.Name() + if hasPrefix(t.string, "map[") { + return "" + } + if hasPrefix(t.string, "struct {") { + return "" + } + if hasPrefix(t.string, "chan ") { + return "" + } + if hasPrefix(t.string, "func(") { + return "" + } + if t.string[0] == '[' || t.string[0] == '*' { + return "" + } + i := len(t.string) - 1 + for i >= 0 { + if t.string[i] == '.' { + break + } + i-- + } + return t.string[i+1:] } func (t *rtype) ChanDir() ChanDir { @@ -1030,15 +1047,7 @@ func PtrTo(t Type) Type { } func (t *rtype) ptrTo() *rtype { - if p := t.ptrToThis; p != nil { - return p - } - - // Otherwise, synthesize one. - // This only happens for pointers with no methods. - // We keep the mapping in a map on the side, because - // this operation is rare and a separate map lets us keep - // the type structures in read-only memory. + // Check the cache. ptrMap.RLock() if m := ptrMap.m; m != nil { if p := m[t]; p != nil { @@ -1047,6 +1056,7 @@ func (t *rtype) ptrTo() *rtype { } } ptrMap.RUnlock() + ptrMap.Lock() if ptrMap.m == nil { ptrMap.m = make(map[*rtype]*ptrType) @@ -1059,7 +1069,7 @@ func (t *rtype) ptrTo() *rtype { } // Look in known types. - s := "*" + *t.string + s := "*" + t.string for _, tt := range typesByString(s) { p = (*ptrType)(unsafe.Pointer(tt)) if p.elem == t { @@ -1076,7 +1086,7 @@ func (t *rtype) ptrTo() *rtype { prototype := *(**ptrType)(unsafe.Pointer(&iptr)) *p = *prototype - p.string = &s + p.string = s // For the type structures linked into the binary, the // compiler provides a good hash of the string. @@ -1086,7 +1096,6 @@ func (t *rtype) ptrTo() *rtype { p.hash = fnv1(t.hash, '*') p.uncommonType = nil - p.ptrToThis = nil p.elem = t ptrMap.m[t] = p @@ -1310,7 +1319,7 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool { // Note that strings are not unique identifiers for types: // there can be more than one with a given string. // Only types we might want to look up are included: -// channels, maps, slices, and arrays. +// pointers, channels, maps, slices, and arrays. func typelinks() [][]*rtype // typesByString returns the subslice of typelinks() whose elements have @@ -1328,7 +1337,7 @@ func typesByString(s string) []*rtype { for i < j { h := i + (j-i)/2 // avoid overflow when computing h // i ≤ h < j - if !(*typ[h].string >= s) { + if !(typ[h].string >= s) { i = h + 1 // preserves f(i-1) == false } else { j = h // preserves f(j) == true @@ -1340,7 +1349,7 @@ func typesByString(s string) []*rtype { // We could do a second binary search, but the caller is going // to do a linear scan anyway. j = i - for j < len(typ) && *typ[j].string == s { + for j < len(typ) && typ[j].string == s { j++ } @@ -1442,11 +1451,11 @@ func ChanOf(dir ChanDir, t Type) Type { lookupCache.Unlock() panic("reflect.ChanOf: invalid dir") case SendDir: - s = "chan<- " + *typ.string + s = "chan<- " + typ.string case RecvDir: - s = "<-chan " + *typ.string + s = "<-chan " + typ.string case BothDir: - s = "chan " + *typ.string + s = "chan " + typ.string } for _, tt := range typesByString(s) { ch := (*chanType)(unsafe.Pointer(tt)) @@ -1461,11 +1470,10 @@ func ChanOf(dir ChanDir, t Type) Type { ch := new(chanType) *ch = *prototype ch.dir = uintptr(dir) - ch.string = &s + ch.string = s ch.hash = fnv1(typ.hash, 'c', byte(dir)) ch.elem = typ ch.uncommonType = nil - ch.ptrToThis = nil return cachePut(ckey, &ch.rtype) } @@ -1493,7 +1501,7 @@ func MapOf(key, elem Type) Type { } // Look in known types. - s := "map[" + *ktyp.string + "]" + *etyp.string + s := "map[" + ktyp.string + "]" + etyp.string for _, tt := range typesByString(s) { mt := (*mapType)(unsafe.Pointer(tt)) if mt.key == ktyp && mt.elem == etyp { @@ -1505,7 +1513,7 @@ func MapOf(key, elem Type) Type { var imap interface{} = (map[unsafe.Pointer]unsafe.Pointer)(nil) mt := new(mapType) *mt = **(**mapType)(unsafe.Pointer(&imap)) - mt.string = &s + mt.string = s mt.hash = fnv1(etyp.hash, 'm', byte(ktyp.hash>>24), byte(ktyp.hash>>16), byte(ktyp.hash>>8), byte(ktyp.hash)) mt.key = ktyp mt.elem = etyp @@ -1528,7 +1536,6 @@ func MapOf(key, elem Type) Type { mt.reflexivekey = isReflexive(ktyp) mt.needkeyupdate = needKeyUpdate(ktyp) mt.uncommonType = nil - mt.ptrToThis = nil return cachePut(ckey, &mt.rtype) } @@ -1605,9 +1612,8 @@ func FuncOf(in, out []Type, variadic bool) Type { } // Populate the remaining fields of ft and store in cache. - ft.string = &str + ft.string = str ft.uncommonType = nil - ft.ptrToThis = nil funcLookupCache.m[hash] = append(funcLookupCache.m[hash], &ft.rtype) return &ft.rtype @@ -1623,9 +1629,9 @@ func funcStr(ft *funcType) string { } if ft.dotdotdot && i == len(ft.in)-1 { repr = append(repr, "..."...) - repr = append(repr, *(*sliceType)(unsafe.Pointer(t)).elem.string...) + repr = append(repr, (*sliceType)(unsafe.Pointer(t)).elem.string...) } else { - repr = append(repr, *t.string...) + repr = append(repr, t.string...) } } repr = append(repr, ')') @@ -1638,7 +1644,7 @@ func funcStr(ft *funcType) string { if i > 0 { repr = append(repr, ", "...) } - repr = append(repr, *t.string...) + repr = append(repr, t.string...) } if len(ft.out) > 1 { repr = append(repr, ')') @@ -1803,8 +1809,8 @@ func bucketOf(ktyp, etyp *rtype) *rtype { b.ptrdata = ptrdata b.kind = kind b.gcdata = gcdata - s := "bucket(" + *ktyp.string + "," + *etyp.string + ")" - b.string = &s + s := "bucket(" + ktyp.string + "," + etyp.string + ")" + b.string = s return b } @@ -1820,7 +1826,7 @@ func SliceOf(t Type) Type { } // Look in known types. - s := "[]" + *typ.string + s := "[]" + typ.string for _, tt := range typesByString(s) { slice := (*sliceType)(unsafe.Pointer(tt)) if slice.elem == typ { @@ -1833,11 +1839,10 @@ func SliceOf(t Type) Type { prototype := *(**sliceType)(unsafe.Pointer(&islice)) slice := new(sliceType) *slice = *prototype - slice.string = &s + slice.string = s slice.hash = fnv1(typ.hash, '[') slice.elem = typ slice.uncommonType = nil - slice.ptrToThis = nil return cachePut(ckey, &slice.rtype) } @@ -1864,7 +1869,7 @@ func ArrayOf(count int, elem Type) Type { } // Look in known types. - s := "[" + strconv.Itoa(count) + "]" + *typ.string + s := "[" + strconv.Itoa(count) + "]" + typ.string for _, tt := range typesByString(s) { array := (*arrayType)(unsafe.Pointer(tt)) if array.elem == typ { @@ -1877,7 +1882,7 @@ func ArrayOf(count int, elem Type) Type { prototype := *(**arrayType)(unsafe.Pointer(&iarray)) array := new(arrayType) *array = *prototype - array.string = &s + array.string = s array.hash = fnv1(typ.hash, '[') for n := uint32(count); n > 0; n >>= 8 { array.hash = fnv1(array.hash, byte(n)) @@ -1895,7 +1900,6 @@ func ArrayOf(count int, elem Type) Type { array.align = typ.align array.fieldAlign = typ.fieldAlign array.uncommonType = nil - array.ptrToThis = nil array.len = uintptr(count) array.slice = slice.(*rtype) @@ -2133,11 +2137,11 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin var s string if rcvr != nil { - s = "methodargs(" + *rcvr.string + ")(" + *t.string + ")" + s = "methodargs(" + rcvr.string + ")(" + t.string + ")" } else { - s = "funcargs(" + *t.string + ")" + s = "funcargs(" + t.string + ")" } - x.string = &s + x.string = s // cache result for future callers if layoutCache.m == nil { diff --git a/src/regexp/exec_test.go b/src/regexp/exec_test.go index 4872cb3def..0f95b95972 100644 --- a/src/regexp/exec_test.go +++ b/src/regexp/exec_test.go @@ -409,7 +409,7 @@ Reading: // h REG_MULTIREF multiple digit backref // i REG_ICASE ignore case // j REG_SPAN . matches \n - // k REG_ESCAPE \ to ecape [...] delimiter + // k REG_ESCAPE \ to escape [...] delimiter // l REG_LEFT implicit ^... // m REG_MINIMAL minimal match // n REG_NEWLINE explicit \n match diff --git a/src/runtime/alg.go b/src/runtime/alg.go index 9e19119f4a..e507e71715 100644 --- a/src/runtime/alg.go +++ b/src/runtime/alg.go @@ -146,7 +146,7 @@ func interhash(p unsafe.Pointer, h uintptr) uintptr { t := tab._type fn := t.alg.hash if fn == nil { - panic(errorString("hash of unhashable type " + *t._string)) + panic(errorString("hash of unhashable type " + t._string)) } if isDirectIface(t) { return c1 * fn(unsafe.Pointer(&a.data), h^c0) @@ -163,7 +163,7 @@ func nilinterhash(p unsafe.Pointer, h uintptr) uintptr { } fn := t.alg.hash if fn == nil { - panic(errorString("hash of unhashable type " + *t._string)) + panic(errorString("hash of unhashable type " + t._string)) } if isDirectIface(t) { return c1 * fn(unsafe.Pointer(&a.data), h^c0) @@ -221,7 +221,7 @@ func efaceeq(x, y eface) bool { } eq := t.alg.equal if eq == nil { - panic(errorString("comparing uncomparable type " + *t._string)) + panic(errorString("comparing uncomparable type " + t._string)) } if isDirectIface(t) { return eq(noescape(unsafe.Pointer(&x.data)), noescape(unsafe.Pointer(&y.data))) @@ -239,7 +239,7 @@ func ifaceeq(x, y iface) bool { t := xtab._type eq := t.alg.equal if eq == nil { - panic(errorString("comparing uncomparable type " + *t._string)) + panic(errorString("comparing uncomparable type " + t._string)) } if isDirectIface(t) { return eq(noescape(unsafe.Pointer(&x.data)), noescape(unsafe.Pointer(&y.data))) diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index 98a8e839ed..ac4630c833 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -1838,80 +1838,98 @@ TEXT strings·IndexByte(SB),NOSPLIT,$0-32 // AL: byte sought // R8: address to put result TEXT runtime·indexbytebody(SB),NOSPLIT,$0 - MOVQ SI, DI - - CMPQ BX, $16 - JLT small - - CMPQ BX, $32 - JA avx2 -no_avx2: - // round up to first 16-byte boundary - TESTQ $15, SI - JZ aligned - MOVQ SI, CX - ANDQ $~15, CX - ADDQ $16, CX - - // search the beginning - SUBQ SI, CX - REPN; SCASB - JZ success - -// DI is 16-byte aligned; get ready to search using SSE instructions -aligned: - // round down to last 16-byte boundary - MOVQ BX, R11 - ADDQ SI, R11 - ANDQ $~15, R11 - - // shuffle X0 around so that each byte contains c + // Shuffle X0 around so that each byte contains + // the character we're looking for. MOVD AX, X0 PUNPCKLBW X0, X0 PUNPCKLBW X0, X0 PSHUFL $0, X0, X0 - JMP condition + + CMPQ BX, $16 + JLT small + MOVQ SI, DI + + CMPQ BX, $32 + JA avx2 sse: - // move the next 16-byte chunk of the buffer into X1 - MOVO (DI), X1 - // compare bytes in X0 to X1 - PCMPEQB X0, X1 - // take the top bit of each byte in X1 and put the result in DX + LEAQ -16(SI)(BX*1), AX // AX = address of last 16 bytes + JMP sseloopentry + +sseloop: + // Move the next 16-byte chunk of the data into X1. + MOVOU (DI), X1 + // Compare bytes in X0 to X1. + PCMPEQB X0, X1 + // Take the top bit of each byte in X1 and put the result in DX. PMOVMSKB X1, DX - TESTL DX, DX - JNZ ssesuccess - ADDQ $16, DI + // Find first set bit, if any. + BSFL DX, DX + JNZ ssesuccess + // Advance to next block. + ADDQ $16, DI +sseloopentry: + CMPQ DI, AX + JB sseloop -condition: - CMPQ DI, R11 - JLT sse - - // search the end - MOVQ SI, CX - ADDQ BX, CX - SUBQ R11, CX - // if CX == 0, the zero flag will be set and we'll end up - // returning a false success - JZ failure - REPN; SCASB - JZ success + // Search the last 16-byte chunk. This chunk may overlap with the + // chunks we've already searched, but that's ok. + MOVQ AX, DI + MOVOU (AX), X1 + PCMPEQB X0, X1 + PMOVMSKB X1, DX + BSFL DX, DX + JNZ ssesuccess failure: MOVQ $-1, (R8) RET +// We've found a chunk containing the byte. +// The chunk was loaded from DI. +// The index of the matching byte in the chunk is DX. +// The start of the data is SI. +ssesuccess: + SUBQ SI, DI // Compute offset of chunk within data. + ADDQ DX, DI // Add offset of byte within chunk. + MOVQ DI, (R8) + RET + // handle for lengths < 16 small: - MOVQ BX, CX - REPN; SCASB - JZ success - MOVQ $-1, (R8) + TESTQ BX, BX + JEQ failure + + // Check if we'll load across a page boundary. + LEAQ 16(SI), AX + TESTW $0xff0, AX + JEQ endofpage + + MOVOU (SI), X1 // Load data + PCMPEQB X0, X1 // Compare target byte with each byte in data. + PMOVMSKB X1, DX // Move result bits to integer register. + BSFL DX, DX // Find first set bit. + JZ failure // No set bit, failure. + CMPL DX, BX + JAE failure // Match is past end of data. + MOVQ DX, (R8) + RET + +endofpage: + MOVOU -16(SI)(BX*1), X1 // Load data into the high end of X1. + PCMPEQB X0, X1 // Compare target byte with each byte in data. + PMOVMSKB X1, DX // Move result bits to integer register. + MOVL BX, CX + SHLL CX, DX + SHRL $16, DX // Shift desired bits down to bottom of register. + BSFL DX, DX // Find first set bit. + JZ failure // No set bit, failure. + MOVQ DX, (R8) RET avx2: CMPB runtime·support_avx2(SB), $1 - JNE no_avx2 + JNE sse MOVD AX, X0 LEAQ -32(SI)(BX*1), R11 VPBROADCASTB X0, Y1 @@ -1941,22 +1959,6 @@ avx2success: VZEROUPPER RET -// we've found the chunk containing the byte -// now just figure out which specific byte it is -ssesuccess: - // get the index of the least significant set bit - BSFW DX, DX - SUBQ SI, DI - ADDQ DI, DX - MOVQ DX, (R8) - RET - -success: - SUBQ SI, DI - SUBL $1, DI - MOVQ DI, (R8) - RET - TEXT bytes·Equal(SB),NOSPLIT,$0-49 MOVQ a_len+8(FP), BX MOVQ b_len+32(FP), CX diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s index 5d0206d1c9..2fdfbea0e1 100644 --- a/src/runtime/asm_arm.s +++ b/src/runtime/asm_arm.s @@ -706,7 +706,7 @@ TEXT runtime·abort(SB),NOSPLIT,$-4-0 // armPublicationBarrier is a native store/store barrier for ARMv7+. // On earlier ARM revisions, armPublicationBarrier is a no-op. // This will not work on SMP ARMv6 machines, if any are in use. -// To implement publiationBarrier in sys_$GOOS_arm.s using the native +// To implement publicationBarrier in sys_$GOOS_arm.s using the native // instructions, use: // // TEXT ·publicationBarrier(SB),NOSPLIT,$-4-0 diff --git a/src/runtime/callers_test.go b/src/runtime/callers_test.go new file mode 100644 index 0000000000..cb3e6e87c7 --- /dev/null +++ b/src/runtime/callers_test.go @@ -0,0 +1,83 @@ +// 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 runtime_test + +import ( + "runtime" + "strings" + "testing" +) + +func f1(pan bool) []uintptr { + return f2(pan) // line 14 +} + +func f2(pan bool) []uintptr { + return f3(pan) // line 18 +} + +func f3(pan bool) []uintptr { + if pan { + panic("f3") // line 23 + } + ret := make([]uintptr, 20) + return ret[:runtime.Callers(0, ret)] // line 26 +} + +func testCallers(t *testing.T, pcs []uintptr, pan bool) { + m := make(map[string]int, len(pcs)) + frames := runtime.CallersFrames(pcs) + for { + frame, more := frames.Next() + if frame.Function != "" { + m[frame.Function] = frame.Line + } + if !more { + break + } + } + + var seen []string + for k := range m { + seen = append(seen, k) + } + t.Logf("functions seen: %s", strings.Join(seen, " ")) + + var f3Line int + if pan { + f3Line = 23 + } else { + f3Line = 26 + } + want := []struct { + name string + line int + }{ + {"f1", 14}, + {"f2", 18}, + {"f3", f3Line}, + } + for _, w := range want { + if got := m["runtime_test."+w.name]; got != w.line { + t.Errorf("%s is line %d, want %d", w.name, got, w.line) + } + } +} + +func TestCallers(t *testing.T) { + testCallers(t, f1(false), false) +} + +func TestCallersPanic(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Fatal("did not panic") + } + pcs := make([]uintptr, 20) + pcs = pcs[:runtime.Callers(0, pcs)] + testCallers(t, pcs, true) + }() + f1(true) +} diff --git a/src/runtime/cgo/callbacks.go b/src/runtime/cgo/callbacks.go index 08f230d47e..14ac9da0ac 100644 --- a/src/runtime/cgo/callbacks.go +++ b/src/runtime/cgo/callbacks.go @@ -78,7 +78,7 @@ var _cgo_thread_start = &x_cgo_thread_start var x_cgo_sys_thread_create byte var _cgo_sys_thread_create = &x_cgo_sys_thread_create -// Notifies that the runtime has been intialized. +// Notifies that the runtime has been initialized. // // We currently block at every CGO entry point (via _cgo_wait_runtime_init_done) // to ensure that the runtime has been initialized before the CGO call is diff --git a/src/runtime/chan.go b/src/runtime/chan.go index 063c5ce391..f6f3ce4d90 100644 --- a/src/runtime/chan.go +++ b/src/runtime/chan.go @@ -203,6 +203,8 @@ func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uin if t0 != 0 { mysg.releasetime = -1 } + // No stack splits between assigning elem and enqueuing mysg + // on gp.waiting where copystack can find it. mysg.elem = ep mysg.waitlink = nil mysg.g = gp @@ -460,6 +462,8 @@ func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, r if t0 != 0 { mysg.releasetime = -1 } + // No stack splits between assigning elem and enqueuing mysg + // on gp.waiting where copystack can find it. mysg.elem = ep mysg.waitlink = nil gp.waiting = mysg diff --git a/src/runtime/chan_test.go b/src/runtime/chan_test.go index 497e87f43d..2cdfae866c 100644 --- a/src/runtime/chan_test.go +++ b/src/runtime/chan_test.go @@ -573,7 +573,7 @@ func TestSelectDuplicateChannel(t *testing.T) { } e <- 9 }() - time.Sleep(time.Millisecond) // make sure goroutine A gets qeueued first on c + time.Sleep(time.Millisecond) // make sure goroutine A gets queued first on c // goroutine B go func() { diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go index 63769e801c..00686ee755 100644 --- a/src/runtime/crash_cgo_test.go +++ b/src/runtime/crash_cgo_test.go @@ -7,6 +7,7 @@ package runtime_test import ( + "fmt" "internal/testenv" "os/exec" "runtime" @@ -161,22 +162,35 @@ func TestCgoCheckBytes(t *testing.T) { t.Fatal(err) } - cmd := testEnv(exec.Command(exe, "CgoCheckBytes")) + // Try it 10 times to avoid flakiness. + const tries = 10 + var tot1, tot2 time.Duration + for i := 0; i < tries; i++ { + cmd := testEnv(exec.Command(exe, "CgoCheckBytes")) + cmd.Env = append(cmd.Env, "GODEBUG=cgocheck=0", fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i)) - start := time.Now() - cmd.Run() - d1 := time.Since(start) + start := time.Now() + cmd.Run() + d1 := time.Since(start) - cmd = testEnv(exec.Command(exe, "CgoCheckBytes")) - cmd.Env = append(cmd.Env, "GODEBUG=cgocheck=0") + cmd = testEnv(exec.Command(exe, "CgoCheckBytes")) + cmd.Env = append(cmd.Env, fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i)) - start = time.Now() - cmd.Run() - d2 := time.Since(start) + start = time.Now() + cmd.Run() + d2 := time.Since(start) - if d2*10 < d1 { - t.Errorf("cgo check too slow: got %v, expected at most %v", d1, d2*10) + if d1*20 > d2 { + // The slow version (d2) was less than 20 times + // slower than the fast version (d1), so OK. + return + } + + tot1 += d1 + tot2 += d2 } + + t.Errorf("cgo check too slow: got %v, expected at most %v", tot2/tries, (tot1/tries)*20) } func TestCgoPanicDeadlock(t *testing.T) { diff --git a/src/runtime/error.go b/src/runtime/error.go index de07bcb643..3e1ec4bc5a 100644 --- a/src/runtime/error.go +++ b/src/runtime/error.go @@ -56,7 +56,7 @@ type stringer interface { func typestring(x interface{}) string { e := efaceOf(&x) - return *e._type._string + return e._type._string } // For calling from C. diff --git a/src/runtime/extern.go b/src/runtime/extern.go index 2c98482e26..9c1f9f5a03 100644 --- a/src/runtime/extern.go +++ b/src/runtime/extern.go @@ -191,12 +191,9 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool) { // // Note that since each slice entry pc[i] is a return program counter, // looking up the file and line for pc[i] (for example, using (*Func).FileLine) -// will return the file and line number of the instruction immediately +// will normally return the file and line number of the instruction immediately // following the call. -// To look up the file and line number of the call itself, use pc[i]-1. -// As an exception to this rule, if pc[i-1] corresponds to the function -// runtime.sigpanic, then pc[i] is the program counter of a faulting -// instruction and should be used without any subtraction. +// To easily look up file/line information for the call sequence, use Frames. func Callers(skip int, pc []uintptr) int { // runtime.callers uses pc.array==nil as a signal // to print a stack trace. Pick off 0-length pc here diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go index 4d1da1c1df..3bff36bd78 100644 --- a/src/runtime/heapdump.go +++ b/src/runtime/heapdump.go @@ -183,11 +183,12 @@ func dumptype(t *_type) { dumpint(tagType) dumpint(uint64(uintptr(unsafe.Pointer(t)))) dumpint(uint64(t.size)) - if t.x == nil || t.x.pkgpath == nil || t.x.name == nil { - dumpstr(*t._string) + if t.x == nil || t.x.pkgpath == nil { + dumpstr(t._string) } else { pkgpath := stringStructOf(t.x.pkgpath) - name := stringStructOf(t.x.name) + namestr := t.name() + name := stringStructOf(&namestr) dumpint(uint64(uintptr(pkgpath.len) + 1 + uintptr(name.len))) dwrite(pkgpath.str, uintptr(pkgpath.len)) dwritebyte('.') @@ -502,28 +503,10 @@ func dumpparams() { func itab_callback(tab *itab) { t := tab._type - // Dump a map from itab* to the type of its data field. - // We want this map so we can deduce types of interface referents. - if t.kind&kindDirectIface == 0 { - // indirect - data slot is a pointer to t. - dumptype(t.ptrto) - dumpint(tagItab) - dumpint(uint64(uintptr(unsafe.Pointer(tab)))) - dumpint(uint64(uintptr(unsafe.Pointer(t.ptrto)))) - } else if t.kind&kindNoPointers == 0 { - // t is pointer-like - data slot is a t. - dumptype(t) - dumpint(tagItab) - dumpint(uint64(uintptr(unsafe.Pointer(tab)))) - dumpint(uint64(uintptr(unsafe.Pointer(t)))) - } else { - // Data slot is a scalar. Dump type just for fun. - // With pointer-only interfaces, this shouldn't happen. - dumptype(t) - dumpint(tagItab) - dumpint(uint64(uintptr(unsafe.Pointer(tab)))) - dumpint(uint64(uintptr(unsafe.Pointer(t)))) - } + dumptype(t) + dumpint(tagItab) + dumpint(uint64(uintptr(unsafe.Pointer(tab)))) + dumpint(uint64(uintptr(unsafe.Pointer(t)))) } func dumpitabs() { @@ -639,7 +622,7 @@ func dumpmemprof() { } } -var dumphdr = []byte("go1.6 heap dump\n") +var dumphdr = []byte("go1.7 heap dump\n") func mdump() { // make sure we're done sweeping diff --git a/src/runtime/iface.go b/src/runtime/iface.go index 50dff77e42..d980367866 100644 --- a/src/runtime/iface.go +++ b/src/runtime/iface.go @@ -30,7 +30,7 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab { if canfail { return nil } - panic(&TypeAssertionError{"", *typ._string, *inter.typ._string, *inter.mhdr[0].name}) + panic(&TypeAssertionError{"", typ._string, inter.typ._string, *inter.mhdr[0].name}) } // compiler has provided some good hash codes for us. @@ -101,7 +101,7 @@ search: if locked != 0 { unlock(&ifaceLock) } - panic(&TypeAssertionError{"", *typ._string, *inter.typ._string, *iname}) + panic(&TypeAssertionError{"", typ._string, inter.typ._string, *iname}) } m.bad = 1 break @@ -177,18 +177,18 @@ func convT2I(t *_type, inter *interfacetype, cache **itab, elem unsafe.Pointer, func panicdottype(have, want, iface *_type) { haveString := "" if have != nil { - haveString = *have._string + haveString = have._string } - panic(&TypeAssertionError{*iface._string, haveString, *want._string, ""}) + panic(&TypeAssertionError{iface._string, haveString, want._string, ""}) } func assertI2T(t *_type, i iface, r unsafe.Pointer) { tab := i.tab if tab == nil { - panic(&TypeAssertionError{"", "", *t._string, ""}) + panic(&TypeAssertionError{"", "", t._string, ""}) } if tab._type != t { - panic(&TypeAssertionError{*tab.inter.typ._string, *tab._type._string, *t._string, ""}) + panic(&TypeAssertionError{tab.inter.typ._string, tab._type._string, t._string, ""}) } if r != nil { if isDirectIface(t) { @@ -219,10 +219,10 @@ func assertI2T2(t *_type, i iface, r unsafe.Pointer) bool { func assertE2T(t *_type, e eface, r unsafe.Pointer) { if e._type == nil { - panic(&TypeAssertionError{"", "", *t._string, ""}) + panic(&TypeAssertionError{"", "", t._string, ""}) } if e._type != t { - panic(&TypeAssertionError{"", *e._type._string, *t._string, ""}) + panic(&TypeAssertionError{"", e._type._string, t._string, ""}) } if r != nil { if isDirectIface(t) { @@ -266,7 +266,7 @@ func assertI2E(inter *interfacetype, i iface, r *eface) { tab := i.tab if tab == nil { // explicit conversions require non-nil interface value. - panic(&TypeAssertionError{"", "", *inter.typ._string, ""}) + panic(&TypeAssertionError{"", "", inter.typ._string, ""}) } r._type = tab._type r.data = i.data @@ -303,7 +303,7 @@ func assertI2I(inter *interfacetype, i iface, r *iface) { tab := i.tab if tab == nil { // explicit conversions require non-nil interface value. - panic(&TypeAssertionError{"", "", *inter.typ._string, ""}) + panic(&TypeAssertionError{"", "", inter.typ._string, ""}) } if tab.inter == inter { r.tab = tab @@ -342,7 +342,7 @@ func assertE2I(inter *interfacetype, e eface, r *iface) { t := e._type if t == nil { // explicit conversions require non-nil interface value. - panic(&TypeAssertionError{"", "", *inter.typ._string, ""}) + panic(&TypeAssertionError{"", "", inter.typ._string, ""}) } r.tab = getitab(inter, t, false) r.data = e.data @@ -383,7 +383,7 @@ func reflect_ifaceE2I(inter *interfacetype, e eface, dst *iface) { func assertE2E(inter *interfacetype, e eface, r *eface) { if e._type == nil { // explicit conversions require non-nil interface value. - panic(&TypeAssertionError{"", "", *inter.typ._string, ""}) + panic(&TypeAssertionError{"", "", inter.typ._string, ""}) } *r = e } diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index 336d4d8c81..154fc3e0f3 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -290,7 +290,9 @@ func (h heapBits) forward(n uintptr) heapBits { // The result includes in its higher bits the bits for subsequent words // described by the same bitmap byte. func (h heapBits) bits() uint32 { - return uint32(*h.bitp) >> h.shift + // The (shift & 31) eliminates a test and conditional branch + // from the generated code. + return uint32(*h.bitp) >> (h.shift & 31) } // isMarked reports whether the heap bits have the marked bit set. @@ -459,11 +461,11 @@ func typeBitsBulkBarrier(typ *_type, p, size uintptr) { throw("runtime: typeBitsBulkBarrier without type") } if typ.size != size { - println("runtime: typeBitsBulkBarrier with type ", *typ._string, " of size ", typ.size, " but memory size", size) + println("runtime: typeBitsBulkBarrier with type ", typ._string, " of size ", typ.size, " but memory size", size) throw("runtime: invalid typeBitsBulkBarrier") } if typ.kind&kindGCProg != 0 { - println("runtime: typeBitsBulkBarrier with type ", *typ._string, " with GC prog") + println("runtime: typeBitsBulkBarrier with type ", typ._string, " with GC prog") throw("runtime: invalid typeBitsBulkBarrier") } if !writeBarrier.needed { @@ -494,6 +496,10 @@ func typeBitsBulkBarrier(typ *_type, p, size uintptr) { // TODO(rsc): Perhaps introduce a different heapBitsSpan type. // initSpan initializes the heap bitmap for a span. +// It clears all mark and checkmark bits. +// If this is a span of pointer-sized objects, it initializes all +// words to pointer (and there are no dead bits). +// Otherwise, it initializes all words to scalar/dead. func (h heapBits) initSpan(size, n, total uintptr) { if total%heapBitmapScale != 0 { throw("initSpan: unaligned length") @@ -558,7 +564,7 @@ func (h heapBits) clearCheckmarkSpan(size, n, total uintptr) { // heapBitsSweepSpan coordinates the sweeping of a span by reading // and updating the corresponding heap bitmap entries. // For each free object in the span, heapBitsSweepSpan sets the type -// bits for the first two words (or one for single-word objects) to typeDead +// bits for the first four words (less for smaller objects) to scalar/dead // and then calls f(p), where p is the object's base address. // f is expected to add the object to a free list. // For non-free objects, heapBitsSweepSpan turns off the marked bit. @@ -910,7 +916,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { } if nw == 0 { // No pointers! Caller was supposed to check. - println("runtime: invalid type ", *typ._string) + println("runtime: invalid type ", typ._string) throw("heapBitsSetType: called with non-pointer type") return } @@ -1094,7 +1100,7 @@ Phase4: if doubleCheck { end := heapBitsForAddr(x + size) if typ.kind&kindGCProg == 0 && (hbitp != end.bitp || (w == nw+2) != (end.shift == 2)) { - println("ended at wrong bitmap byte for", *typ._string, "x", dataSize/typ.size) + println("ended at wrong bitmap byte for", typ._string, "x", dataSize/typ.size) print("typ.size=", typ.size, " typ.ptrdata=", typ.ptrdata, " dataSize=", dataSize, " size=", size, "\n") print("w=", w, " nw=", nw, " b=", hex(b), " nb=", nb, " hb=", hex(hb), "\n") h0 := heapBitsForAddr(x) @@ -1130,7 +1136,7 @@ Phase4: } } if have != want { - println("mismatch writing bits for", *typ._string, "x", dataSize/typ.size) + println("mismatch writing bits for", typ._string, "x", dataSize/typ.size) print("typ.size=", typ.size, " typ.ptrdata=", typ.ptrdata, " dataSize=", dataSize, " size=", size, "\n") print("kindGCProg=", typ.kind&kindGCProg != 0, "\n") print("w=", w, " nw=", nw, " b=", hex(b), " nb=", nb, " hb=", hex(hb), "\n") diff --git a/src/runtime/mcentral.go b/src/runtime/mcentral.go index 29a7b77376..8a2fbe98c9 100644 --- a/src/runtime/mcentral.go +++ b/src/runtime/mcentral.go @@ -195,7 +195,6 @@ func (c *mcentral) freeSpan(s *mspan, n int32, start gclinkptr, end gclinkptr, p s.needzero = 1 s.freelist = 0 unlock(&c.lock) - heapBitsForSpan(s.base()).initSpan(s.layout()) mheap_.freeSpan(s, 0) return true } diff --git a/src/runtime/memclr_amd64.s b/src/runtime/memclr_amd64.s index c257d59b30..6f30eca242 100644 --- a/src/runtime/memclr_amd64.s +++ b/src/runtime/memclr_amd64.s @@ -103,7 +103,7 @@ loop_avx2_huge: ADDQ $128, DI CMPQ BX, $128 JAE loop_avx2_huge - // In the desciption of MOVNTDQ in [1] + // In the description of MOVNTDQ in [1] // "... fencing operation implemented with the SFENCE or MFENCE instruction // should be used in conjunction with MOVNTDQ instructions..." // [1] 64-ia-32-architectures-software-developer-manual-325462.pdf diff --git a/src/runtime/memmove_ppc64x.s b/src/runtime/memmove_ppc64x.s index b6d0b85459..ea73b455b4 100644 --- a/src/runtime/memmove_ppc64x.s +++ b/src/runtime/memmove_ppc64x.s @@ -24,7 +24,7 @@ check: BC 12, 9, backward // I think you should be able to write this as "BGT CR2, backward" // Copying forward proceeds by copying R6 words then copying R7 bytes. - // R3 and R4 are advanced as we copy. Becuase PPC64 lacks post-increment + // R3 and R4 are advanced as we copy. Because PPC64 lacks post-increment // load/store, R3 and R4 point before the bytes that are to be copied. BC 12, 6, noforwardlarge // "BEQ CR1, noforwardlarge" diff --git a/src/runtime/mfinal.go b/src/runtime/mfinal.go index 512edeffe8..7d45862521 100644 --- a/src/runtime/mfinal.go +++ b/src/runtime/mfinal.go @@ -274,7 +274,7 @@ func SetFinalizer(obj interface{}, finalizer interface{}) { throw("runtime.SetFinalizer: first argument is nil") } if etyp.kind&kindMask != kindPtr { - throw("runtime.SetFinalizer: first argument is " + *etyp._string + ", not pointer") + throw("runtime.SetFinalizer: first argument is " + etyp._string + ", not pointer") } ot := (*ptrtype)(unsafe.Pointer(etyp)) if ot.elem == nil { @@ -328,11 +328,11 @@ func SetFinalizer(obj interface{}, finalizer interface{}) { } if ftyp.kind&kindMask != kindFunc { - throw("runtime.SetFinalizer: second argument is " + *ftyp._string + ", not a function") + throw("runtime.SetFinalizer: second argument is " + ftyp._string + ", not a function") } ft := (*functype)(unsafe.Pointer(ftyp)) if ft.dotdotdot || len(ft.in) != 1 { - throw("runtime.SetFinalizer: cannot pass " + *etyp._string + " to finalizer " + *ftyp._string) + throw("runtime.SetFinalizer: cannot pass " + etyp._string + " to finalizer " + ftyp._string) } fint := ft.in[0] switch { @@ -340,7 +340,7 @@ func SetFinalizer(obj interface{}, finalizer interface{}) { // ok - same type goto okarg case fint.kind&kindMask == kindPtr: - if (fint.x == nil || fint.x.name == nil || etyp.x == nil || etyp.x.name == nil) && (*ptrtype)(unsafe.Pointer(fint)).elem == ot.elem { + if (fint.x == nil || etyp.x == nil) && (*ptrtype)(unsafe.Pointer(fint)).elem == ot.elem { // ok - not same type, but both pointers, // one or the other is unnamed, and same element type, so assignable. goto okarg @@ -355,7 +355,7 @@ func SetFinalizer(obj interface{}, finalizer interface{}) { goto okarg } } - throw("runtime.SetFinalizer: cannot pass " + *etyp._string + " to finalizer " + *ftyp._string) + throw("runtime.SetFinalizer: cannot pass " + etyp._string + " to finalizer " + ftyp._string) okarg: // compute size needed for return parameters nret := uintptr(0) diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go index 102d44160e..138a623ca5 100644 --- a/src/runtime/mgc.go +++ b/src/runtime/mgc.go @@ -1556,8 +1556,8 @@ func gcMark(start_time int64) { gchelperstart() - var gcw gcWork - gcDrain(&gcw, gcDrainBlock) + gcw := &getg().m.p.ptr().gcw + gcDrain(gcw, gcDrainBlock) gcw.dispose() gcMarkRootCheck() @@ -1799,8 +1799,8 @@ func gchelper() { // Parallel mark over GC roots and heap if gcphase == _GCmarktermination { - var gcw gcWork - gcDrain(&gcw, gcDrainBlock) // blocks in getfull + gcw := &_g_.m.p.ptr().gcw + gcDrain(gcw, gcDrainBlock) // blocks in getfull gcw.dispose() } diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go index eac45ec168..0be908bfe8 100644 --- a/src/runtime/mgcmark.go +++ b/src/runtime/mgcmark.go @@ -98,10 +98,7 @@ var oneptrmask = [...]uint8{1} // Preemption must be disabled (because this uses a gcWork). // //go:nowritebarrier -func markroot(i uint32) { - // TODO: Consider using getg().m.p.ptr().gcw. - var gcw gcWork - +func markroot(gcw *gcWork, i uint32) { baseData := uint32(fixedRootCount) baseBSS := baseData + uint32(work.nDataRoots) baseSpans := baseBSS + uint32(work.nBSSRoots) @@ -111,17 +108,17 @@ func markroot(i uint32) { switch { case baseData <= i && i < baseBSS: for datap := &firstmoduledata; datap != nil; datap = datap.next { - markrootBlock(datap.data, datap.edata-datap.data, datap.gcdatamask.bytedata, &gcw, int(i-baseData)) + markrootBlock(datap.data, datap.edata-datap.data, datap.gcdatamask.bytedata, gcw, int(i-baseData)) } case baseBSS <= i && i < baseSpans: for datap := &firstmoduledata; datap != nil; datap = datap.next { - markrootBlock(datap.bss, datap.ebss-datap.bss, datap.gcbssmask.bytedata, &gcw, int(i-baseBSS)) + markrootBlock(datap.bss, datap.ebss-datap.bss, datap.gcbssmask.bytedata, gcw, int(i-baseBSS)) } case i == fixedRootFinalizers: for fb := allfin; fb != nil; fb = fb.alllink { - scanblock(uintptr(unsafe.Pointer(&fb.fin[0])), uintptr(fb.cnt)*unsafe.Sizeof(fb.fin[0]), &finptrmask[0], &gcw) + scanblock(uintptr(unsafe.Pointer(&fb.fin[0])), uintptr(fb.cnt)*unsafe.Sizeof(fb.fin[0]), &finptrmask[0], gcw) } case i == fixedRootFlushCaches: @@ -131,7 +128,7 @@ func markroot(i uint32) { case baseSpans <= i && i < baseStacks: // mark MSpan.specials - markrootSpans(&gcw, int(i-baseSpans)) + markrootSpans(gcw, int(i-baseSpans)) default: // the rest is scanning goroutine stacks @@ -193,8 +190,6 @@ func markroot(i uint32) { } }) } - - gcw.dispose() } // markrootBlock scans the shard'th shard of the block of memory [b0, @@ -808,8 +803,7 @@ func gcDrain(gcw *gcWork, flags gcDrainFlags) { if job >= work.markrootJobs { break } - // TODO: Pass in gcw. - markroot(job) + markroot(gcw, job) } } diff --git a/src/runtime/mgcsweep.go b/src/runtime/mgcsweep.go index b00ceb0a8a..d6d91d2021 100644 --- a/src/runtime/mgcsweep.go +++ b/src/runtime/mgcsweep.go @@ -276,7 +276,6 @@ func (s *mspan) sweep(preserve bool) bool { if preserve { throw("can't preserve large span") } - heapBitsForSpan(p).initSpan(s.layout()) s.needzero = 1 // Free the span after heapBitsSweepSpan diff --git a/src/runtime/mgcwork.go b/src/runtime/mgcwork.go index 0a0285d816..7bc4278195 100644 --- a/src/runtime/mgcwork.go +++ b/src/runtime/mgcwork.go @@ -11,8 +11,7 @@ import ( ) const ( - _Debugwbufs = false // if true check wbufs consistency - _WorkbufSize = 2048 // in bytes; larger values result in less contention + _WorkbufSize = 2048 // in bytes; larger values result in less contention ) // Garbage collector work pool abstraction. @@ -44,14 +43,6 @@ func (wp wbufptr) ptr() *workbuf { // // A gcWork can be used on the stack as follows: // -// var gcw gcWork -// disable preemption -// .. call gcw.put() to produce and gcw.get() to consume .. -// gcw.dispose() -// enable preemption -// -// Or from the per-P gcWork cache: -// // (preemption must be disabled) // gcw := &getg().m.p.ptr().gcw // .. call gcw.put() to produce and gcw.get() to consume .. @@ -94,10 +85,10 @@ type gcWork struct { } func (w *gcWork) init() { - w.wbuf1 = wbufptrOf(getempty(101)) - wbuf2 := trygetfull(102) + w.wbuf1 = wbufptrOf(getempty()) + wbuf2 := trygetfull() if wbuf2 == nil { - wbuf2 = getempty(103) + wbuf2 = getempty() } w.wbuf2 = wbufptrOf(wbuf2) } @@ -105,9 +96,7 @@ func (w *gcWork) init() { // put enqueues a pointer for the garbage collector to trace. // obj must point to the beginning of a heap object. //go:nowritebarrier -func (ww *gcWork) put(obj uintptr) { - w := (*gcWork)(noescape(unsafe.Pointer(ww))) // TODO: remove when escape analysis is fixed - +func (w *gcWork) put(obj uintptr) { wbuf := w.wbuf1.ptr() if wbuf == nil { w.init() @@ -117,8 +106,8 @@ func (ww *gcWork) put(obj uintptr) { w.wbuf1, w.wbuf2 = w.wbuf2, w.wbuf1 wbuf = w.wbuf1.ptr() if wbuf.nobj == len(wbuf.obj) { - putfull(wbuf, 132) - wbuf = getempty(133) + putfull(wbuf) + wbuf = getempty() w.wbuf1 = wbufptrOf(wbuf) } } @@ -133,9 +122,7 @@ func (ww *gcWork) put(obj uintptr) { // queue, tryGet returns 0. Note that there may still be pointers in // other gcWork instances or other caches. //go:nowritebarrier -func (ww *gcWork) tryGet() uintptr { - w := (*gcWork)(noescape(unsafe.Pointer(ww))) // TODO: remove when escape analysis is fixed - +func (w *gcWork) tryGet() uintptr { wbuf := w.wbuf1.ptr() if wbuf == nil { w.init() @@ -147,11 +134,11 @@ func (ww *gcWork) tryGet() uintptr { wbuf = w.wbuf1.ptr() if wbuf.nobj == 0 { owbuf := wbuf - wbuf = trygetfull(167) + wbuf = trygetfull() if wbuf == nil { return 0 } - putempty(owbuf, 166) + putempty(owbuf) w.wbuf1 = wbufptrOf(wbuf) } } @@ -164,9 +151,7 @@ func (ww *gcWork) tryGet() uintptr { // if necessary to ensure all pointers from all queues and caches have // been retrieved. get returns 0 if there are no pointers remaining. //go:nowritebarrier -func (ww *gcWork) get() uintptr { - w := (*gcWork)(noescape(unsafe.Pointer(ww))) // TODO: remove when escape analysis is fixed - +func (w *gcWork) get() uintptr { wbuf := w.wbuf1.ptr() if wbuf == nil { w.init() @@ -178,11 +163,11 @@ func (ww *gcWork) get() uintptr { wbuf = w.wbuf1.ptr() if wbuf.nobj == 0 { owbuf := wbuf - wbuf = getfull(185) + wbuf = getfull() if wbuf == nil { return 0 } - putempty(owbuf, 184) + putempty(owbuf) w.wbuf1 = wbufptrOf(wbuf) } } @@ -203,17 +188,17 @@ func (ww *gcWork) get() uintptr { func (w *gcWork) dispose() { if wbuf := w.wbuf1.ptr(); wbuf != nil { if wbuf.nobj == 0 { - putempty(wbuf, 212) + putempty(wbuf) } else { - putfull(wbuf, 214) + putfull(wbuf) } w.wbuf1 = 0 wbuf = w.wbuf2.ptr() if wbuf.nobj == 0 { - putempty(wbuf, 218) + putempty(wbuf) } else { - putfull(wbuf, 220) + putfull(wbuf) } w.wbuf2 = 0 } @@ -239,8 +224,8 @@ func (w *gcWork) balance() { return } if wbuf := w.wbuf2.ptr(); wbuf.nobj != 0 { - putfull(wbuf, 246) - w.wbuf2 = wbufptrOf(getempty(247)) + putfull(wbuf) + w.wbuf2 = wbufptrOf(getempty()) } else if wbuf := w.wbuf1.ptr(); wbuf.nobj > 4 { w.wbuf1 = wbufptrOf(handoff(wbuf)) } @@ -257,10 +242,8 @@ func (w *gcWork) empty() bool { // avoid contending on the global work buffer lists. type workbufhdr struct { - node lfnode // must be first - nobj int - inuse bool // This workbuf is in use by some gorotuine and is not on the work.empty/full queues. - log [4]int // line numbers forming a history of ownership changes to workbuf + node lfnode // must be first + nobj int } type workbuf struct { @@ -273,69 +256,23 @@ type workbuf struct { // workbufs. // If the GC asks for some work these are the only routines that // make wbufs available to the GC. -// Each of the gets and puts also take an distinct integer that is used -// to record a brief history of changes to ownership of the workbuf. -// The convention is to use a unique line number but any encoding -// is permissible. For example if you want to pass in 2 bits of information -// you could simple add lineno1*100000+lineno2. - -// logget records the past few values of entry to aid in debugging. -// logget checks the buffer b is not currently in use. -func (b *workbuf) logget(entry int) { - if !_Debugwbufs { - return - } - if b.inuse { - println("runtime: logget fails log entry=", entry, - "b.log[0]=", b.log[0], "b.log[1]=", b.log[1], - "b.log[2]=", b.log[2], "b.log[3]=", b.log[3]) - throw("logget: get not legal") - } - b.inuse = true - copy(b.log[1:], b.log[:]) - b.log[0] = entry -} - -// logput records the past few values of entry to aid in debugging. -// logput checks the buffer b is currently in use. -func (b *workbuf) logput(entry int) { - if !_Debugwbufs { - return - } - if !b.inuse { - println("runtime: logput fails log entry=", entry, - "b.log[0]=", b.log[0], "b.log[1]=", b.log[1], - "b.log[2]=", b.log[2], "b.log[3]=", b.log[3]) - throw("logput: put not legal") - } - b.inuse = false - copy(b.log[1:], b.log[:]) - b.log[0] = entry -} func (b *workbuf) checknonempty() { if b.nobj == 0 { - println("runtime: nonempty check fails", - "b.log[0]=", b.log[0], "b.log[1]=", b.log[1], - "b.log[2]=", b.log[2], "b.log[3]=", b.log[3]) throw("workbuf is empty") } } func (b *workbuf) checkempty() { if b.nobj != 0 { - println("runtime: empty check fails", - "b.log[0]=", b.log[0], "b.log[1]=", b.log[1], - "b.log[2]=", b.log[2], "b.log[3]=", b.log[3]) throw("workbuf is not empty") } } // getempty pops an empty work buffer off the work.empty list, // allocating new buffers if none are available. -// entry is used to record a brief history of ownership. //go:nowritebarrier -func getempty(entry int) *workbuf { +func getempty() *workbuf { var b *workbuf if work.empty != 0 { b = (*workbuf)(lfstackpop(&work.empty)) @@ -346,16 +283,14 @@ func getempty(entry int) *workbuf { if b == nil { b = (*workbuf)(persistentalloc(unsafe.Sizeof(*b), sys.CacheLineSize, &memstats.gc_sys)) } - b.logget(entry) return b } // putempty puts a workbuf onto the work.empty list. // Upon entry this go routine owns b. The lfstackpush relinquishes ownership. //go:nowritebarrier -func putempty(b *workbuf, entry int) { +func putempty(b *workbuf) { b.checkempty() - b.logput(entry) lfstackpush(&work.empty, &b.node) } @@ -363,9 +298,8 @@ func putempty(b *workbuf, entry int) { // putfull accepts partially full buffers so the GC can avoid competing // with the mutators for ownership of partially full buffers. //go:nowritebarrier -func putfull(b *workbuf, entry int) { +func putfull(b *workbuf) { b.checknonempty() - b.logput(entry) lfstackpush(&work.full, &b.node) // We just made more work available. Let the GC controller @@ -378,10 +312,9 @@ func putfull(b *workbuf, entry int) { // trygetfull tries to get a full or partially empty workbuffer. // If one is not immediately available return nil //go:nowritebarrier -func trygetfull(entry int) *workbuf { +func trygetfull() *workbuf { b := (*workbuf)(lfstackpop(&work.full)) if b != nil { - b.logget(entry) b.checknonempty() return b } @@ -400,10 +333,9 @@ func trygetfull(entry int) *workbuf { // This is in fact the termination condition for the STW mark // phase. //go:nowritebarrier -func getfull(entry int) *workbuf { +func getfull() *workbuf { b := (*workbuf)(lfstackpop(&work.full)) if b != nil { - b.logget(entry) b.checknonempty() return b } @@ -422,7 +354,6 @@ func getfull(entry int) *workbuf { } b = (*workbuf)(lfstackpop(&work.full)) if b != nil { - b.logget(entry) b.checknonempty() return b } @@ -452,7 +383,7 @@ func getfull(entry int) *workbuf { //go:nowritebarrier func handoff(b *workbuf) *workbuf { // Make new buffer with half of b's pointers. - b1 := getempty(915) + b1 := getempty() n := b.nobj / 2 b.nobj -= n b1.nobj = n @@ -462,6 +393,6 @@ func handoff(b *workbuf) *workbuf { _g_.m.gcstats.nhandoffcnt += uint64(n) // Put b on full list - let first half of b get stolen. - putfull(b, 942) + putfull(b) return b1 } diff --git a/src/runtime/mprof.go b/src/runtime/mprof.go index 3efa375d6a..d498a9328a 100644 --- a/src/runtime/mprof.go +++ b/src/runtime/mprof.go @@ -624,7 +624,7 @@ func tracealloc(p unsafe.Pointer, size uintptr, typ *_type) { if typ == nil { print("tracealloc(", p, ", ", hex(size), ")\n") } else { - print("tracealloc(", p, ", ", hex(size), ", ", *typ._string, ")\n") + print("tracealloc(", p, ", ", hex(size), ", ", typ._string, ")\n") } if gp.m.curg == nil || gp == gp.m.curg { goroutineheader(gp) diff --git a/src/runtime/netpoll_solaris.go b/src/runtime/netpoll_solaris.go index 86e9b997ef..53b2aacdb5 100644 --- a/src/runtime/netpoll_solaris.go +++ b/src/runtime/netpoll_solaris.go @@ -58,7 +58,7 @@ import "unsafe" // // The open and arming mechanisms are serialized using the lock // inside PollDesc. This is required because the netpoll loop runs -// asynchonously in respect to other Go code and by the time we get +// asynchronously in respect to other Go code and by the time we get // to call port_associate to update the association in the loop, the // file descriptor might have been closed and reopened already. The // lock allows runtime·netpollupdate to be called synchronously from @@ -125,7 +125,7 @@ func netpollopen(fd uintptr, pd *pollDesc) int32 { lock(&pd.lock) // We don't register for any specific type of events yet, that's // netpollarm's job. We merely ensure we call port_associate before - // asynchonous connect/accept completes, so when we actually want + // asynchronous connect/accept completes, so when we actually want // to do any I/O, the call to port_associate (from netpollarm, // with the interested event set) will unblock port_getn right away // because of the I/O readiness notification. diff --git a/src/runtime/os_linux_386.go b/src/runtime/os_linux_386.go index 3577a2406b..0f39cade3b 100644 --- a/src/runtime/os_linux_386.go +++ b/src/runtime/os_linux_386.go @@ -15,8 +15,6 @@ const ( _AT_SYSINFO = 32 ) -var _vdso uint32 - func sysargs(argc int32, argv **byte) { // skip over argv, envv to get to auxv n := argc + 1 @@ -28,9 +26,6 @@ func sysargs(argc int32, argv **byte) { for i := 0; auxv[i] != _AT_NULL; i += 2 { switch auxv[i] { - case _AT_SYSINFO: - _vdso = auxv[i+1] - case _AT_RANDOM: startupRandomData = (*[16]byte)(unsafe.Pointer(uintptr(auxv[i+1])))[:] } diff --git a/src/runtime/pprof/pprof.go b/src/runtime/pprof/pprof.go index e09a33d5d9..5e91fa8abe 100644 --- a/src/runtime/pprof/pprof.go +++ b/src/runtime/pprof/pprof.go @@ -325,33 +325,24 @@ func printCountProfile(w io.Writer, debug int, name string, p countProfile) erro // for a single stack trace. func printStackRecord(w io.Writer, stk []uintptr, allFrames bool) { show := allFrames - wasPanic := false - for i, pc := range stk { - f := runtime.FuncForPC(pc) - if f == nil { + frames := runtime.CallersFrames(stk) + for { + frame, more := frames.Next() + name := frame.Function + if name == "" { show = true - fmt.Fprintf(w, "#\t%#x\n", pc) - wasPanic = false + fmt.Fprintf(w, "#\t%#x\n", frame.PC) } else { - tracepc := pc - // Back up to call instruction. - if i > 0 && pc > f.Entry() && !wasPanic { - if runtime.GOARCH == "386" || runtime.GOARCH == "amd64" { - tracepc-- - } else { - tracepc -= 4 // arm, etc - } - } - file, line := f.FileLine(tracepc) - name := f.Name() // Hide runtime.goexit and any runtime functions at the beginning. // This is useful mainly for allocation traces. - wasPanic = name == "runtime.gopanic" if name == "runtime.goexit" || !show && strings.HasPrefix(name, "runtime.") { continue } show = true - fmt.Fprintf(w, "#\t%#x\t%s+%#x\t%s:%d\n", pc, name, pc-f.Entry(), file, line) + fmt.Fprintf(w, "#\t%#x\t%s+%#x\t%s:%d\n", frame.PC, name, frame.PC-frame.Entry, frame.File, frame.Line) + } + if !more { + break } } if !show { diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 389917916f..16237e98ec 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -660,10 +660,6 @@ func casfrom_Gscanstatus(gp *g, oldval, newval uint32) { if newval == oldval&^_Gscan { success = atomic.Cas(&gp.atomicstatus, oldval, newval) } - case _Gscanenqueue: - if newval == _Gwaiting { - success = atomic.Cas(&gp.atomicstatus, oldval, newval) - } } if !success { print("runtime: casfrom_Gscanstatus failed gp=", gp, ", oldval=", hex(oldval), ", newval=", hex(newval), "\n") @@ -680,15 +676,12 @@ func casfrom_Gscanstatus(gp *g, oldval, newval uint32) { func castogscanstatus(gp *g, oldval, newval uint32) bool { switch oldval { case _Grunnable, + _Grunning, _Gwaiting, _Gsyscall: if newval == oldval|_Gscan { return atomic.Cas(&gp.atomicstatus, oldval, newval) } - case _Grunning: - if newval == _Gscanrunning || newval == _Gscanenqueue { - return atomic.Cas(&gp.atomicstatus, oldval, newval) - } } print("runtime: castogscanstatus oldval=", hex(oldval), " newval=", hex(newval), "\n") throw("castogscanstatus") @@ -843,17 +836,6 @@ func restartg(gp *g) { _Gscanwaiting, _Gscansyscall: casfrom_Gscanstatus(gp, s, s&^_Gscan) - - // Scan is now completed. - // Goroutine now needs to be made runnable. - // We put it on the global run queue; ready blocks on the global scheduler lock. - case _Gscanenqueue: - casfrom_Gscanstatus(gp, _Gscanenqueue, _Gwaiting) - if gp != getg().m.curg { - throw("processing Gscanenqueue on wrong m") - } - dropg() - ready(gp, 0) } } @@ -2099,10 +2081,8 @@ top: func dropg() { _g_ := getg() - if _g_.m.lockedg == nil { - _g_.m.curg.m = nil - _g_.m.curg = nil - } + _g_.m.curg.m = nil + _g_.m.curg = nil } func parkunlock_c(gp *g, lock unsafe.Pointer) bool { @@ -3910,7 +3890,7 @@ retry: if runqputslow(_p_, gp, h, t) { return } - // the queue is not full, now the put above must suceed + // the queue is not full, now the put above must succeed goto retry } diff --git a/src/runtime/race/output_test.go b/src/runtime/race/output_test.go index 0c71a019dd..27d9efb687 100644 --- a/src/runtime/race/output_test.go +++ b/src/runtime/race/output_test.go @@ -180,4 +180,21 @@ func TestFail(t *testing.T) { PASS Found 1 data race\(s\) FAIL`}, + + {"slicebytetostring_pc", "run", "atexit_sleep_ms=0", ` +package main +func main() { + done := make(chan string) + data := make([]byte, 10) + go func() { + done <- string(data) + }() + data[0] = 1 + <-done +} +`, ` + runtime\.slicebytetostring\(\) + .*/runtime/string\.go:.* + main\.main\.func1\(\) + .*/main.go:7`}, } diff --git a/src/runtime/rt0_linux_386.s b/src/runtime/rt0_linux_386.s index 59a30b41e8..23bfc98b10 100644 --- a/src/runtime/rt0_linux_386.s +++ b/src/runtime/rt0_linux_386.s @@ -73,11 +73,3 @@ GLOBL _rt0_386_linux_lib_argv<>(SB),NOPTR, $4 TEXT main(SB),NOSPLIT,$0 JMP runtime·rt0_go(SB) - -TEXT _fallback_vdso(SB),NOSPLIT,$0 - INT $0x80 - RET - -DATA runtime·_vdso(SB)/4, $_fallback_vdso(SB) -GLOBL runtime·_vdso(SB), NOPTR, $4 - diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go index f63e09cc61..400ea296a9 100644 --- a/src/runtime/runtime1.go +++ b/src/runtime/runtime1.go @@ -52,7 +52,7 @@ var ( argv **byte ) -// nosplit for use in linux/386 startup linux_setup_vdso +// nosplit for use in linux startup sysargs //go:nosplit func argv_index(argv **byte, i int32) *byte { return *(**byte)(add(unsafe.Pointer(argv), uintptr(i)*sys.PtrSize)) diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 6a4dfa17b8..5f22afd863 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -26,7 +26,7 @@ const ( _Gwaiting // 4 _Gmoribund_unused // 5 currently unused, but hardcoded in gdb scripts _Gdead // 6 - _Genqueue // 7 Only the Gscanenqueue is used. + _Genqueue_unused // 7 currently unused _Gcopystack // 8 in this state when newstack is moving the stack // the following encode that the GC is scanning the stack and what to do when it is done _Gscan = 0x1000 // atomicstatus&~Gscan = the non-scan state, @@ -37,7 +37,7 @@ const ( _Gscanwaiting = _Gscan + _Gwaiting // 0x1004 When scanning completes make it Gwaiting // _Gscanmoribund_unused, // not possible // _Gscandead, // not possible - _Gscanenqueue = _Gscan + _Genqueue // When scanning completes make it Grunnable and put on runqueue + // _Gscanenqueue_unused // not possible ) const ( diff --git a/src/runtime/runtime_test.go b/src/runtime/runtime_test.go index a6150a77ee..b3350ef82f 100644 --- a/src/runtime/runtime_test.go +++ b/src/runtime/runtime_test.go @@ -104,7 +104,7 @@ func TestStopCPUProfilingWithProfilerOff(t *testing.T) { // of the larger addresses must themselves be invalid addresses. // We might get unlucky and the OS might have mapped one of these // addresses, but probably not: they're all in the first page, very high -// adderesses that normally an OS would reserve for itself, or malformed +// addresses that normally an OS would reserve for itself, or malformed // addresses. Even so, we might have to remove one or two on different // systems. We will see. diff --git a/src/runtime/select.go b/src/runtime/select.go index b6c3fea001..b315dde6c6 100644 --- a/src/runtime/select.go +++ b/src/runtime/select.go @@ -370,6 +370,8 @@ loop: sg.g = gp // Note: selectdone is adjusted for stack copies in stack1.go:adjustsudogs sg.selectdone = (*uint32)(noescape(unsafe.Pointer(&done))) + // No stack splits between assigning elem and enqueuing + // sg on gp.waiting where copystack can find it. sg.elem = cas.elem sg.releasetime = 0 if t0 != 0 { diff --git a/src/runtime/signal_386.go b/src/runtime/signal_386.go index 0374f4a2d7..25187dad74 100644 --- a/src/runtime/signal_386.go +++ b/src/runtime/signal_386.go @@ -142,30 +142,7 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) { level, _, docrash := gotraceback() if level > 0 { goroutineheader(gp) - - // On Linux/386, all system calls go through the vdso kernel_vsyscall routine. - // Normally we don't see those PCs, but during signals we can. - // If we see a PC in the vsyscall area (it moves around, but near the top of memory), - // assume we're blocked in the vsyscall routine, which has saved - // three words on the stack after the initial call saved the caller PC. - // Pop all four words off SP and use the saved PC. - // The check of the stack bounds here should suffice to avoid a fault - // during the actual PC pop. - // If we do load a bogus PC, not much harm done: we weren't going - // to get a decent traceback anyway. - // TODO(rsc): Make this more precise: we should do more checks on the PC, - // and we should find out whether different versions of the vdso page - // use different prologues that store different amounts on the stack. - pc := uintptr(c.eip()) - sp := uintptr(c.esp()) - if GOOS == "linux" && pc >= 0xf4000000 && gp.stack.lo <= sp && sp+16 <= gp.stack.hi { - // Assume in vsyscall page. - sp += 16 - pc = *(*uintptr)(unsafe.Pointer(sp - 4)) - print("runtime: unwind vdso kernel_vsyscall: pc=", hex(pc), " sp=", hex(sp), "\n") - } - - tracebacktrap(pc, sp, 0, gp) + tracebacktrap(uintptr(c.eip()), uintptr(c.esp()), 0, gp) if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning { // tracebackothers on original m skipped this one; trace it now. goroutineheader(_g_.m.curg) diff --git a/src/runtime/softfloat_arm.go b/src/runtime/softfloat_arm.go index 202e7bbf86..99048d612e 100644 --- a/src/runtime/softfloat_arm.go +++ b/src/runtime/softfloat_arm.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. -// Software floating point interpretaton of ARM 7500 FP instructions. +// Software floating point interpretation of ARM 7500 FP instructions. // The interpretation is not bit compatible with the 7500. // It uses true little-endian doubles, while the 7500 used mixed-endian. diff --git a/src/runtime/string.go b/src/runtime/string.go index dd04bda04b..5dc7e0295a 100644 --- a/src/runtime/string.go +++ b/src/runtime/string.go @@ -84,7 +84,7 @@ func slicebytetostring(buf *tmpBuf, b []byte) string { if raceenabled && l > 0 { racereadrangepc(unsafe.Pointer(&b[0]), uintptr(l), - getcallerpc(unsafe.Pointer(&b)), + getcallerpc(unsafe.Pointer(&buf)), funcPC(slicebytetostring)) } if msanenabled && l > 0 { @@ -189,7 +189,7 @@ func slicerunetostring(buf *tmpBuf, a []rune) string { if raceenabled && len(a) > 0 { racereadrangepc(unsafe.Pointer(&a[0]), uintptr(len(a))*unsafe.Sizeof(a[0]), - getcallerpc(unsafe.Pointer(&a)), + getcallerpc(unsafe.Pointer(&buf)), funcPC(slicerunetostring)) } if msanenabled && len(a) > 0 { diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go index 00b0a850e0..afea41448f 100644 --- a/src/runtime/symtab.go +++ b/src/runtime/symtab.go @@ -9,6 +9,84 @@ import ( "unsafe" ) +// Frames may be used to get function/file/line information for a +// slice of PC values returned by Callers. +type Frames struct { + callers []uintptr + + // If previous caller in iteration was a panic, then + // ci.callers[0] is the address of the faulting instruction + // instead of the return address of the call. + wasPanic bool +} + +// Frame is the information returned by Frames for each call frame. +type Frame struct { + // Program counter for this frame; multiple frames may have + // the same PC value. + PC uintptr + + // Func for this frame; may be nil for non-Go code or fully + // inlined functions. + Func *Func + + // Function name, file name, and line number for this call frame. + // May be the empty string or zero if not known. + // If Func is not nil then Function == Func.Name(). + Function string + File string + Line int + + // Entry point for the function; may be zero if not known. + // If Func is not nil then Entry == Func.Entry(). + Entry uintptr +} + +// CallersFrames takes a slice of PC values returned by Callers and +// prepares to return function/file/line information. +// Do not change the slice until you are done with the Frames. +func CallersFrames(callers []uintptr) *Frames { + return &Frames{callers, false} +} + +// Next returns frame information for the next caller. +// If more is false, there are no more callers (the Frame value is valid). +func (ci *Frames) Next() (frame Frame, more bool) { + if len(ci.callers) == 0 { + ci.wasPanic = false + return Frame{}, false + } + pc := ci.callers[0] + ci.callers = ci.callers[1:] + more = len(ci.callers) > 0 + f := FuncForPC(pc) + if f == nil { + ci.wasPanic = false + return Frame{}, more + } + + entry := f.Entry() + xpc := pc + if xpc > entry && !ci.wasPanic { + xpc-- + } + file, line := f.FileLine(xpc) + + function := f.Name() + ci.wasPanic = entry == sigpanicPC + + frame = Frame{ + PC: xpc, + Func: f, + Function: function, + File: file, + Line: line, + Entry: entry, + } + + return frame, more +} + // NOTE: Func does not expose the actual unexported fields, because we return *Func // values to users, and we want to keep them from being able to overwrite the data // with (say) *f = Func{}. diff --git a/src/runtime/sys_linux_386.s b/src/runtime/sys_linux_386.s index 1a3aaf0104..4a74196032 100644 --- a/src/runtime/sys_linux_386.s +++ b/src/runtime/sys_linux_386.s @@ -12,16 +12,17 @@ // Most linux systems use glibc's dynamic linker, which puts the // __kernel_vsyscall vdso helper at 0x10(GS) for easy access from position -// independent code and setldt in this file does the same in the statically -// linked case. Android, however, uses bionic's dynamic linker, which does not -// save the helper anywhere, and so the only way to invoke a syscall from -// position independent code is boring old int $0x80 (which is also what -// bionic's syscall wrappers use). -#ifdef GOOS_android +// independent code and setldt in runtime does the same in the statically +// linked case. However, systems that use alternative libc such as Android's +// bionic and musl, do not save the helper anywhere, and so the only way to +// invoke a syscall from position independent code is boring old int $0x80 +// (which is also what syscall wrappers in bionic/musl use). +// +// The benchmarks also showed that using int $0x80 is as fast as calling +// *%gs:0x10 except on AMD Opteron. See https://golang.org/cl/19833 +// for the benchmark program and raw data. +//#define INVOKE_SYSCALL CALL 0x10(GS) // non-portable #define INVOKE_SYSCALL INT $0x80 -#else -#define INVOKE_SYSCALL CALL 0x10(GS) -#endif TEXT runtime·exit(SB),NOSPLIT,$0 MOVL $252, AX // syscall number @@ -434,12 +435,6 @@ TEXT runtime·setldt(SB),NOSPLIT,$32 */ ADDL $0x4, DX // address MOVL DX, 0(DX) - // We copy the glibc dynamic linker behaviour of storing the - // __kernel_vsyscall entry point at 0x10(GS) so that it can be invoked - // by "CALL 0x10(GS)" in all situations, not only those where the - // binary is actually dynamically linked. - MOVL runtime·_vdso(SB), AX - MOVL AX, 0x10(DX) #endif // set up user_desc diff --git a/src/runtime/sys_solaris_amd64.s b/src/runtime/sys_solaris_amd64.s index 3a82674684..09f22636a9 100644 --- a/src/runtime/sys_solaris_amd64.s +++ b/src/runtime/sys_solaris_amd64.s @@ -52,7 +52,7 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$0 // pipe(3c) wrapper that returns fds in AX, DX. // NOT USING GO CALLING CONVENTION. TEXT runtime·pipe1(SB),NOSPLIT,$0 - SUBQ $16, SP // 8 bytes will do, but stack has to be 16-byte alligned + SUBQ $16, SP // 8 bytes will do, but stack has to be 16-byte aligned MOVQ SP, DI LEAQ libc_pipe(SB), AX CALL AX diff --git a/src/runtime/testdata/testprogcgo/cgo.go b/src/runtime/testdata/testprogcgo/cgo.go index 5d2550dbb0..7a2e013d43 100644 --- a/src/runtime/testdata/testprogcgo/cgo.go +++ b/src/runtime/testdata/testprogcgo/cgo.go @@ -11,7 +11,9 @@ void foo2(void* p) {} import "C" import ( "fmt" + "os" "runtime" + "strconv" "time" "unsafe" ) @@ -83,8 +85,16 @@ func CgoTraceback() { } func CgoCheckBytes() { - b := make([]byte, 1e6) - for i := 0; i < 1e3; i++ { + try, _ := strconv.Atoi(os.Getenv("GO_CGOCHECKBYTES_TRY")) + if try <= 0 { + try = 1 + } + b := make([]byte, 1e6*try) + start := time.Now() + for i := 0; i < 1e3*try; i++ { C.foo2(unsafe.Pointer(&b[0])) + if time.Since(start) > time.Second { + break + } } } diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index b4bfe71627..1d76947380 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -645,7 +645,6 @@ var gStatusStrings = [...]string{ _Gsyscall: "syscall", _Gwaiting: "waiting", _Gdead: "dead", - _Genqueue: "enqueue", _Gcopystack: "copystack", } diff --git a/src/runtime/type.go b/src/runtime/type.go index d5f3bb1ef0..18c6a32ecb 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -24,9 +24,38 @@ type _type struct { // If the KindGCProg bit is set in kind, gcdata is a GC program. // Otherwise it is a ptrmask bitmap. See mbitmap.go for details. gcdata *byte - _string *string + _string string x *uncommontype - ptrto *_type +} + +func hasPrefix(s, prefix string) bool { + return len(s) >= len(prefix) && s[:len(prefix)] == prefix +} + +func (t *_type) name() string { + if hasPrefix(t._string, "map[") { + return "" + } + if hasPrefix(t._string, "struct {") { + return "" + } + if hasPrefix(t._string, "chan ") { + return "" + } + if hasPrefix(t._string, "func(") { + return "" + } + if t._string[0] == '[' || t._string[0] == '*' { + return "" + } + i := len(t._string) - 1 + for i >= 0 { + if t._string[i] == '.' { + break + } + i-- + } + return t._string[i+1:] } type method struct { @@ -39,7 +68,6 @@ type method struct { } type uncommontype struct { - name *string pkgpath *string mhdr []method } diff --git a/src/sort/sort.go b/src/sort/sort.go index 5eb45c6d4a..ce3dc06f88 100644 --- a/src/sort/sort.go +++ b/src/sort/sort.go @@ -119,15 +119,15 @@ func doPivot(data Interface, lo, hi int) (midlo, midhi int) { pivot := lo a, c := lo+1, hi-1 - for ; a != c && data.Less(a, pivot); a++ { + for ; a < c && data.Less(a, pivot); a++ { } b := a for { - for ; b != c && !data.Less(pivot, b); b++ { // data[b] <= pivot + for ; b < c && !data.Less(pivot, b); b++ { // data[b] <= pivot } - for ; b != c && data.Less(pivot, c-1); c-- { // data[c-1] > pivot + for ; b < c && data.Less(pivot, c-1); c-- { // data[c-1] > pivot } - if b == c { + if b >= c { break } // data[b] > pivot; data[c-1] <= pivot @@ -167,11 +167,11 @@ func doPivot(data Interface, lo, hi int) (midlo, midhi int) { // data[a <= i < b] unexamined // data[b <= i < c] = pivot for { - for ; a != b && !data.Less(b-1, pivot); b-- { // data[b] == pivot + for ; a < b && !data.Less(b-1, pivot); b-- { // data[b] == pivot } - for ; a != b && data.Less(a, pivot); a++ { // data[a] < pivot + for ; a < b && data.Less(a, pivot); a++ { // data[a] < pivot } - if a == b { + if a >= b { break } // data[a] == pivot; data[b-1] < pivot diff --git a/src/sort/sort_test.go b/src/sort/sort_test.go index 6c36f30e0e..60fac2d695 100644 --- a/src/sort/sort_test.go +++ b/src/sort/sort_test.go @@ -109,6 +109,43 @@ func TestReverseSortIntSlice(t *testing.T) { } } +type nonDeterministicTestingData struct { + r *rand.Rand +} + +func (t *nonDeterministicTestingData) Len() int { + return 500 +} +func (t *nonDeterministicTestingData) Less(i, j int) bool { + if i < 0 || j < 0 || i >= t.Len() || j >= t.Len() { + panic("nondeterministic comparison out of bounds") + } + return t.r.Float32() < 0.5 +} +func (t *nonDeterministicTestingData) Swap(i, j int) { + if i < 0 || j < 0 || i >= t.Len() || j >= t.Len() { + panic("nondeterministic comparison out of bounds") + } +} + +func TestNonDeterministicComparison(t *testing.T) { + // Ensure that sort.Sort does not panic when Less returns inconsistent results. + // See https://golang.org/issue/14377. + defer func() { + if r := recover(); r != nil { + t.Error(r) + } + }() + + td := &nonDeterministicTestingData{ + r: rand.New(rand.NewSource(0)), + } + + for i := 0; i < 10; i++ { + Sort(td) + } +} + func BenchmarkSortString1K(b *testing.B) { b.StopTimer() for i := 0; i < b.N; i++ { @@ -469,10 +506,10 @@ func TestStability(t *testing.T) { data.initB() Stable(data) if !IsSorted(data) { - t.Errorf("Stable shuffeled sorted %d ints (order)", n) + t.Errorf("Stable shuffled sorted %d ints (order)", n) } if !data.inOrder() { - t.Errorf("Stable shuffeled sorted %d ints (stability)", n) + t.Errorf("Stable shuffled sorted %d ints (stability)", n) } // sorted reversed diff --git a/src/strconv/quote.go b/src/strconv/quote.go index 40d0667551..a37a309f26 100644 --- a/src/strconv/quote.go +++ b/src/strconv/quote.go @@ -6,15 +6,19 @@ package strconv -import ( - "unicode/utf8" -) +import "unicode/utf8" const lowerhex = "0123456789abcdef" func quoteWith(s string, quote byte, ASCIIonly, graphicOnly bool) string { - var runeTmp [utf8.UTFMax]byte - buf := make([]byte, 0, 3*len(s)/2) // Try to avoid more allocations. + return string(appendQuotedWith(make([]byte, 0, 3*len(s)/2), s, quote, ASCIIonly, graphicOnly)) +} + +func quoteRuneWith(r rune, quote byte, ASCIIonly, graphicOnly bool) string { + return string(appendQuotedRuneWith(nil, r, quote, ASCIIonly, graphicOnly)) +} + +func appendQuotedWith(buf []byte, s string, quote byte, ASCIIonly, graphicOnly bool) []byte { buf = append(buf, quote) for width := 0; len(s) > 0; s = s[width:] { r := rune(s[0]) @@ -28,61 +32,76 @@ func quoteWith(s string, quote byte, ASCIIonly, graphicOnly bool) string { buf = append(buf, lowerhex[s[0]&0xF]) continue } - if r == rune(quote) || r == '\\' { // always backslashed - buf = append(buf, '\\') + buf = appendEscapedRune(buf, r, width, quote, ASCIIonly, graphicOnly) + } + buf = append(buf, quote) + return buf +} + +func appendQuotedRuneWith(buf []byte, r rune, quote byte, ASCIIonly, graphicOnly bool) []byte { + buf = append(buf, quote) + if !utf8.ValidRune(r) { + r = utf8.RuneError + } + buf = appendEscapedRune(buf, r, utf8.RuneLen(r), quote, ASCIIonly, graphicOnly) + buf = append(buf, quote) + return buf +} + +func appendEscapedRune(buf []byte, r rune, width int, quote byte, ASCIIonly, graphicOnly bool) []byte { + var runeTmp [utf8.UTFMax]byte + if r == rune(quote) || r == '\\' { // always backslashed + buf = append(buf, '\\') + buf = append(buf, byte(r)) + return buf + } + if ASCIIonly { + if r < utf8.RuneSelf && IsPrint(r) { buf = append(buf, byte(r)) - continue + return buf } - if ASCIIonly { - if r < utf8.RuneSelf && IsPrint(r) { - buf = append(buf, byte(r)) - continue + } else if IsPrint(r) || graphicOnly && isInGraphicList(r) { + n := utf8.EncodeRune(runeTmp[:], r) + buf = append(buf, runeTmp[:n]...) + return buf + } + switch r { + case '\a': + buf = append(buf, `\a`...) + case '\b': + buf = append(buf, `\b`...) + case '\f': + buf = append(buf, `\f`...) + case '\n': + buf = append(buf, `\n`...) + case '\r': + buf = append(buf, `\r`...) + case '\t': + buf = append(buf, `\t`...) + case '\v': + buf = append(buf, `\v`...) + default: + switch { + case r < ' ': + buf = append(buf, `\x`...) + buf = append(buf, lowerhex[byte(r)>>4]) + buf = append(buf, lowerhex[byte(r)&0xF]) + case r > utf8.MaxRune: + r = 0xFFFD + fallthrough + case r < 0x10000: + buf = append(buf, `\u`...) + for s := 12; s >= 0; s -= 4 { + buf = append(buf, lowerhex[r>>uint(s)&0xF]) } - } else if IsPrint(r) || graphicOnly && isInGraphicList(r) { - n := utf8.EncodeRune(runeTmp[:], r) - buf = append(buf, runeTmp[:n]...) - continue - } - switch r { - case '\a': - buf = append(buf, `\a`...) - case '\b': - buf = append(buf, `\b`...) - case '\f': - buf = append(buf, `\f`...) - case '\n': - buf = append(buf, `\n`...) - case '\r': - buf = append(buf, `\r`...) - case '\t': - buf = append(buf, `\t`...) - case '\v': - buf = append(buf, `\v`...) default: - switch { - case r < ' ': - buf = append(buf, `\x`...) - buf = append(buf, lowerhex[s[0]>>4]) - buf = append(buf, lowerhex[s[0]&0xF]) - case r > utf8.MaxRune: - r = 0xFFFD - fallthrough - case r < 0x10000: - buf = append(buf, `\u`...) - for s := 12; s >= 0; s -= 4 { - buf = append(buf, lowerhex[r>>uint(s)&0xF]) - } - default: - buf = append(buf, `\U`...) - for s := 28; s >= 0; s -= 4 { - buf = append(buf, lowerhex[r>>uint(s)&0xF]) - } + buf = append(buf, `\U`...) + for s := 28; s >= 0; s -= 4 { + buf = append(buf, lowerhex[r>>uint(s)&0xF]) } } } - buf = append(buf, quote) - return string(buf) - + return buf } // Quote returns a double-quoted Go string literal representing s. The @@ -96,7 +115,7 @@ func Quote(s string) string { // AppendQuote appends a double-quoted Go string literal representing s, // as generated by Quote, to dst and returns the extended buffer. func AppendQuote(dst []byte, s string) []byte { - return append(dst, Quote(s)...) + return appendQuotedWith(dst, s, '"', false, false) } // QuoteToASCII returns a double-quoted Go string literal representing s. @@ -109,7 +128,7 @@ func QuoteToASCII(s string) string { // AppendQuoteToASCII appends a double-quoted Go string literal representing s, // as generated by QuoteToASCII, to dst and returns the extended buffer. func AppendQuoteToASCII(dst []byte, s string) []byte { - return append(dst, QuoteToASCII(s)...) + return appendQuotedWith(dst, s, '"', true, false) } // QuoteToGraphic returns a double-quoted Go string literal representing s. @@ -122,21 +141,20 @@ func QuoteToGraphic(s string) string { // AppendQuoteToGraphic appends a double-quoted Go string literal representing s, // as generated by QuoteToGraphic, to dst and returns the extended buffer. func AppendQuoteToGraphic(dst []byte, s string) []byte { - return append(dst, QuoteToGraphic(s)...) + return appendQuotedWith(dst, s, '"', false, true) } // QuoteRune returns a single-quoted Go character literal representing the // rune. The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) // for control characters and non-printable characters as defined by IsPrint. func QuoteRune(r rune) string { - // TODO: avoid the allocation here. - return quoteWith(string(r), '\'', false, false) + return quoteRuneWith(r, '\'', false, false) } // AppendQuoteRune appends a single-quoted Go character literal representing the rune, // as generated by QuoteRune, to dst and returns the extended buffer. func AppendQuoteRune(dst []byte, r rune) []byte { - return append(dst, QuoteRune(r)...) + return appendQuotedRuneWith(dst, r, '\'', false, false) } // QuoteRuneToASCII returns a single-quoted Go character literal representing @@ -144,14 +162,13 @@ func AppendQuoteRune(dst []byte, r rune) []byte { // \u0100) for non-ASCII characters and non-printable characters as defined // by IsPrint. func QuoteRuneToASCII(r rune) string { - // TODO: avoid the allocation here. - return quoteWith(string(r), '\'', true, false) + return quoteRuneWith(r, '\'', true, false) } // AppendQuoteRuneToASCII appends a single-quoted Go character literal representing the rune, // as generated by QuoteRuneToASCII, to dst and returns the extended buffer. func AppendQuoteRuneToASCII(dst []byte, r rune) []byte { - return append(dst, QuoteRuneToASCII(r)...) + return appendQuotedRuneWith(dst, r, '\'', true, false) } // QuoteRuneToGraphic returns a single-quoted Go character literal representing @@ -159,14 +176,13 @@ func AppendQuoteRuneToASCII(dst []byte, r rune) []byte { // \u0100) for non-ASCII characters and non-printable characters as defined // by IsGraphic. func QuoteRuneToGraphic(r rune) string { - // TODO: avoid the allocation here. - return quoteWith(string(r), '\'', false, true) + return quoteRuneWith(r, '\'', false, true) } // AppendQuoteRuneToGraphic appends a single-quoted Go character literal representing the rune, // as generated by QuoteRuneToGraphic, to dst and returns the extended buffer. func AppendQuoteRuneToGraphic(dst []byte, r rune) []byte { - return append(dst, QuoteRuneToGraphic(r)...) + return appendQuotedRuneWith(dst, r, '\'', false, true) } // CanBackquote reports whether the string s can be represented diff --git a/src/strconv/quote_test.go b/src/strconv/quote_test.go index 3e8ec2c98f..10735e316c 100644 --- a/src/strconv/quote_test.go +++ b/src/strconv/quote_test.go @@ -89,6 +89,34 @@ func TestQuoteToGraphic(t *testing.T) { } } +func BenchmarkQuote(b *testing.B) { + for i := 0; i < b.N; i++ { + Quote("\a\b\f\r\n\t\v\a\b\f\r\n\t\v\a\b\f\r\n\t\v") + } +} + +func BenchmarkQuoteRune(b *testing.B) { + for i := 0; i < b.N; i++ { + QuoteRune('\a') + } +} + +var benchQuoteBuf []byte + +func BenchmarkAppendQuote(b *testing.B) { + for i := 0; i < b.N; i++ { + benchQuoteBuf = AppendQuote(benchQuoteBuf[:0], "\a\b\f\r\n\t\v\a\b\f\r\n\t\v\a\b\f\r\n\t\v") + } +} + +var benchQuoteRuneBuf []byte + +func BenchmarkAppendQuoteRune(b *testing.B) { + for i := 0; i < b.N; i++ { + benchQuoteRuneBuf = AppendQuoteRune(benchQuoteRuneBuf[:0], '\a') + } +} + type quoteRuneTest struct { in rune out string diff --git a/src/sync/atomic/asm_plan9_arm.s b/src/sync/atomic/asm_plan9_arm.s new file mode 100644 index 0000000000..5108fc6b91 --- /dev/null +++ b/src/sync/atomic/asm_plan9_arm.s @@ -0,0 +1,108 @@ +// Copyright 2012 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. + +#include "textflag.h" + +#define DMB_ISH_7 \ + MOVB runtime·goarm(SB), R11; \ + CMP $7, R11; \ + BLT 2(PC); \ + WORD $0xf57ff05b // dmb ish + +// Plan9/ARM atomic operations. +// TODO(minux): this only supports ARMv6K or higher. + +TEXT ·CompareAndSwapInt32(SB),NOSPLIT,$0 + B ·CompareAndSwapUint32(SB) + +TEXT ·CompareAndSwapUint32(SB),NOSPLIT,$0 + B ·armCompareAndSwapUint32(SB) + +TEXT ·CompareAndSwapUintptr(SB),NOSPLIT,$0 + B ·CompareAndSwapUint32(SB) + +TEXT ·AddInt32(SB),NOSPLIT,$0 + B ·AddUint32(SB) + +TEXT ·AddUint32(SB),NOSPLIT,$0 + B ·armAddUint32(SB) + +TEXT ·AddUintptr(SB),NOSPLIT,$0 + B ·AddUint32(SB) + +TEXT ·SwapInt32(SB),NOSPLIT,$0 + B ·SwapUint32(SB) + +TEXT ·SwapUint32(SB),NOSPLIT,$0 + B ·armSwapUint32(SB) + +TEXT ·SwapUintptr(SB),NOSPLIT,$0 + B ·SwapUint32(SB) + +TEXT ·CompareAndSwapInt64(SB),NOSPLIT,$0 + B ·CompareAndSwapUint64(SB) + +TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$-4 + B ·armCompareAndSwapUint64(SB) + +TEXT ·AddInt64(SB),NOSPLIT,$0 + B ·addUint64(SB) + +TEXT ·AddUint64(SB),NOSPLIT,$0 + B ·addUint64(SB) + +TEXT ·SwapInt64(SB),NOSPLIT,$0 + B ·swapUint64(SB) + +TEXT ·SwapUint64(SB),NOSPLIT,$0 + B ·swapUint64(SB) + +TEXT ·LoadInt32(SB),NOSPLIT,$0 + B ·LoadUint32(SB) + +TEXT ·LoadUint32(SB),NOSPLIT,$0-8 + MOVW addr+0(FP), R1 +load32loop: + LDREX (R1), R2 // loads R2 + STREX R2, (R1), R0 // stores R2 + CMP $0, R0 + BNE load32loop + MOVW R2, val+4(FP) + DMB_ISH_7 + RET + +TEXT ·LoadInt64(SB),NOSPLIT,$0 + B ·loadUint64(SB) + +TEXT ·LoadUint64(SB),NOSPLIT,$0 + B ·loadUint64(SB) + +TEXT ·LoadUintptr(SB),NOSPLIT,$0 + B ·LoadUint32(SB) + +TEXT ·LoadPointer(SB),NOSPLIT,$0 + B ·LoadUint32(SB) + +TEXT ·StoreInt32(SB),NOSPLIT,$0 + B ·StoreUint32(SB) + +TEXT ·StoreUint32(SB),NOSPLIT,$0-8 + MOVW addr+0(FP), R1 + MOVW val+4(FP), R2 + DMB_ISH_7 +storeloop: + LDREX (R1), R4 // loads R4 + STREX R2, (R1), R0 // stores R2 + CMP $0, R0 + BNE storeloop + RET + +TEXT ·StoreInt64(SB),NOSPLIT,$0 + B ·storeUint64(SB) + +TEXT ·StoreUint64(SB),NOSPLIT,$0 + B ·storeUint64(SB) + +TEXT ·StoreUintptr(SB),NOSPLIT,$0 + B ·StoreUint32(SB) diff --git a/src/sync/atomic/value_test.go b/src/sync/atomic/value_test.go index 382dc6854d..fd90451dd8 100644 --- a/src/sync/atomic/value_test.go +++ b/src/sync/atomic/value_test.go @@ -86,6 +86,11 @@ func TestValueConcurrent(t *testing.T) { {complex(0, 0), complex(1, 2), complex(3, 4), complex(5, 6)}, } p := 4 * runtime.GOMAXPROCS(0) + N := int(1e5) + if testing.Short() { + p /= 2 + N = 1e3 + } for _, test := range tests { var v Value done := make(chan bool) @@ -93,7 +98,7 @@ func TestValueConcurrent(t *testing.T) { go func() { r := rand.New(rand.NewSource(rand.Int63())) loop: - for j := 0; j < 1e5; j++ { + for j := 0; j < N; j++ { x := test[r.Intn(len(test))] v.Store(x) x = v.Load() diff --git a/src/syscall/asm_linux_386.s b/src/syscall/asm_linux_386.s index c94060571b..228a542cf1 100644 --- a/src/syscall/asm_linux_386.s +++ b/src/syscall/asm_linux_386.s @@ -12,18 +12,9 @@ // func Syscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr); // Trap # in AX, args in BX CX DX SI DI, return in AX -// Most linux systems use glibc's dynamic linker, which puts the -// __kernel_vsyscall vdso helper at 0x10(GS) for easy access from position -// independent code and setldt in runtime does the same in the statically -// linked case. Android, however, uses bionic's dynamic linker, which does not -// save the helper anywhere, and so the only way to invoke a syscall from -// position independent code is boring old int $0x80 (which is also what -// bionic's syscall wrappers use). -#ifdef GOOS_android +// See ../runtime/sys_linux_386.s for the reason why we always use int 0x80 +// instead of the glibc-specific "CALL 0x10(GS)". #define INVOKE_SYSCALL INT $0x80 -#else -#define INVOKE_SYSCALL CALL 0x10(GS) -#endif TEXT ·Syscall(SB),NOSPLIT,$0-28 CALL runtime·entersyscall(SB) diff --git a/src/syscall/asm_plan9_arm.s b/src/syscall/asm_plan9_arm.s new file mode 100644 index 0000000000..dd0584446d --- /dev/null +++ b/src/syscall/asm_plan9_arm.s @@ -0,0 +1,97 @@ +// 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. + +#include "textflag.h" + +#define SYS_SEEK 39 /* from zsysnum_plan9.go */ + +// System call support for plan9 on arm + +TEXT sysresult<>(SB),NOSPLIT,$12 + MOVW $runtime·emptystring+0(SB), R2 + CMP $-1, R0 + B.NE ok + MOVW R1, save-4(SP) + BL runtime·errstr(SB) + MOVW save-4(SP), R1 + MOVW $err-12(SP), R2 +ok: + MOVM.IA (R2), [R3-R4] + MOVM.IA [R3-R4], (R1) + RET + +//func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err ErrorString) +TEXT ·Syscall(SB),NOSPLIT,$0 + BL runtime·entersyscall(SB) + MOVW trap+0(FP), R0 // syscall num + MOVM.IA.W (R13),[R1-R2] // pop LR and caller's LR + SWI 0 + MOVM.DB.W [R1-R2],(R13) // push LR and caller's LR + MOVW $0, R2 + MOVW $r1+16(FP), R1 + MOVM.IA.W [R0,R2], (R1) + BL sysresult<>(SB) + BL runtime·exitsyscall(SB) + RET + +//func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err ErrorString) +// Actually Syscall5 but the rest of the code expects it to be named Syscall6. +TEXT ·Syscall6(SB),NOSPLIT,$0 + BL runtime·entersyscall(SB) + MOVW trap+0(FP), R0 // syscall num + MOVM.IA.W (R13),[R1-R2] // pop LR and caller's LR + SWI 0 + MOVM.DB.W [R1-R2],(R13) // push LR and caller's LR + MOVW $0, R1 + MOVW $r1+28(FP), R1 + MOVM.IA.W [R0,R2], (R1) + BL sysresult<>(SB) + BL runtime·exitsyscall(SB) + RET + +//func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) +TEXT ·RawSyscall(SB),NOSPLIT,$0 + MOVW trap+0(FP), R0 // syscall num + MOVM.IA.W (R13),[R1] // pop caller's LR + SWI 0 + MOVM.DB.W [R1],(R13) // push caller's LR + MOVW R0, r1+16(FP) + MOVW R0, r2+20(FP) + MOVW R0, err+24(FP) + RET + +//func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) +// Actually RawSyscall5 but the rest of the code expects it to be named RawSyscall6. +TEXT ·RawSyscall6(SB),NOSPLIT,$0 + MOVW trap+0(FP), R0 // syscall num + MOVM.IA.W (R13),[R1] // pop caller's LR + SWI 0 + MOVM.DB.W [R1],(R13) // push caller's LR + MOVW R0, r1+28(FP) + MOVW R0, r2+32(FP) + MOVW R0, err+36(FP) + RET + +//func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string) +TEXT ·seek(SB),NOSPLIT,$0 + MOVW $newoffset_lo+20(FP), R5 + MOVW R5, placeholder+0(FP) //placeholder = dest for return value + MOVW $SYS_SEEK, R0 // syscall num + MOVM.IA.W (R13),[R1] // pop LR + SWI 0 + MOVM.DB.W [R1],(R13) // push LR + CMP $-1, R0 + MOVW.EQ R0, 0(R5) + MOVW.EQ R0, 4(R5) + MOVW $err+28(FP), R1 + BL sysresult<>(SB) + RET + +//func exit(code int) +// Import runtime·exit for cleanly exiting. +TEXT ·exit(SB),NOSPLIT,$4 + MOVW code+0(FP), R0 + MOVW R0, e-4(SP) + BL runtime·exit(SB) + RET diff --git a/src/syscall/route_bsd.go b/src/syscall/route_bsd.go index c635a1385e..d9b6697a75 100644 --- a/src/syscall/route_bsd.go +++ b/src/syscall/route_bsd.go @@ -138,7 +138,7 @@ func parseNetworkLayerAddr(b []byte, family byte) (Sockaddr, error) { // // - The kernel form appends leading bytes to the prefix field // to make the tuple to be conformed with - // the routing messeage boundary + // the routing message boundary l := int(rsaAlignOf(int(b[0]))) if len(b) < l { return nil, EINVAL diff --git a/src/syscall/zsyscall_plan9_arm.go b/src/syscall/zsyscall_plan9_arm.go new file mode 100644 index 0000000000..d54aeff3a6 --- /dev/null +++ b/src/syscall/zsyscall_plan9_arm.go @@ -0,0 +1,294 @@ +// mksyscall.pl -l32 -plan9 syscall_plan9.go +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +// +build arm,plan9 + +package syscall + +import "unsafe" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fd2path(fd int, buf []byte) (err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf))) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pipe(p *[2]int32) (err error) { + r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func await(s []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(s) > 0 { + _p0 = unsafe.Pointer(&s[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0) + n = int(r0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func open(path string, mode int) (fd int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + use(unsafe.Pointer(_p0)) + fd = int(r0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func create(path string, mode int, perm uint32) (fd int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) + use(unsafe.Pointer(_p0)) + fd = int(r0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func remove(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0) + use(unsafe.Pointer(_p0)) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func stat(path string, edir []byte) (n int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer + if len(edir) > 0 { + _p1 = unsafe.Pointer(&edir[0]) + } else { + _p1 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) + use(unsafe.Pointer(_p0)) + n = int(r0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func bind(name string, old string, flag int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(old) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag)) + use(unsafe.Pointer(_p0)) + use(unsafe.Pointer(_p1)) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func mount(fd int, afd int, old string, flag int, aname string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(old) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(aname) + if err != nil { + return + } + r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0) + use(unsafe.Pointer(_p0)) + use(unsafe.Pointer(_p1)) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func wstat(path string, edir []byte) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer + if len(edir) > 0 { + _p1 = unsafe.Pointer(&edir[0]) + } else { + _p1 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) + use(unsafe.Pointer(_p0)) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func chdir(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) + use(unsafe.Pointer(_p0)) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Dup(oldfd int, newfd int) (fd int, err error) { + r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0) + fd = int(r0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Pread(fd int, p []byte, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) + n = int(r0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Pwrite(fd int, p []byte, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) + n = int(r0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Close(fd int) (err error) { + r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fstat(fd int, edir []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(edir) > 0 { + _p0 = unsafe.Pointer(&edir[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir))) + n = int(r0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fwstat(fd int, edir []byte) (err error) { + var _p0 unsafe.Pointer + if len(edir) > 0 { + _p0 = unsafe.Pointer(&edir[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir))) + if int32(r0) == -1 { + err = e1 + } + return +} diff --git a/src/testing/quick/quick.go b/src/testing/quick/quick.go index 187195c759..0c2bf2d72b 100644 --- a/src/testing/quick/quick.go +++ b/src/testing/quick/quick.go @@ -14,7 +14,7 @@ import ( "strings" ) -var defaultMaxCount *int = flag.Int("quickchecks", 100, "The default number of iterations for each check") +var defaultMaxCount = flag.Int("quickchecks", 100, "The default number of iterations for each check") // A Generator can generate random values of its own type. type Generator interface { @@ -98,18 +98,22 @@ func sizedValue(t reflect.Type, rand *rand.Rand, size int) (value reflect.Value, case reflect.Uintptr: v.SetUint(uint64(randInt64(rand))) case reflect.Map: - numElems := rand.Intn(size) - v.Set(reflect.MakeMap(concrete)) - for i := 0; i < numElems; i++ { - key, ok1 := sizedValue(concrete.Key(), rand, size) - value, ok2 := sizedValue(concrete.Elem(), rand, size) - if !ok1 || !ok2 { - return reflect.Value{}, false + if generateNilValue(rand) { + v.Set(reflect.Zero(concrete)) // Generate nil map. + } else { + numElems := rand.Intn(size) + v.Set(reflect.MakeMap(concrete)) + for i := 0; i < numElems; i++ { + key, ok1 := sizedValue(concrete.Key(), rand, size) + value, ok2 := sizedValue(concrete.Elem(), rand, size) + if !ok1 || !ok2 { + return reflect.Value{}, false + } + v.SetMapIndex(key, value) } - v.SetMapIndex(key, value) } case reflect.Ptr: - if rand.Intn(size) == 0 { + if generateNilValue(rand) { v.Set(reflect.Zero(concrete)) // Generate nil pointer. } else { elem, ok := sizedValue(concrete.Elem(), rand, size) @@ -120,15 +124,20 @@ func sizedValue(t reflect.Type, rand *rand.Rand, size int) (value reflect.Value, v.Elem().Set(elem) } case reflect.Slice: - numElems := rand.Intn(size) - sizeLeft := size - numElems - v.Set(reflect.MakeSlice(concrete, numElems, numElems)) - for i := 0; i < numElems; i++ { - elem, ok := sizedValue(concrete.Elem(), rand, sizeLeft) - if !ok { - return reflect.Value{}, false + if generateNilValue(rand) { + v.Set(reflect.Zero(concrete)) // Generate nil slice. + } else { + slCap := rand.Intn(size) + slLen := rand.Intn(slCap + 1) + sizeLeft := size - slCap + v.Set(reflect.MakeSlice(concrete, slLen, slCap)) + for i := 0; i < slLen; i++ { + elem, ok := sizedValue(concrete.Elem(), rand, sizeLeft) + if !ok { + return reflect.Value{}, false + } + v.Index(i).Set(elem) } - v.Index(i).Set(elem) } case reflect.Array: for i := 0; i < v.Len(); i++ { @@ -384,3 +393,5 @@ func toString(interfaces []interface{}) string { } return strings.Join(s, ", ") } + +func generateNilValue(r *rand.Rand) bool { return r.Intn(20) == 0 } diff --git a/src/testing/quick/quick_test.go b/src/testing/quick/quick_test.go index fe443592f8..018ece2a52 100644 --- a/src/testing/quick/quick_test.go +++ b/src/testing/quick/quick_test.go @@ -290,20 +290,3 @@ func TestMutuallyRecursive(t *testing.T) { f := func(a A) bool { return true } Check(f, nil) } - -// Some serialization formats (e.g. encoding/pem) cannot distinguish -// between a nil and an empty map or slice, so avoid generating the -// zero value for these. -func TestNonZeroSliceAndMap(t *testing.T) { - type Q struct { - M map[int]int - S []int - } - f := func(q Q) bool { - return q.M != nil && q.S != nil - } - err := Check(f, nil) - if err != nil { - t.Fatal(err) - } -} diff --git a/src/text/tabwriter/example_test.go b/src/text/tabwriter/example_test.go index 20443cb1ff..422ec117ad 100644 --- a/src/text/tabwriter/example_test.go +++ b/src/text/tabwriter/example_test.go @@ -36,3 +36,38 @@ func ExampleWriter_Init() { // a b c d. // 123 12345 1234567 123456789. } + +func Example_elastic() { + // Observe how the b's and the d's, despite appearing in the + // second cell of each line, belong to different columns. + w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, '.', tabwriter.AlignRight|tabwriter.Debug) + fmt.Fprintln(w, "a\tb\tc") + fmt.Fprintln(w, "aa\tbb\tcc") + fmt.Fprintln(w, "aaa\t") // trailing tab + fmt.Fprintln(w, "aaaa\tdddd\teeee") + w.Flush() + + // output: + // ....a|..b|c + // ...aa|.bb|cc + // ..aaa| + // .aaaa|.dddd|eeee +} + +func Example_trailingTab() { + // Observe that the third line has no trailing tab, + // so its final cell is not part of an aligned column. + const padding = 3 + w := tabwriter.NewWriter(os.Stdout, 0, 0, padding, '-', tabwriter.AlignRight|tabwriter.Debug) + fmt.Fprintln(w, "a\tb\taligned\t") + fmt.Fprintln(w, "aa\tbb\taligned\t") + fmt.Fprintln(w, "aaa\tbbb\tunaligned") // no trailing tab + fmt.Fprintln(w, "aaaa\tbbbb\taligned\t") + w.Flush() + + // output: + // ------a|------b|---aligned| + // -----aa|-----bb|---aligned| + // ----aaa|----bbb|unaligned + // ---aaaa|---bbbb|---aligned| +} diff --git a/src/text/tabwriter/tabwriter.go b/src/text/tabwriter/tabwriter.go index c0c32d5dec..4cafdba2b2 100644 --- a/src/text/tabwriter/tabwriter.go +++ b/src/text/tabwriter/tabwriter.go @@ -33,18 +33,32 @@ type cell struct { // A Writer is a filter that inserts padding around tab-delimited // columns in its input to align them in the output. // -// The Writer treats incoming bytes as UTF-8 encoded text consisting -// of cells terminated by (horizontal or vertical) tabs or line -// breaks (newline or formfeed characters). Cells in adjacent lines -// constitute a column. The Writer inserts padding as needed to -// make all cells in a column have the same width, effectively -// aligning the columns. It assumes that all characters have the -// same width except for tabs for which a tabwidth must be specified. -// Note that cells are tab-terminated, not tab-separated: trailing -// non-tab text at the end of a line does not form a column cell. +// The Writer treats incoming bytes as UTF-8-encoded text consisting +// of cells terminated by horizontal ('\t') or vertical ('\v') tabs, +// and newline ('\n') or formfeed ('\f') characters; both newline and +// formfeed act as line breaks. +// +// Tab-terminated cells in contiguous lines constitute a column. The +// Writer inserts padding as needed to make all cells in a column have +// the same width, effectively aligning the columns. It assumes that +// all characters have the same width, except for tabs for which a +// tabwidth must be specified. Column cells must be tab-terminated, not +// tab-separated: non-tab terminated trailing text at the end of a line +// forms a cell but that cell is not part of an aligned column. +// For instance, in this example (where | stands for a horizontal tab): +// +// aaaa|bbb|d +// aa |b |dd +// a | +// aa |cccc|eee +// +// the b and c are in distinct columns (the b column is not contiguous +// all the way). The d and e are not in a column at all (there's no +// terminating tab, nor would the column be contiguous). // // The Writer assumes that all Unicode code points have the same width; -// this may not be true in some fonts. +// this may not be true in some fonts or if the string contains combining +// characters. // // If DiscardEmptyColumns is set, empty columns that are terminated // entirely by vertical (or "soft") tabs are discarded. Columns @@ -64,9 +78,9 @@ type cell struct { // width of the escaped text is always computed excluding the Escape // characters. // -// The formfeed character ('\f') acts like a newline but it also -// terminates all columns in the current line (effectively calling -// Flush). Cells in the next line start new columns. Unless found +// The formfeed character acts like a newline but it also terminates +// all columns in the current line (effectively calling Flush). Tab- +// terminated cells in the next line start new columns. Unless found // inside an HTML tag or inside an escaped text segment, formfeed // characters appear as newlines in the output. // diff --git a/src/text/template/multi_test.go b/src/text/template/multi_test.go index a8342f50aa..c8723cb7a8 100644 --- a/src/text/template/multi_test.go +++ b/src/text/template/multi_test.go @@ -4,7 +4,7 @@ package template -// Tests for mulitple-template parsing and execution. +// Tests for multiple-template parsing and execution. import ( "bytes" diff --git a/src/unicode/utf16/utf16.go b/src/unicode/utf16/utf16.go index 276fce9e56..0a5a02ebe2 100644 --- a/src/unicode/utf16/utf16.go +++ b/src/unicode/utf16/utf16.go @@ -45,7 +45,7 @@ func DecodeRune(r1, r2 rune) rune { // If the rune is not a valid Unicode code point or does not need encoding, // EncodeRune returns U+FFFD, U+FFFD. func EncodeRune(r rune) (r1, r2 rune) { - if r < surrSelf || r > maxRune || IsSurrogate(r) { + if r < surrSelf || r > maxRune { return replacementChar, replacementChar } r -= surrSelf @@ -65,20 +65,22 @@ func Encode(s []rune) []uint16 { n = 0 for _, v := range s { switch { - case v < 0, surr1 <= v && v < surr3, v > maxRune: - v = replacementChar - fallthrough - case v < surrSelf: + case 0 <= v && v < surr1, surr3 <= v && v < surrSelf: + // normal rune a[n] = uint16(v) n++ - default: + case surrSelf <= v && v <= maxRune: + // needs surrogate sequence r1, r2 := EncodeRune(v) a[n] = uint16(r1) a[n+1] = uint16(r2) n += 2 + default: + a[n] = uint16(replacementChar) + n++ } } - return a[0:n] + return a[:n] } // Decode returns the Unicode code point sequence represented diff --git a/test/fixedbugs/bug108.go b/test/fixedbugs/bug108.go index 9f2a27ebd9..cfec4c9f1f 100644 --- a/test/fixedbugs/bug108.go +++ b/test/fixedbugs/bug108.go @@ -6,6 +6,6 @@ package main func f() { - v := 1 << 1025; // ERROR "overflow|stupid shift" + v := 1 << 1025; // ERROR "overflow|shift count too large" _ = v } diff --git a/test/fixedbugs/issue11610.go b/test/fixedbugs/issue11610.go index 56f245dee5..f32d480482 100644 --- a/test/fixedbugs/issue11610.go +++ b/test/fixedbugs/issue11610.go @@ -9,7 +9,7 @@ package a import"" // ERROR "import path is empty" -var? // ERROR "unexpected \?" +var? // ERROR "illegal character U\+003F '\?'" var x int // ERROR "unexpected var" "cannot declare name" diff --git a/test/fixedbugs/issue14520.go b/test/fixedbugs/issue14520.go new file mode 100644 index 0000000000..43c48b5c27 --- /dev/null +++ b/test/fixedbugs/issue14520.go @@ -0,0 +1,14 @@ +// errorcheck + +// 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 f + +import /* // ERROR "import path" */ ` +bogus` + +func f(x int /* // ERROR "unexpected semicolon" + +*/)