Merge "[dev.ssa] Merge remote-tracking branch 'origin/master' into ssamerge" into dev.ssa

This commit is contained in:
Gerrit Code Review 2016-02-29 23:19:17 +00:00
commit acdb0da47d
240 changed files with 4681 additions and 3187 deletions

18
.github/ISSUE_TEMPLATE vendored Normal file
View File

@ -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?

7
.github/PULL_REQUEST_TEMPLATE vendored Normal file
View File

@ -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!

1
.gitignore vendored
View File

@ -28,6 +28,7 @@ src/cmd/**/y.output
src/cmd/cgo/zdefaultcc.go src/cmd/cgo/zdefaultcc.go
src/cmd/go/zdefaultcc.go src/cmd/go/zdefaultcc.go
src/cmd/internal/obj/zbootstrap.go src/cmd/internal/obj/zbootstrap.go
src/go/build/zcgo.go
src/go/doc/headscan src/go/doc/headscan
src/runtime/internal/sys/zversion.go src/runtime/internal/sys/zversion.go
src/unicode/maketables src/unicode/maketables

View File

@ -1,14 +1,20 @@
Tools: Tools:
cmd/dist: add list subcommand to list all supported platforms (CL 19837)
cmd/go: GO15VENDOREXPERIMENT gone, assumed on (CL 19615) cmd/go: GO15VENDOREXPERIMENT gone, assumed on (CL 19615)
cmd/link: "-X name value" form gone (CL 19614) 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: 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: 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)

View File

@ -445,6 +445,9 @@ The valid combinations of <code>$GOOS</code> and <code>$GOARCH</code> are:
<th width="50"></th><th align="left" width="100"><code>$GOOS</code></th> <th align="left" width="100"><code>$GOARCH</code></th> <th width="50"></th><th align="left" width="100"><code>$GOOS</code></th> <th align="left" width="100"><code>$GOARCH</code></th>
</tr> </tr>
<tr> <tr>
<td></td><td><code>android</code></td> <td><code>arm</code></td>
</tr>
<tr>
<td></td><td><code>darwin</code></td> <td><code>386</code></td> <td></td><td><code>darwin</code></td> <td><code>386</code></td>
</tr> </tr>
<tr> <tr>

View File

@ -122,6 +122,16 @@ var ptrTests = []ptrTest{
body: `i := 0; p := &S{p:&i}; s := p.a[:]; C.f(unsafe.Pointer(&s[0]))`, body: `i := 0; p := &S{p:&i}; s := p.a[:]; C.f(unsafe.Pointer(&s[0]))`,
fail: false, 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 // Passing the address of a static variable with no
// pointers doesn't matter. // pointers doesn't matter.

View File

@ -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

View File

@ -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())
}

View File

@ -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)
}
}

View File

@ -0,0 +1,3 @@
program HelloWorldF90
write(*,*) "Hello World!"
end program HelloWorldF90

22
misc/cgo/fortran/test.bash Executable file
View File

@ -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

View File

@ -10,7 +10,7 @@ set -e
if [ ! -f src/libgo/libgo.go ]; then if [ ! -f src/libgo/libgo.go ]; then
cwd=$(pwd) 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 exit 1
fi fi

View File

@ -10,7 +10,7 @@
# #
# This script does not handle file names that contain spaces. # 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 [ -z "$gofiles" ] && exit 0
unformatted=$(gofmt -l $gofiles) unformatted=$(gofmt -l $gofiles)

View File

@ -705,7 +705,7 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
} }
} }
if err == io.EOF { 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 { if b.Available() == 0 {
err = b.flush() err = b.flush()
} else { } else {

View File

@ -351,7 +351,7 @@ func TestSplitError(t *testing.T) {
// Test that an EOF is overridden by a user-generated scan error. // Test that an EOF is overridden by a user-generated scan error.
func TestErrAtEOF(t *testing.T) { func TestErrAtEOF(t *testing.T) {
s := NewScanner(strings.NewReader("1 2 33")) 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) { split := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
advance, token, err = ScanWords(data, atEOF) advance, token, err = ScanWords(data, atEOF)
if len(token) > 1 { if len(token) > 1 {

View File

@ -32,13 +32,13 @@ if [ "$pattern" = "" ]; then
pattern=. pattern=.
fi 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 ./make.bash || exit 1
GOROOT="$(cd .. && pwd)" 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 failed=false
for target in $targets for target in $targets
do do

View File

@ -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) { func TestIndexRune(t *testing.T) {
for _, tt := range indexRuneTests { for _, tt := range indexRuneTests {
a := []byte(tt.a) a := []byte(tt.a)
@ -348,10 +383,12 @@ func TestIndexRune(t *testing.T) {
var bmbuf []byte var bmbuf []byte
func BenchmarkIndexByte10(b *testing.B) { bmIndexByte(b, IndexByte, 10) }
func BenchmarkIndexByte32(b *testing.B) { bmIndexByte(b, IndexByte, 32) } func BenchmarkIndexByte32(b *testing.B) { bmIndexByte(b, IndexByte, 32) }
func BenchmarkIndexByte4K(b *testing.B) { bmIndexByte(b, IndexByte, 4<<10) } func BenchmarkIndexByte4K(b *testing.B) { bmIndexByte(b, IndexByte, 4<<10) }
func BenchmarkIndexByte4M(b *testing.B) { bmIndexByte(b, IndexByte, 4<<20) } func BenchmarkIndexByte4M(b *testing.B) { bmIndexByte(b, IndexByte, 4<<20) }
func BenchmarkIndexByte64M(b *testing.B) { bmIndexByte(b, IndexByte, 64<<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 BenchmarkIndexBytePortable32(b *testing.B) { bmIndexByte(b, IndexBytePortable, 32) }
func BenchmarkIndexBytePortable4K(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<10) } func BenchmarkIndexBytePortable4K(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<10) }
func BenchmarkIndexBytePortable4M(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<20) } func BenchmarkIndexBytePortable4M(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<20) }

View File

@ -20,7 +20,7 @@ The GOOS and GOARCH environment variables set the desired target.
Flags: Flags:
-D value -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 can be set multiple times
-I value -I value
include directory; can be set multiple times include directory; can be set multiple times

View File

@ -179,7 +179,7 @@ Diff:
t.Errorf(format, args...) t.Errorf(format, args...)
ok = false ok = false
} }
obj.Flushplist(ctxt) obj.FlushplistNoFree(ctxt)
for p := top; p != nil; p = p.Link { for p := top; p != nil; p = p.Link {
if p.As == obj.ATEXT { if p.As == obj.ATEXT {

View File

@ -297,7 +297,7 @@ func (p *Parser) operand(a *obj.Addr) bool {
p.errorf("illegal use of register list") p.errorf("illegal use of register list")
} }
p.registerList(a) p.registerList(a)
p.expect(scanner.EOF) p.expectOperandEnd()
return true return true
} }
@ -331,7 +331,7 @@ func (p *Parser) operand(a *obj.Addr) bool {
} }
} }
// fmt.Printf("REG %s\n", obj.Dconv(&emptyProg, 0, a)) // fmt.Printf("REG %s\n", obj.Dconv(&emptyProg, 0, a))
p.expect(scanner.EOF) p.expectOperandEnd()
return true return true
} }
@ -363,7 +363,7 @@ func (p *Parser) operand(a *obj.Addr) bool {
a.Type = obj.TYPE_FCONST a.Type = obj.TYPE_FCONST
a.Val = p.floatExpr() a.Val = p.floatExpr()
// fmt.Printf("FCONST %s\n", obj.Dconv(&emptyProg, 0, a)) // fmt.Printf("FCONST %s\n", obj.Dconv(&emptyProg, 0, a))
p.expect(scanner.EOF) p.expectOperandEnd()
return true return true
} }
if p.have(scanner.String) { if p.have(scanner.String) {
@ -378,7 +378,7 @@ func (p *Parser) operand(a *obj.Addr) bool {
a.Type = obj.TYPE_SCONST a.Type = obj.TYPE_SCONST
a.Val = str a.Val = str
// fmt.Printf("SCONST %s\n", obj.Dconv(&emptyProg, 0, a)) // fmt.Printf("SCONST %s\n", obj.Dconv(&emptyProg, 0, a))
p.expect(scanner.EOF) p.expectOperandEnd()
return true return true
} }
a.Offset = int64(p.expr()) a.Offset = int64(p.expr())
@ -392,7 +392,7 @@ func (p *Parser) operand(a *obj.Addr) bool {
a.Type = obj.TYPE_MEM a.Type = obj.TYPE_MEM
} }
// fmt.Printf("CONST %d %s\n", a.Offset, obj.Dconv(&emptyProg, 0, a)) // fmt.Printf("CONST %d %s\n", a.Offset, obj.Dconv(&emptyProg, 0, a))
p.expect(scanner.EOF) p.expectOperandEnd()
return true return true
} }
// fmt.Printf("offset %d \n", a.Offset) // fmt.Printf("offset %d \n", a.Offset)
@ -402,7 +402,7 @@ func (p *Parser) operand(a *obj.Addr) bool {
p.registerIndirect(a, prefix) p.registerIndirect(a, prefix)
// fmt.Printf("DONE %s\n", p.arch.Dconv(&emptyProg, 0, a)) // fmt.Printf("DONE %s\n", p.arch.Dconv(&emptyProg, 0, a))
p.expect(scanner.EOF) p.expectOperandEnd()
return true return true
} }
@ -983,14 +983,19 @@ func (p *Parser) more() bool {
// get verifies that the next item has the expected type and returns it. // get verifies that the next item has the expected type and returns it.
func (p *Parser) get(expected lex.ScanToken) lex.Token { func (p *Parser) get(expected lex.ScanToken) lex.Token {
p.expect(expected) p.expect(expected, expected.String())
return p.next() 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. // expect verifies that the next item has the expected type. It does not consume it.
func (p *Parser) expect(expected lex.ScanToken) { func (p *Parser) expect(expectedToken lex.ScanToken, expectedMessage string) {
if p.peek() != expected { if p.peek() != expectedToken {
p.errorf("expected %s, found %s", expected, p.next()) p.errorf("expected %s, found %s", expectedMessage, p.next())
} }
} }

View File

@ -35,7 +35,7 @@ func TestErroneous(t *testing.T) {
{"TEXT", "%", "expect two or three operands for TEXT"}, {"TEXT", "%", "expect two or three operands for TEXT"},
{"TEXT", "1, 1", "TEXT symbol \"<erroneous symbol>\" must be a symbol(SB)"}, {"TEXT", "1, 1", "TEXT symbol \"<erroneous symbol>\" must be a symbol(SB)"},
{"TEXT", "$\"foo\", 0, $1", "TEXT symbol \"<erroneous symbol>\" must be a symbol(SB)"}, {"TEXT", "$\"foo\", 0, $1", "TEXT symbol \"<erroneous symbol>\" must be a symbol(SB)"},
{"TEXT", "$0É:0, 0, $1", "expected EOF, found É"}, // Issue #12467. {"TEXT", "$0É:0, 0, $1", "expected end of operand, found É"}, // Issue #12467.
{"TEXT", "$:0:(SB, 0, $1", "expected '(', found 0"}, // Issue 12468. {"TEXT", "$:0:(SB, 0, $1", "expected '(', found 0"}, // Issue 12468.
{"FUNCDATA", "", "expect two operands for FUNCDATA"}, {"FUNCDATA", "", "expect two operands for FUNCDATA"},
{"FUNCDATA", "(SB ", "expect two operands for FUNCDATA"}, {"FUNCDATA", "(SB ", "expect two operands for FUNCDATA"},

View File

@ -237,7 +237,7 @@ again:
// //
// LSTXR reg ',' addr ',' reg // LSTXR reg ',' addr ',' reg
// { // {
// outtcode($1, &$2, &$4, &$6); // outcode($1, &$2, &$4, &$6);
// } // }
LDAXRW (R0), R2 LDAXRW (R0), R2
STLXRW R1, (R0), R3 STLXRW R1, (R0), R3

View File

@ -29,7 +29,7 @@ var (
) )
func init() { 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") flag.Var(&I, "I", "include directory; can be set multiple times")
} }

View File

@ -30,7 +30,7 @@ type Input struct {
peekText string peekText string
} }
// NewInput returns a // NewInput returns an Input from the given path.
func NewInput(name string) *Input { func NewInput(name string) *Input {
return &Input{ return &Input{
// include directories: look in source dir, then -I directories. // include directories: look in source dir, then -I directories.

View File

@ -77,7 +77,7 @@ func NewLexer(name string, ctxt *obj.Link) TokenReader {
input := NewInput(name) input := NewInput(name)
fd, err := os.Open(name) fd, err := os.Open(name)
if err != nil { if err != nil {
log.Fatalf("asm: %s\n", err) log.Fatalf("%s\n", err)
} }
input.Push(NewTokenizer(name, fd, fd)) input.Push(NewTokenizer(name, fd, fd))
return input return input

View File

@ -26,7 +26,7 @@ func main() {
architecture := arch.Set(GOARCH) architecture := arch.Set(GOARCH)
if architecture == nil { if architecture == nil {
log.Fatalf("asm: unrecognized architecture %s", GOARCH) log.Fatalf("unrecognized architecture %s", GOARCH)
} }
flags.Parse() flags.Parse()
@ -66,7 +66,7 @@ func main() {
obj.Writeobjdirect(ctxt, output) obj.Writeobjdirect(ctxt, output)
} }
if !ok || diag { 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.Remove(*flags.OutputFile)
os.Exit(1) os.Exit(1)
} }

View File

@ -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: "C? Go? Cgo!" for an introduction to using cgo:
https://golang.org/doc/articles/c_go_cgo.html. https://golang.org/doc/articles/c_go_cgo.html.
CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS may be defined with pseudo #cgo CFLAGS, CPPFLAGS, CXXFLAGS, FFLAGS and LDFLAGS may be defined with pseudo
directives within these comments to tweak the behavior of the C or C++ #cgo directives within these comments to tweak the behavior of the C, C++
compiler. Values defined in multiple directives are concatenated or Fortran compiler. Values defined in multiple directives are concatenated
together. The directive can include a list of build constraints limiting its together. The directive can include a list of build constraints limiting its
effect to systems satisfying one of the constraints effect to systems satisfying one of the constraints
(see https://golang.org/pkg/go/build/#hdr-Build_Constraints for details about the constraint syntax). (see https://golang.org/pkg/go/build/#hdr-Build_Constraints for details about the constraint syntax).
@ -53,7 +53,7 @@ For example:
// #include <png.h> // #include <png.h>
import "C" 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 CGO_LDFLAGS environment variables are added to the flags derived from
these directives. Package-specific flags should be set using the these directives. Package-specific flags should be set using the
directives, not the environment variables, so that builds work in 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 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 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 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 package. All the CPPFLAGS and FFLAGS directives in a package are concatenated
concatenated and used at link time. All the pkg-config directives are and used to compile Fortran files in that package. All the LDFLAGS directives
concatenated and sent to pkg-config simultaneously to add to each appropriate in any package in the program are concatenated and used at link time. All the
set of command-line flags. 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} 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 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 "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 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 .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, not be compiled separately, but, if these header files are changed,
the C and C++ files will be recompiled. The default C and C++ the C and C++ files will be recompiled. The default C and C++
compilers may be changed by the CC and CXX environment variables, compilers may be changed by the CC and CXX environment variables,

View File

@ -819,14 +819,17 @@ func (p *Package) hasSideEffects(f *File, x ast.Expr) bool {
func (p *Package) isType(t ast.Expr) bool { func (p *Package) isType(t ast.Expr) bool {
switch t := t.(type) { switch t := t.(type) {
case *ast.SelectorExpr: case *ast.SelectorExpr:
if t.Sel.Name != "Pointer" {
return false
}
id, ok := t.X.(*ast.Ident) id, ok := t.X.(*ast.Ident)
if !ok { if !ok {
return false 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: case *ast.Ident:
// TODO: This ignores shadowing. // TODO: This ignores shadowing.
switch t.Name { switch t.Name {

View File

@ -10,9 +10,10 @@ import (
"cmd/internal/obj/x86" "cmd/internal/obj/x86"
) )
func defframe(ptxt *obj.Prog) { // no floating point in note handlers on Plan 9
var n *gc.Node var isPlan9 = obj.Getgoos() == "plan9"
func defframe(ptxt *obj.Prog) {
// fill in argument size, stack size // fill in argument size, stack size
ptxt.To.Type = obj.TYPE_TEXTSIZE ptxt.To.Type = obj.TYPE_TEXTSIZE
@ -31,8 +32,7 @@ func defframe(ptxt *obj.Prog) {
x0 := uint32(0) x0 := uint32(0)
// iterate through declarations - they are sorted in decreasing xoffset order. // iterate through declarations - they are sorted in decreasing xoffset order.
for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next { for _, n := range gc.Curfn.Func.Dcl {
n = l.N
if !n.Name.Needzero { if !n.Name.Needzero {
continue continue
} }
@ -126,7 +126,7 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32, x0 *uin
*ax = 1 *ax = 1
} }
p = appendpp(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo) 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 { if *x0 == 0 {
p = appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0) p = appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
*x0 = 1 *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 { 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)) 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 { if *x0 == 0 {
p = appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0) p = appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
*x0 = 1 *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, 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 = 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)) p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg))
@ -563,7 +562,7 @@ func clearfat(nl *gc.Node) {
w := nl.Type.Width w := nl.Type.Width
if w > 1024 || (gc.Nacl && w >= 64) { if w > 1024 || (w >= 64 && (gc.Nacl || isPlan9)) {
var oldn1 gc.Node var oldn1 gc.Node
var n1 gc.Node var n1 gc.Node
savex(x86.REG_DI, &n1, &oldn1, nil, gc.Types[gc.Tptr]) 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) { 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 { if b >= 16 {
var vec_zero gc.Node var vec_zero gc.Node
gc.Regalloc(&vec_zero, gc.Types[gc.TFLOAT64], nil) gc.Regalloc(&vec_zero, gc.Types[gc.TFLOAT64], nil)

View File

@ -427,7 +427,7 @@ func elimshortmov(g *gc.Graph) {
} }
if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST { 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. // from another register or constant can be movl.
// we don't switch to 64-bit arithmetic if it can // we don't switch to 64-bit arithmetic if it can
// change how the carry bit is set (and the carry bit is needed). // change how the carry bit is set (and the carry bit is needed).

View File

@ -11,8 +11,6 @@ import (
) )
func defframe(ptxt *obj.Prog) { func defframe(ptxt *obj.Prog) {
var n *gc.Node
// fill in argument size, stack size // fill in argument size, stack size
ptxt.To.Type = obj.TYPE_TEXTSIZE ptxt.To.Type = obj.TYPE_TEXTSIZE
@ -28,8 +26,7 @@ func defframe(ptxt *obj.Prog) {
hi := int64(0) hi := int64(0)
lo := hi lo := hi
r0 := uint32(0) r0 := uint32(0)
for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next { for _, n := range gc.Curfn.Func.Dcl {
n = l.N
if !n.Name.Needzero { if !n.Name.Needzero {
continue continue
} }

View File

@ -12,8 +12,6 @@ import (
) )
func defframe(ptxt *obj.Prog) { func defframe(ptxt *obj.Prog) {
var n *gc.Node
// fill in argument size, stack size // fill in argument size, stack size
ptxt.To.Type = obj.TYPE_TEXTSIZE ptxt.To.Type = obj.TYPE_TEXTSIZE
@ -37,8 +35,7 @@ func defframe(ptxt *obj.Prog) {
lo := hi lo := hi
// iterate through declarations - they are sorted in decreasing xoffset order. // iterate through declarations - they are sorted in decreasing xoffset order.
for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next { for _, n := range gc.Curfn.Func.Dcl {
n = l.N
if !n.Name.Needzero { if !n.Name.Needzero {
continue 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. // The hardware will generate undefined result.
// Also need to explicitly trap on division on zero, // Also need to explicitly trap on division on zero,
// the hardware will silently generate undefined result. // 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. // so always use DIVD/DIVDU.
t := nl.Type t := nl.Type

View File

@ -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
}

View File

@ -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. 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 and position information in the encoding. This mechanism permits an importer
to recognize immediately when it is out of sync. The importer recognizes this 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 mode automatically (i.e., it can import export data produced with debugging

View File

@ -240,10 +240,9 @@ func (p *importer) typ() *Type {
{ {
saved := structpkg saved := structpkg
structpkg = tsym.Pkg structpkg = tsym.Pkg
addmethod(sym, n.Type, false, nointerface) addmethod(sym, n.Type, false, false)
structpkg = saved structpkg = saved
} }
nointerface = false
funchdr(n) funchdr(n)
// (comment from go.y) // (comment from go.y)

View File

@ -781,7 +781,7 @@ var sys_wbptr *Node
func cgen_wbptr(n, res *Node) { func cgen_wbptr(n, res *Node) {
if Curfn != nil { if Curfn != nil {
if Curfn.Func.Nowritebarrier { if Curfn.Func.Pragma&Nowritebarrier != 0 {
Yyerror("write barrier prohibited") Yyerror("write barrier prohibited")
} }
if Curfn.Func.WBLineno == 0 { if Curfn.Func.WBLineno == 0 {
@ -831,7 +831,7 @@ func cgen_wbptr(n, res *Node) {
func cgen_wbfat(n, res *Node) { func cgen_wbfat(n, res *Node) {
if Curfn != nil { if Curfn != nil {
if Curfn.Func.Nowritebarrier { if Curfn.Func.Pragma&Nowritebarrier != 0 {
Yyerror("write barrier prohibited") Yyerror("write barrier prohibited")
} }
if Curfn.Func.WBLineno == 0 { 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 // If copying .args, that's all the results, so record definition sites
// for them for the liveness analysis. // for them for the liveness analysis.
if ns.Op == ONAME && ns.Sym.Name == ".args" { if ns.Op == ONAME && ns.Sym.Name == ".args" {
for l := Curfn.Func.Dcl; l != nil; l = l.Next { for _, ln := range Curfn.Func.Dcl {
if l.N.Class == PPARAMOUT { if ln.Class == PPARAMOUT {
Gvardef(l.N) Gvardef(ln)
} }
} }
} }
@ -2621,7 +2621,7 @@ func cgen_ret(n *Node) {
if hasdefer { if hasdefer {
Ginscall(Deferreturn, 0) Ginscall(Deferreturn, 0)
} }
Genlist(Curfn.Func.Exit) Genslice(Curfn.Func.Exit.Slice())
p := Thearch.Gins(obj.ARET, nil, nil) p := Thearch.Gins(obj.ARET, nil, nil)
if n != nil && n.Op == ORETJMP { if n != nil && n.Op == ORETJMP {
p.To.Type = obj.TYPE_MEM p.To.Type = obj.TYPE_MEM

View File

@ -67,9 +67,7 @@ func closurebody(body *NodeList) *Node {
// ordinary ones in the symbol table; see oldname. // ordinary ones in the symbol table; see oldname.
// unhook them. // unhook them.
// make the list of pointers for the closure call. // make the list of pointers for the closure call.
var v *Node for _, v := range func_.Func.Cvars.Slice() {
for l := func_.Func.Cvars; l != nil; l = l.Next {
v = l.N
v.Name.Param.Closure.Name.Param.Closure = v.Name.Param.Outer v.Name.Param.Closure.Name.Param.Closure = v.Name.Param.Outer
v.Name.Param.Outerexpr = oldname(v.Sym) v.Name.Param.Outerexpr = oldname(v.Sym)
} }
@ -78,10 +76,8 @@ func closurebody(body *NodeList) *Node {
} }
func typecheckclosure(func_ *Node, top int) { func typecheckclosure(func_ *Node, top int) {
var n *Node for _, ln := range func_.Func.Cvars.Slice() {
n := ln.Name.Param.Closure
for l := func_.Func.Cvars; l != nil; l = l.Next {
n = l.N.Name.Param.Closure
if !n.Name.Captured { if !n.Name.Captured {
n.Name.Captured = true n.Name.Captured = true
if n.Name.Decldepth == 0 { 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 { for _, ln := range func_.Func.Dcl {
if l.N.Op == ONAME && (l.N.Class == PPARAM || l.N.Class == PPARAMOUT) { if ln.Op == ONAME && (ln.Class == PPARAM || ln.Class == PPARAMOUT) {
l.N.Name.Decldepth = 1 ln.Name.Decldepth = 1
} }
} }
@ -198,7 +194,8 @@ func makeclosure(func_ *Node) *Node {
makefuncsym(xfunc.Func.Nname.Sym) makefuncsym(xfunc.Func.Nname.Sym)
xfunc.Nbody = func_.Nbody 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 { if xfunc.Nbody == nil {
Fatalf("empty body - won't generate any code") 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 // We use value capturing for values <= 128 bytes that are never reassigned
// after capturing (effectively constant). // after capturing (effectively constant).
func capturevars(xfunc *Node) { func capturevars(xfunc *Node) {
var v *Node
var outer *Node var outer *Node
lno := int(lineno) lno := int(lineno)
lineno = xfunc.Lineno lineno = xfunc.Lineno
func_ := xfunc.Func.Closure func_ := xfunc.Func.Closure
func_.Func.Enter = nil func_.Func.Enter.Set(nil)
for l := func_.Func.Cvars; l != nil; l = l.Next { for _, v := range func_.Func.Cvars.Slice() {
v = l.N
if v.Type == nil { if v.Type == nil {
// if v->type is nil, it means v looked like it was // if v->type is nil, it means v looked like it was
// going to be used in the closure but wasn't. // going to be used in the closure but wasn't.
@ -270,7 +265,7 @@ func capturevars(xfunc *Node) {
} }
typecheck(&outer, Erv) typecheck(&outer, Erv)
func_.Func.Enter = list(func_.Func.Enter, outer) func_.Func.Enter.Append(outer)
} }
lineno = int32(lno) lineno = int32(lno)
@ -309,11 +304,9 @@ func transformclosure(xfunc *Node) {
original_dcl := xfunc.Func.Dcl original_dcl := xfunc.Func.Dcl
xfunc.Func.Dcl = nil xfunc.Func.Dcl = nil
var v *Node
var addr *Node var addr *Node
var fld *Type var fld *Type
for l := func_.Func.Cvars; l != nil; l = l.Next { for _, v := range func_.Func.Cvars.Slice() {
v = l.N
if v.Op == OXXX { if v.Op == OXXX {
continue continue
} }
@ -341,13 +334,13 @@ func transformclosure(xfunc *Node) {
fld.Sym = fld.Nname.Sym fld.Sym = fld.Nname.Sym
// Declare the new param and add it the first part of the input arguments. // 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
param = &fld.Down param = &fld.Down
} }
*param = original_args *param = original_args
xfunc.Func.Dcl = concat(xfunc.Func.Dcl, original_dcl) xfunc.Func.Dcl = append(xfunc.Func.Dcl, original_dcl...)
// Recalculate param offsets. // Recalculate param offsets.
if f.Type.Width > 0 { if f.Type.Width > 0 {
@ -357,19 +350,14 @@ func transformclosure(xfunc *Node) {
xfunc.Type = f.Type // update type of ODCLFUNC xfunc.Type = f.Type // update type of ODCLFUNC
} else { } else {
// The closure is not called, so it is going to stay as closure. // The closure is not called, so it is going to stay as closure.
nvar := 0 var body []*Node
var body *NodeList
offset := int64(Widthptr) offset := int64(Widthptr)
var addr *Node var addr *Node
var v *Node
var cv *Node var cv *Node
for l := func_.Func.Cvars; l != nil; l = l.Next { for _, v := range func_.Func.Cvars.Slice() {
v = l.N
if v.Op == OXXX { if v.Op == OXXX {
continue continue
} }
nvar++
// cv refers to the field inside of closure OSTRUCTLIT. // cv refers to the field inside of closure OSTRUCTLIT.
cv = Nod(OCLOSUREVAR, nil, nil) 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. // If it is a small variable captured by value, downgrade it to PAUTO.
v.Class = PAUTO v.Class = PAUTO
v.Ullman = 1 v.Ullman = 1
xfunc.Func.Dcl = list(xfunc.Func.Dcl, v) xfunc.Func.Dcl = append(xfunc.Func.Dcl, v)
body = list(body, Nod(OAS, v, cv)) body = append(body, Nod(OAS, v, cv))
} else { } else {
// Declare variable holding addresses taken from closure // Declare variable holding addresses taken from closure
// and initialize in entry prologue. // and initialize in entry prologue.
@ -396,19 +384,21 @@ func transformclosure(xfunc *Node) {
addr.Class = PAUTO addr.Class = PAUTO
addr.Used = true addr.Used = true
addr.Name.Curfn = xfunc addr.Name.Curfn = xfunc
xfunc.Func.Dcl = list(xfunc.Func.Dcl, addr) xfunc.Func.Dcl = append(xfunc.Func.Dcl, addr)
v.Name.Heapaddr = addr v.Name.Heapaddr = addr
if v.Name.Byval { if v.Name.Byval {
cv = Nod(OADDR, cv, nil) cv = Nod(OADDR, cv, nil)
} }
body = list(body, Nod(OAS, addr, cv)) body = append(body, Nod(OAS, addr, cv))
} }
} }
typechecklist(body, Etop) if len(body) > 0 {
walkstmtlist(body) typecheckslice(body, Etop)
xfunc.Func.Enter = body walkstmtslice(body)
xfunc.Func.Needctxt = nvar > 0 xfunc.Func.Enter.Set(body)
xfunc.Func.Needctxt = true
}
} }
lineno = int32(lno) lineno = int32(lno)
@ -416,7 +406,7 @@ func transformclosure(xfunc *Node) {
func walkclosure(func_ *Node, init **NodeList) *Node { func walkclosure(func_ *Node, init **NodeList) *Node {
// If no closure vars, don't bother wrapping. // 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 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]))) typ.List = list1(Nod(ODCLFIELD, newname(Lookup(".F")), typenod(Types[TUINTPTR])))
var typ1 *Node var typ1 *Node
var v *Node for _, v := range func_.Func.Cvars.Slice() {
for l := func_.Func.Cvars; l != nil; l = l.Next {
v = l.N
if v.Op == OXXX { if v.Op == OXXX {
continue continue
} }
@ -454,7 +442,7 @@ func walkclosure(func_ *Node, init **NodeList) *Node {
clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil)) clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil))
clos.Esc = func_.Esc clos.Esc = func_.Esc
clos.Right.Implicit = true 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. // Force type conversion from *struct to the func type.
clos = Nod(OCONVNOP, clos, nil) clos = Nod(OCONVNOP, clos, nil)
@ -551,7 +539,7 @@ func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node {
n = newname(Lookupf("a%d", i)) n = newname(Lookupf("a%d", i))
i++ i++
n.Class = PPARAM n.Class = PPARAM
xfunc.Func.Dcl = list(xfunc.Func.Dcl, n) xfunc.Func.Dcl = append(xfunc.Func.Dcl, n)
callargs = list(callargs, n) callargs = list(callargs, n)
fld = Nod(ODCLFIELD, n, typenod(t.Type)) fld = Nod(ODCLFIELD, n, typenod(t.Type))
if t.Isddd { if t.Isddd {
@ -570,7 +558,7 @@ func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node {
n = newname(Lookupf("r%d", i)) n = newname(Lookupf("r%d", i))
i++ i++
n.Class = PPARAMOUT n.Class = PPARAMOUT
xfunc.Func.Dcl = list(xfunc.Func.Dcl, n) xfunc.Func.Dcl = append(xfunc.Func.Dcl, n)
retargs = list(retargs, n) retargs = list(retargs, n)
l = list(l, Nod(ODCLFIELD, n, typenod(t.Type))) 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.Used = true
ptr.Name.Curfn = xfunc ptr.Name.Curfn = xfunc
ptr.Xoffset = 0 ptr.Xoffset = 0
xfunc.Func.Dcl = list(xfunc.Func.Dcl, ptr) xfunc.Func.Dcl = append(xfunc.Func.Dcl, ptr)
var body *NodeList var body *NodeList
if Isptr[rcvrtype.Etype] || Isinter(rcvrtype) { if Isptr[rcvrtype.Etype] || Isinter(rcvrtype) {
ptr.Name.Param.Ntype = typenod(rcvrtype) ptr.Name.Param.Ntype = typenod(rcvrtype)

View File

@ -187,7 +187,7 @@ func declare(n *Node, ctxt Class) {
Fatalf("automatic outside function") Fatalf("automatic outside function")
} }
if Curfn != nil { if Curfn != nil {
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
} }
if n.Op == OTYPE { if n.Op == OTYPE {
declare_typegen++ declare_typegen++
@ -426,7 +426,7 @@ func oldname(s *Sym) *Node {
n.Name.Param.Closure = c n.Name.Param.Closure = c
c.Name.Param.Closure = n c.Name.Param.Closure = n
c.Xoffset = 0 c.Xoffset = 0
Curfn.Func.Cvars = list(Curfn.Func.Cvars, c) Curfn.Func.Cvars.Append(c)
} }
// return ref to closure var, not original // return ref to closure var, not original
@ -1449,8 +1449,13 @@ func funccompile(n *Node) {
Funcdepth = n.Func.Depth + 1 Funcdepth = n.Func.Depth + 1
compile(n) compile(n)
Curfn = nil Curfn = nil
Pc = nil
continpc = nil
breakpc = nil
Funcdepth = 0 Funcdepth = 0
dclcontext = PEXTERN dclcontext = PEXTERN
flushdata()
obj.Flushplist(Ctxt) // convert from Prog list to machine code
} }
func funcsym(s *Sym) *Sym { func funcsym(s *Sym) *Sym {
@ -1525,7 +1530,7 @@ func checknowritebarrierrec() {
// Check nowritebarrierrec functions. // Check nowritebarrierrec functions.
for _, n := range list { for _, n := range list {
if !n.Func.Nowritebarrierrec { if n.Func.Pragma&Nowritebarrierrec == 0 {
continue continue
} }
call, hasWB := c.best[n] call, hasWB := c.best[n]

View File

@ -476,35 +476,35 @@ func escfunc(e *EscState, func_ *Node) {
savefn := Curfn savefn := Curfn
Curfn = func_ Curfn = func_
for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { for _, ln := range Curfn.Func.Dcl {
if ll.N.Op != ONAME { if ln.Op != ONAME {
continue continue
} }
llNE := e.nodeEscState(ll.N) llNE := e.nodeEscState(ln)
switch ll.N.Class { switch ln.Class {
// out params are in a loopdepth between the sink and all local variables // out params are in a loopdepth between the sink and all local variables
case PPARAMOUT: case PPARAMOUT:
llNE.Escloopdepth = 0 llNE.Escloopdepth = 0
case PPARAM: case PPARAM:
llNE.Escloopdepth = 1 llNE.Escloopdepth = 1
if ll.N.Type != nil && !haspointers(ll.N.Type) { if ln.Type != nil && !haspointers(ln.Type) {
break break
} }
if Curfn.Nbody == nil && !Curfn.Noescape { if Curfn.Nbody == nil && !Curfn.Noescape {
ll.N.Esc = EscHeap ln.Esc = EscHeap
} else { } 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 // in a mutually recursive group we lose track of the return values
if e.recursive { if e.recursive {
for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { for _, ln := range Curfn.Func.Dcl {
if ll.N.Op == ONAME && ll.N.Class == PPARAMOUT { if ln.Op == ONAME && ln.Class == PPARAMOUT {
escflows(e, &e.theSink, ll.N) escflows(e, &e.theSink, ln)
} }
} }
} }
@ -779,11 +779,14 @@ func esc(e *EscState, n *Node, up *Node) {
ll = e.nodeEscState(n.List.N).Escretval ll = e.nodeEscState(n.List.N).Escretval
} }
for lr := Curfn.Func.Dcl; lr != nil && ll != nil; lr = lr.Next { for _, lrn := range Curfn.Func.Dcl {
if lr.N.Op != ONAME || lr.N.Class != PPARAMOUT { if ll == nil {
break
}
if lrn.Op != ONAME || lrn.Class != PPARAMOUT {
continue continue
} }
escassign(e, lr.N, ll.N) escassign(e, lrn, ll.N)
ll = ll.Next ll = ll.Next
} }
@ -861,9 +864,7 @@ func esc(e *EscState, n *Node, up *Node) {
// Link addresses of captured variables to closure. // Link addresses of captured variables to closure.
case OCLOSURE: case OCLOSURE:
var a *Node var a *Node
var v *Node for _, v := range n.Func.Cvars.Slice() {
for ll := n.Func.Cvars; ll != nil; ll = ll.Next {
v = ll.N
if v.Op == OXXX { // unnamed out argument; see dcl.go:/^funcargs if v.Op == OXXX { // unnamed out argument; see dcl.go:/^funcargs
continue continue
} }
@ -1870,16 +1871,16 @@ func esctag(e *EscState, func_ *Node) {
savefn := Curfn savefn := Curfn
Curfn = func_ Curfn = func_
for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { for _, ln := range Curfn.Func.Dcl {
if ll.N.Op != ONAME { if ln.Op != ONAME {
continue continue
} }
switch ll.N.Esc & EscMask { switch ln.Esc & EscMask {
case EscNone, // not touched by escflood case EscNone, // not touched by escflood
EscReturn: EscReturn:
if haspointers(ll.N.Type) { // don't bother tagging for scalars if haspointers(ln.Type) { // don't bother tagging for scalars
ll.N.Name.Param.Field.Note = mktag(int(ll.N.Esc)) ln.Name.Param.Field.Note = mktag(int(ln.Esc))
} }
case EscHeap, // touched by escflood, moved to heap case EscHeap, // touched by escflood, moved to heap

View File

@ -1730,10 +1730,42 @@ func Hconv(l *NodeList, flag int) string {
return buf.String() return buf.String()
} }
func Hconvslice(l []*Node, flag int) string {
if len(l) == 0 && fmtmode == FDbg {
return "<nil>"
}
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) { func dumplist(s string, l *NodeList) {
fmt.Printf("%s%v\n", s, Hconv(l, obj.FmtSign)) 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) { func Dump(s string, n *Node) {
fmt.Printf("%s [%p]%v\n", s, n, Nconv(n, obj.FmtSign)) fmt.Printf("%s [%p]%v\n", s, n, Nconv(n, obj.FmtSign))
} }

View File

@ -76,6 +76,9 @@ func addrescapes(n *Node) {
oldfn := Curfn oldfn := Curfn
Curfn = n.Name.Curfn Curfn = n.Name.Curfn
if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE {
Curfn = Curfn.Func.Closure
}
n.Name.Heapaddr = temp(Ptrto(n.Type)) n.Name.Heapaddr = temp(Ptrto(n.Type))
buf := fmt.Sprintf("&%v", n.Sym) buf := fmt.Sprintf("&%v", n.Sym)
n.Name.Heapaddr.Sym = Lookup(buf) 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. // generate code to start new proc running call n.
func cgen_proc(n *Node, proc int) { func cgen_proc(n *Node, proc int) {
switch n.Left.Op { switch n.Left.Op {
@ -587,6 +596,10 @@ func Tempname(nn *Node, t *Type) {
if Curfn == nil { if Curfn == nil {
Fatalf("no curfn for tempname") 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 { if t == nil {
Yyerror("tempname called with nil type") Yyerror("tempname called with nil type")
@ -606,7 +619,7 @@ func Tempname(nn *Node, t *Type) {
n.Ullman = 1 n.Ullman = 1
n.Esc = EscNever n.Esc = EscNever
n.Name.Curfn = Curfn n.Name.Curfn = Curfn
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
dowidth(t) dowidth(t)
n.Xoffset = 0 n.Xoffset = 0

View File

@ -11,7 +11,8 @@ import (
"log" "log"
"os" "os"
"os/exec" "os/exec"
"path" "path/filepath"
"strings"
"testing" "testing"
) )
@ -28,7 +29,7 @@ func TestScanfRemoval(t *testing.T) {
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
// Create source. // Create source.
src := path.Join(dir, "test.go") src := filepath.Join(dir, "test.go")
f, err := os.Create(src) f, err := os.Create(src)
if err != nil { if err != nil {
log.Fatalf("could not create source file: %v", err) log.Fatalf("could not create source file: %v", err)
@ -43,7 +44,7 @@ func main() {
f.Close() f.Close()
// Name of destination. // Name of destination.
dst := path.Join(dir, "test") dst := filepath.Join(dir, "test")
// Compile source. // Compile source.
cmd := exec.Command("go", "build", "-o", dst, src) cmd := exec.Command("go", "build", "-o", dst, src)
@ -62,3 +63,53 @@ func main() {
log.Fatalf("scanf code not removed from helloworld") 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)
}
}
}

View File

@ -26,25 +26,6 @@ const (
MaxStackVarSize = 10 * 1024 * 1024 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 ( const (
// Maximum size in bits for Mpints before signalling // Maximum size in bits for Mpints before signalling
// overflow and also mantissa precision for Mpflts. // overflow and also mantissa precision for Mpflts.
@ -122,10 +103,9 @@ type Pkg struct {
} }
type Sym struct { type Sym struct {
Lexical uint16
Flags uint8 Flags uint8
Link *Sym
Uniqgen uint32 Uniqgen uint32
Link *Sym
Importdef *Pkg // where imported definition was found Importdef *Pkg // where imported definition was found
Linkname string // link name 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 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 var lexlineno int32
// lineno is the line number at the start of the most recently lexed token.
var lineno int32 var lineno int32
var prevlineno int32
var pragcgobuf string var pragcgobuf string
var infile string var infile string
@ -616,23 +597,10 @@ var flag_largemodel int
// when the race detector is enabled. // when the race detector is enabled.
var instrumenting bool 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 debuglive int
var Ctxt *obj.Link var Ctxt *obj.Link
var nointerface bool
var writearchive int var writearchive int
var bstdout obj.Biobuf var bstdout obj.Biobuf

View File

@ -173,6 +173,18 @@ func dumpdata() {
Clearp(Pc) 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. // Fixup instructions after allocauto (formerly compactframe) has moved all autos around.
func fixautoused(p *obj.Prog) { func fixautoused(p *obj.Prog) {
for lp := &p; ; { for lp := &p; ; {
@ -554,9 +566,7 @@ func nodarg(t *Type, fp int) *Node {
} }
if fp == 1 || fp == -1 { if fp == 1 || fp == -1 {
var n *Node for _, n := range Curfn.Func.Dcl {
for l := Curfn.Func.Dcl; l != nil; l = l.Next {
n = l.N
if (n.Class == PPARAM || n.Class == PPARAMOUT) && !isblanksym(t.Sym) && n.Sym == t.Sym { if (n.Class == PPARAM || n.Class == PPARAMOUT) && !isblanksym(t.Sym) && n.Sym == t.Sym {
return n return n
} }

View File

@ -107,7 +107,7 @@ func caninl(fn *Node) {
} }
// If marked "go:noinline", don't inline // If marked "go:noinline", don't inline
if fn.Func.Noinline { if fn.Func.Pragma&Noinline != 0 {
return return
} }
@ -150,7 +150,10 @@ func caninl(fn *Node) {
fn.Func.Nname.Func.Inl = fn.Nbody fn.Func.Nname.Func.Inl = fn.Nbody
fn.Nbody = inlcopylist(fn.Func.Nname.Func.Inl) 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) 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 // 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 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 // Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
// calls made to inlineable functions. This is the external entry point. // calls made to inlineable functions. This is the external entry point.
func inlcalls(fn *Node) { func inlcalls(fn *Node) {
@ -556,10 +571,14 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) {
//dumplist("ninit pre", ninit); //dumplist("ninit pre", ninit);
var dcl *NodeList var dcl []*Node
if fn.Name.Defn != nil { // local function if fn.Name.Defn != nil {
dcl = fn.Func.Inldcl // imported function // local function
if fn.Func.Inldcl != nil {
dcl = *fn.Func.Inldcl
}
} else { } else {
// imported function
dcl = fn.Func.Dcl dcl = fn.Func.Dcl
} }
@ -567,18 +586,18 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) {
i := 0 i := 0
// Make temp names to use instead of the originals // Make temp names to use instead of the originals
for ll := dcl; ll != nil; ll = ll.Next { for _, ln := range dcl {
if ll.N.Class == PPARAMOUT { // return values handled below. if ln.Class == PPARAMOUT { // return values handled below.
continue continue
} }
if ll.N.Op == ONAME { if ln.Op == ONAME {
ll.N.Name.Inlvar = inlvar(ll.N) ln.Name.Inlvar = inlvar(ln)
// Typecheck because inlvar is not necessarily a function parameter. // 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 { if ln.Class&^PHEAP != PAUTO {
ninit = list(ninit, Nod(ODCL, ll.N.Name.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs 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) addrescapes(n)
} }
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
return n return n
} }
@ -863,7 +882,7 @@ func retvar(t *Type, i int) *Node {
n.Class = PAUTO n.Class = PAUTO
n.Used = true n.Used = true
n.Name.Curfn = Curfn // the calling function, not the called one 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 return n
} }
@ -875,7 +894,7 @@ func argvar(t *Type, i int) *Node {
n.Class = PAUTO n.Class = PAUTO
n.Used = true n.Used = true
n.Name.Curfn = Curfn // the calling function, not the called one 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 return n
} }

File diff suppressed because it is too large Load Diff

View File

@ -217,7 +217,11 @@ func mplshfixfix(a, b *Mpint) {
s := Mpgetfix(b) s := Mpgetfix(b)
if s < 0 || s >= Mpprec { 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) Mpmovecfix(a, 0)
return return
} }
@ -236,7 +240,7 @@ func mprshfixfix(a, b *Mpint) {
s := Mpgetfix(b) s := Mpgetfix(b)
if s < 0 { if s < 0 {
Yyerror("stupid shift: %d", s) Yyerror("invalid negative shift count: %d", s)
if a.Val.Sign() < 0 { if a.Val.Sign() < 0 {
Mpmovecfix(a, -1) Mpmovecfix(a, -1)
} else { } else {

View File

@ -11,7 +11,7 @@ import (
"math" "math"
) )
/// implements float arihmetic // implements float arithmetic
func newMpflt() *Mpflt { func newMpflt() *Mpflt {
var a Mpflt var a Mpflt

View File

@ -387,7 +387,7 @@ func ordercall(n *Node, order *Order) {
// Ordermapassign appends n to order->out, introducing temporaries // Ordermapassign appends n to order->out, introducing temporaries
// to make sure that all map assignments have the form m[k] = x, // 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.) // (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: // 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: 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 prealloc[n] = ordertemp(Types[TUINT8], order, false) // walk will fill in correct type
} }

View File

@ -102,6 +102,8 @@ func (p *parser) syntax_error(msg string) {
} }
case LASOP: case LASOP:
tok = goopnames[p.op] + "=" tok = goopnames[p.op] + "="
case LINCOP:
tok = goopnames[p.op] + goopnames[p.op]
default: default:
tok = tokstring(p.tok) 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. // Like syntax_error, but reports error at given line rather than current lexer line.
func (p *parser) syntax_error_at(lineno int32, msg string) { func (p *parser) syntax_error_at(lno int32, msg string) {
defer func(lineno int32) { defer func(lno int32) {
lexlineno = lineno lineno = lno
}(lexlineno) }(lineno)
lexlineno = lineno lineno = lno
p.syntax_error(msg) p.syntax_error(msg)
} }
@ -219,12 +221,11 @@ var tokstrings = map[int32]string{
LANDAND: "&&", LANDAND: "&&",
LANDNOT: "&^", LANDNOT: "&^",
LCOMM: "<-", LCOMM: "<-",
LDEC: "--",
LEQ: "==", LEQ: "==",
LGE: ">=", LGE: ">=",
LGT: ">", LGT: ">",
LIGNORE: "LIGNORE", // we should never see this one LIGNORE: "LIGNORE", // we should never see this one
LINC: "++", LINCOP: "opop",
LLE: "<=", LLE: "<=",
LLSH: "<<", LLSH: "<<",
LLT: "<", LLT: "<",
@ -280,13 +281,11 @@ func (p *parser) package_() {
defer p.trace("package_")() defer p.trace("package_")()
} }
if p.got(LPACKAGE) { if !p.got(LPACKAGE) {
mkpackage(p.sym().Name)
} else {
prevlineno = lineno // see issue #13267
p.syntax_error("package statement must be first") p.syntax_error("package statement must be first")
errorexit() errorexit()
} }
mkpackage(p.sym().Name)
} }
// ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) . // ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
@ -335,20 +334,22 @@ func (p *parser) importdcl() {
} }
line := int32(parserline()) line := int32(parserline())
path := p.val
p.next()
importfile(&path, p.indent) // We need to clear importpkg before calling p.next(),
if importpkg == nil { // 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 { if nerrors == 0 {
Fatalf("phase error in import") Fatalf("phase error in import")
} }
return return
} }
ipkg := importpkg
importpkg = nil
ipkg.Direct = true ipkg.Direct = true
if my == nil { if my == nil {
@ -562,22 +563,13 @@ func (p *parser) simple_stmt(labelOk, rangeOk bool) *Node {
stmt.Etype = EType(op) // rathole to pass opcode stmt.Etype = EType(op) // rathole to pass opcode
return stmt return stmt
case LINC: case LINCOP:
// expr LINC // expr LINCOP
p.next() p.next()
stmt := Nod(OASOP, lhs, Nodintconst(1)) stmt := Nod(OASOP, lhs, Nodintconst(1))
stmt.Implicit = true stmt.Implicit = true
stmt.Etype = EType(OADD) stmt.Etype = EType(p.op)
return stmt
case LDEC:
// expr LDEC
p.next()
stmt := Nod(OASOP, lhs, Nodintconst(1))
stmt.Implicit = true
stmt.Etype = EType(OSUB)
return stmt return stmt
case ':': case ':':
@ -689,7 +681,7 @@ func (p *parser) labeled_stmt(label *Node) *Node {
ls = p.stmt() ls = p.stmt()
if ls == missing_stmt { if ls == missing_stmt {
// report error at line of ':' token // 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 // we are already at the end of the labeled statement - no need to advance
return missing_stmt return missing_stmt
} }
@ -1104,55 +1096,18 @@ func (p *parser) select_stmt() *Node {
return hdr 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 . // 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 // 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() x := p.uexpr()
t := prectab[p.tok] for p.prec > prec {
for tprec := t.prec; tprec >= prec; tprec-- { op, prec1 := p.op, p.prec
for tprec == prec {
p.next() p.next()
y := p.bexpr(t.prec + 1) x = Nod(op, x, p.bexpr(prec1))
x = Nod(t.op, x, y)
t = prectab[p.tok]
tprec = t.prec
}
} }
return x return x
} }
@ -1162,7 +1117,7 @@ func (p *parser) expr() *Node {
defer p.trace("expr")() defer p.trace("expr")()
} }
return p.bexpr(1) return p.bexpr(0)
} }
func unparen(x *Node) *Node { func unparen(x *Node) *Node {
@ -1611,13 +1566,15 @@ func (p *parser) new_name(sym *Sym) *Node {
return nil return nil
} }
func (p *parser) dcl_name(sym *Sym) *Node { func (p *parser) dcl_name() *Node {
if trace && Debug['x'] != 0 { if trace && Debug['x'] != 0 {
defer p.trace("dcl_name")() defer p.trace("dcl_name")()
} }
symlineno := lineno
sym := p.sym()
if sym == nil { if sym == nil {
yyerrorl(int(prevlineno), "invalid declaration") yyerrorl(int(symlineno), "invalid declaration")
return nil return nil
} }
return dclname(sym) return dclname(sym)
@ -1894,25 +1851,21 @@ func (p *parser) xfndcl() *Node {
} }
p.want(LFUNC) p.want(LFUNC)
f := p.fndcl() f := p.fndcl(p.pragma&Nointerface != 0)
body := p.fnbody() body := p.fnbody()
if f == nil { if f == nil {
return nil return nil
} }
if noescape && body != nil {
Yyerror("can only use //go:noescape with external func implementations")
}
f.Nbody = body 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.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) funcbody(f)
return f return f
@ -1923,7 +1876,7 @@ func (p *parser) xfndcl() *Node {
// Function = Signature FunctionBody . // Function = Signature FunctionBody .
// MethodDecl = "func" Receiver MethodName ( Function | Signature ) . // MethodDecl = "func" Receiver MethodName ( Function | Signature ) .
// Receiver = Parameters . // Receiver = Parameters .
func (p *parser) fndcl() *Node { func (p *parser) fndcl(nointerface bool) *Node {
if trace && Debug['x'] != 0 { if trace && Debug['x'] != 0 {
defer p.trace("fndcl")() defer p.trace("fndcl")()
} }
@ -2059,8 +2012,7 @@ func (p *parser) hidden_fndcl() *Node {
ss.Type = functype(s2.N, s6, s8) ss.Type = functype(s2.N, s6, s8)
checkwidth(ss.Type) checkwidth(ss.Type)
addmethod(s4, ss.Type, false, nointerface) addmethod(s4, ss.Type, false, false)
nointerface = false
funchdr(ss) funchdr(ss)
// inl.C's inlnode in on a dotmeth node expects to find the inlineable body as // inl.C's inlnode in on a dotmeth node expects to find the inlineable body as
@ -2141,18 +2093,10 @@ loop:
testdclstack() testdclstack()
} }
noescape = false // Reset p.pragma BEFORE advancing to the next token (consuming ';')
noinline = false // since comments before may set pragmas for the next function decl.
nointerface = false p.pragma = 0
norace = false
nosplit = false
nowritebarrier = false
nowritebarrierrec = false
systemstack = false
// 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(';') { if p.tok != EOF && !p.got(';') {
p.syntax_error("after top level declaration") p.syntax_error("after top level declaration")
p.advance(LVAR, LCONST, LTYPE, LFUNC) p.advance(LVAR, LCONST, LTYPE, LFUNC)
@ -2567,15 +2511,15 @@ func (p *parser) stmt() *Node {
stmt := Nod(ORETURN, nil, nil) stmt := Nod(ORETURN, nil, nil)
stmt.List = results stmt.List = results
if stmt.List == nil && Curfn != nil { if stmt.List == nil && Curfn != nil {
for l := Curfn.Func.Dcl; l != nil; l = l.Next { for _, ln := range Curfn.Func.Dcl {
if l.N.Class == PPARAM { if ln.Class == PPARAM {
continue continue
} }
if l.N.Class != PPARAMOUT { if ln.Class != PPARAMOUT {
break break
} }
if l.N.Sym.Def != l.N { if ln.Sym.Def != ln {
Yyerror("%s is shadowed during return", l.N.Sym.Name) 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")() defer p.trace("dcl_name_list")()
} }
l := list1(p.dcl_name(p.sym())) l := list1(p.dcl_name())
for p.got(',') { for p.got(',') {
l = list(l, p.dcl_name(p.sym())) l = list(l, p.dcl_name())
} }
return l return l
} }

View File

@ -9,6 +9,7 @@ import (
"cmd/internal/obj" "cmd/internal/obj"
"crypto/md5" "crypto/md5"
"fmt" "fmt"
"sort"
"strings" "strings"
) )
@ -218,6 +219,13 @@ func cmpstackvarlt(a, b *Node) bool {
return a.Sym.Name < b.Sym.Name 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 // stkdelta records the stack offset delta for a node
// during the compaction of the stack frame to remove // during the compaction of the stack frame to remove
// unused stack slots. // unused stack slots.
@ -228,25 +236,23 @@ func allocauto(ptxt *obj.Prog) {
Stksize = 0 Stksize = 0
stkptrsize = 0 stkptrsize = 0
if Curfn.Func.Dcl == nil { if len(Curfn.Func.Dcl) == 0 {
return return
} }
// Mark the PAUTO's unused. // Mark the PAUTO's unused.
for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { for _, ln := range Curfn.Func.Dcl {
if ll.N.Class == PAUTO { if ln.Class == PAUTO {
ll.N.Used = false ln.Used = false
} }
} }
markautoused(ptxt) markautoused(ptxt)
listsort(&Curfn.Func.Dcl, cmpstackvarlt) sort.Sort(byStackVar(Curfn.Func.Dcl))
// Unused autos are at the end, chop 'em off. // Unused autos are at the end, chop 'em off.
ll := Curfn.Func.Dcl n := Curfn.Func.Dcl[0]
n := ll.N
if n.Class == PAUTO && n.Op == ONAME && !n.Used { if n.Class == PAUTO && n.Op == ONAME && !n.Used {
// No locals used at all // No locals used at all
Curfn.Func.Dcl = nil Curfn.Func.Dcl = nil
@ -255,19 +261,17 @@ func allocauto(ptxt *obj.Prog) {
return return
} }
for ll := Curfn.Func.Dcl; ll.Next != nil; ll = ll.Next { for i := 1; i < len(Curfn.Func.Dcl); i++ {
n = ll.Next.N n = Curfn.Func.Dcl[i]
if n.Class == PAUTO && n.Op == ONAME && !n.Used { if n.Class == PAUTO && n.Op == ONAME && !n.Used {
ll.Next = nil Curfn.Func.Dcl = Curfn.Func.Dcl[:i]
Curfn.Func.Dcl.End = ll
break break
} }
} }
// Reassign stack offsets of the locals that are still there. // Reassign stack offsets of the locals that are still there.
var w int64 var w int64
for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { for _, n := range Curfn.Func.Dcl {
n = ll.N
if n.Class != PAUTO || n.Op != ONAME { if n.Class != PAUTO || n.Op != ONAME {
continue continue
} }
@ -299,12 +303,12 @@ func allocauto(ptxt *obj.Prog) {
fixautoused(ptxt) fixautoused(ptxt)
// The debug information needs accurate offsets on the symbols. // The debug information needs accurate offsets on the symbols.
for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { for _, ln := range Curfn.Func.Dcl {
if ll.N.Class != PAUTO || ll.N.Op != ONAME { if ln.Class != PAUTO || ln.Op != ONAME {
continue continue
} }
ll.N.Xoffset += stkdelta[ll.N] ln.Xoffset += stkdelta[ln]
delete(stkdelta, ll.N) delete(stkdelta, ln)
} }
} }
@ -442,10 +446,10 @@ func compile(fn *Node) {
if fn.Func.Needctxt { if fn.Func.Needctxt {
ptxt.From3.Offset |= obj.NEEDCTXT ptxt.From3.Offset |= obj.NEEDCTXT
} }
if fn.Func.Nosplit { if fn.Func.Pragma&Nosplit != 0 {
ptxt.From3.Offset |= obj.NOSPLIT ptxt.From3.Offset |= obj.NOSPLIT
} }
if fn.Func.Systemstack { if fn.Func.Pragma&Systemstack != 0 {
ptxt.From.Sym.Cfunc = 1 ptxt.From.Sym.Cfunc = 1
} }
@ -467,16 +471,15 @@ func compile(fn *Node) {
gtrack(tracksym(t)) gtrack(tracksym(t))
} }
for l := fn.Func.Dcl; l != nil; l = l.Next { for _, n := range fn.Func.Dcl {
n = l.N
if n.Op != ONAME { // might be OTYPE or OLITERAL if n.Op != ONAME { // might be OTYPE or OLITERAL
continue continue
} }
switch n.Class { switch n.Class {
case PAUTO, PPARAM, PPARAMOUT: case PAUTO, PPARAM, PPARAMOUT:
Nodconst(&nod1, Types[TUINTPTR], l.N.Type.Width) Nodconst(&nod1, Types[TUINTPTR], n.Type.Width)
p = Thearch.Gins(obj.ATYPE, l.N, &nod1) p = Thearch.Gins(obj.ATYPE, n, &nod1)
p.From.Gotype = Linksym(ngotype(l.N)) p.From.Gotype = Linksym(ngotype(n))
} }
} }
@ -488,7 +491,7 @@ func compile(fn *Node) {
ssafn.Free() ssafn.Free()
return return
} }
Genlist(Curfn.Func.Enter) Genslice(Curfn.Func.Enter.Slice())
Genlist(Curfn.Nbody) Genlist(Curfn.Nbody)
gclean() gclean()
checklabels() checklabels()

View File

@ -122,7 +122,7 @@ func addedge(from *BasicBlock, to *BasicBlock) {
} }
// Inserts prev before curr in the instruction // 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. // existing instruction are adjusted to target the new instruction.
func splicebefore(lv *Liveness, bb *BasicBlock, prev *obj.Prog, curr *obj.Prog) { func splicebefore(lv *Liveness, bb *BasicBlock, prev *obj.Prog, curr *obj.Prog) {
// There may be other instructions pointing at curr, // There may be other instructions pointing at curr,
@ -198,8 +198,8 @@ func blockany(bb *BasicBlock, f func(*obj.Prog) bool) bool {
// variables. // variables.
func getvariables(fn *Node) []*Node { func getvariables(fn *Node) []*Node {
result := make([]*Node, 0, 0) result := make([]*Node, 0, 0)
for ll := fn.Func.Dcl; ll != nil; ll = ll.Next { for _, ln := range fn.Func.Dcl {
if ll.N.Op == ONAME { if ln.Op == ONAME {
// In order for GODEBUG=gcdead=1 to work, each bitmap needs // In order for GODEBUG=gcdead=1 to work, each bitmap needs
// to contain information about all variables covered by the bitmap. // to contain information about all variables covered by the bitmap.
// For local variables, the bitmap only covers the stkptrsize // 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, // 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 // we will check that n->curfn == curfn and n->opt > 0. Then n->opt - 1
// is the index in the variables list. // 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. // The compiler doesn't emit initializations for zero-width parameters or results.
if ll.N.Type.Width == 0 { if ln.Type.Width == 0 {
continue continue
} }
ll.N.Name.Curfn = Curfn ln.Name.Curfn = Curfn
switch ll.N.Class { switch ln.Class {
case PAUTO: case PAUTO:
if haspointers(ll.N.Type) { if haspointers(ln.Type) {
ll.N.SetOpt(int32(len(result))) ln.SetOpt(int32(len(result)))
result = append(result, ll.N) result = append(result, ln)
} }
case PPARAM, PPARAMOUT: case PPARAM, PPARAMOUT:
ll.N.SetOpt(int32(len(result))) ln.SetOpt(int32(len(result)))
result = append(result, ll.N) result = append(result, ln)
} }
} }
} }
@ -798,8 +798,8 @@ func livenessprintcfg(lv *Liveness) {
} }
func checkauto(fn *Node, p *obj.Prog, n *Node) { func checkauto(fn *Node, p *obj.Prog, n *Node) {
for l := fn.Func.Dcl; l != nil; l = l.Next { for _, ln := range fn.Func.Dcl {
if l.N.Op == ONAME && l.N.Class == PAUTO && l.N == n { if ln.Op == ONAME && ln.Class == PAUTO && ln == n {
return 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) 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 { for _, ln := range fn.Func.Dcl {
fmt.Printf("\t%v (%p; class=%d)\n", l.N, l.N, l.N.Class) fmt.Printf("\t%v (%p; class=%d)\n", ln, ln, ln.Class)
} }
Yyerror("checkauto: invariant lost") Yyerror("checkauto: invariant lost")
} }
@ -820,10 +820,8 @@ func checkparam(fn *Node, p *obj.Prog, n *Node) {
if isfunny(n) { if isfunny(n) {
return return
} }
var a *Node
var class Class var class Class
for l := fn.Func.Dcl; l != nil; l = l.Next { for _, a := range fn.Func.Dcl {
a = l.N
class = a.Class &^ PHEAP class = a.Class &^ PHEAP
if a.Op == ONAME && (class == PPARAM || class == PPARAMOUT) && a == n { if a.Op == ONAME && (class == PPARAM || class == PPARAMOUT) && a == n {
return 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) 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 { for _, ln := range fn.Func.Dcl {
fmt.Printf("\t%v (%p; class=%d)\n", l.N, l.N, l.N.Class) fmt.Printf("\t%v (%p; class=%d)\n", ln, ln, ln.Class)
} }
Yyerror("checkparam: invariant lost") Yyerror("checkparam: invariant lost")
} }
@ -1815,9 +1813,9 @@ func liveness(fn *Node, firstp *obj.Prog, argssym *Sym, livesym *Sym) {
onebitwritesymbol(lv.argslivepointers, argssym) onebitwritesymbol(lv.argslivepointers, argssym)
// Free everything. // Free everything.
for l := fn.Func.Dcl; l != nil; l = l.Next { for _, ln := range fn.Func.Dcl {
if l.N != nil { if ln != nil {
l.N.SetOpt(nil) ln.SetOpt(nil)
} }
} }
freeliveness(lv) freeliveness(lv)

View File

@ -138,15 +138,16 @@ func fixjmp(firstp *obj.Prog) {
fmt.Printf("%v\n", p) 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 { 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 {
if Debug['N'] == 0 {
p.To.Val = chasejmp(p.To.Val.(*obj.Prog), &jmploop) p.To.Val = chasejmp(p.To.Val.(*obj.Prog), &jmploop)
if Debug['R'] != 0 && Debug['v'] != 0 { if Debug['R'] != 0 && Debug['v'] != 0 {
fmt.Printf("->%v\n", p) fmt.Printf("->%v\n", p)
} }
} }
}
p.Opt = dead p.Opt = dead
} }
if Debug['R'] != 0 && Debug['v'] != 0 { if Debug['R'] != 0 && Debug['v'] != 0 {
fmt.Printf("\n") fmt.Printf("\n")
} }
@ -186,7 +187,7 @@ func fixjmp(firstp *obj.Prog) {
// pass 4: elide JMP to next instruction. // pass 4: elide JMP to next instruction.
// only safe if there are no jumps to JMPs anymore. // only safe if there are no jumps to JMPs anymore.
if jmploop == 0 { if jmploop == 0 && Debug['N'] == 0 {
var last *obj.Prog var last *obj.Prog
for p := firstp; p != nil; p = p.Link { for p := firstp; p != nil; p = p.Link {
if p.As == obj.AJMP && p.To.Type == obj.TYPE_BRANCH && p.To.Val == 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 // Control flow analysis. The Flow structures hold predecessor and successor
// information as well as basic loop analysis. // information as well as basic loop analysis.
// //
// graph = flowstart(firstp, 0); // graph = Flowstart(firstp, nil)
// ... use flow graph ... // ... 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: // 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 // 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 // The second argument (newData) to Flowstart specifies a func to create object
// to allocate in every f->data field, for use by the client. // for every f.Data field, for use by the client.
// If size == 0, f->data will be nil. // If newData is nil, f.Data will be nil.
var flowmark int var flowmark int
@ -471,8 +472,8 @@ func flowrpo(g *Graph) {
me = r1.Rpo me = r1.Rpo
d = -1 d = -1
// rpo2r[r->rpo] == r protects against considering dead code, // rpo2r[r.Rpo] == r protects against considering dead code,
// which has r->rpo == 0. // which has r.Rpo == 0.
if r1.P1 != nil && rpo2r[r1.P1.Rpo] == r1.P1 && r1.P1.Rpo < me { if r1.P1 != nil && rpo2r[r1.P1.Rpo] == r1.P1 && r1.P1.Rpo < me {
d = r1.P1.Rpo d = r1.P1.Rpo
} }
@ -588,8 +589,8 @@ func mergetemp(firstp *obj.Prog) {
// Build list of all mergeable variables. // Build list of all mergeable variables.
var vars []*TempVar var vars []*TempVar
for l := Curfn.Func.Dcl; l != nil; l = l.Next { for _, n := range Curfn.Func.Dcl {
if n := l.N; canmerge(n) { if canmerge(n) {
v := &TempVar{} v := &TempVar{}
vars = append(vars, v) vars = append(vars, v)
n.SetOpt(v) n.SetOpt(v)
@ -684,7 +685,7 @@ func mergetemp(firstp *obj.Prog) {
// Traverse live range of each variable to set start, end. // Traverse live range of each variable to set start, end.
// Each flood uses a new value of gen so that we don't have // 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) gen := uint32(0)
for _, v := range vars { for _, v := range vars {
@ -818,22 +819,15 @@ func mergetemp(firstp *obj.Prog) {
} }
// Delete merged nodes from declaration list. // Delete merged nodes from declaration list.
for lp := &Curfn.Func.Dcl; ; { dcl := make([]*Node, 0, len(Curfn.Func.Dcl)-nkill)
l := *lp for _, n := range Curfn.Func.Dcl {
if l == nil {
break
}
Curfn.Func.Dcl.End = l
n := l.N
v, _ := n.Opt().(*TempVar) v, _ := n.Opt().(*TempVar)
if v != nil && (v.merge != nil || v.removed) { if v != nil && (v.merge != nil || v.removed) {
*lp = l.Next
continue continue
} }
dcl = append(dcl, n)
lp = &l.Next
} }
Curfn.Func.Dcl = dcl
// Clear aux structures. // Clear aux structures.
for _, v := range vars { 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 // from memory without being rechecked. Other variables need to be checked on
// each load. // 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) { func nilopt(firstp *obj.Prog) {
g := Flowstart(firstp, nil) g := Flowstart(firstp, nil)

View File

@ -50,7 +50,7 @@ func ispkgin(pkgs []string) bool {
} }
func instrument(fn *Node) { func instrument(fn *Node) {
if ispkgin(omit_pkgs) || fn.Func.Norace { if ispkgin(omit_pkgs) || fn.Func.Pragma&Norace != 0 {
return return
} }
@ -58,7 +58,7 @@ func instrument(fn *Node) {
instrumentlist(fn.Nbody, nil) instrumentlist(fn.Nbody, nil)
// nothing interesting for race detector in fn->enter // nothing interesting for race detector in fn->enter
instrumentlist(fn.Func.Exit, nil) instrumentslice(fn.Func.Exit.Slice(), nil)
} }
if flag_race != 0 { if flag_race != 0 {
@ -71,18 +71,18 @@ func instrument(fn *Node) {
nodpc.Type = Types[TUINTPTR] nodpc.Type = Types[TUINTPTR]
nodpc.Xoffset = int64(-Widthptr) nodpc.Xoffset = int64(-Widthptr)
nd := mkcall("racefuncenter", nil, nil, nodpc) 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) nd = mkcall("racefuncexit", nil, nil)
fn.Func.Exit = list(fn.Func.Exit, nd) fn.Func.Exit.Append(nd)
} }
if Debug['W'] != 0 { if Debug['W'] != 0 {
s := fmt.Sprintf("after instrument %v", fn.Func.Nname.Sym) s := fmt.Sprintf("after instrument %v", fn.Func.Nname.Sym)
dumplist(s, fn.Nbody) dumplist(s, fn.Nbody)
s = fmt.Sprintf("enter %v", fn.Func.Nname.Sym) 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) 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 // walkexpr and walkstmt combined
// walks the tree and adds calls to the // walks the tree and adds calls to the
// instrumentation code to top-level (statement) nodes' init // instrumentation code to top-level (statement) nodes' init

View File

@ -491,17 +491,11 @@ func dextratype(sym *Sym, off int, t *Type, ptroff int) int {
ot := off ot := off
s := sym s := sym
if t.Sym != nil { if t.Sym != nil && t != Types[t.Etype] && t != errortype {
ot = dgostringptr(s, ot, t.Sym.Name)
if t != Types[t.Etype] && t != errortype {
ot = dgopkgpath(s, ot, t.Sym.Pkg) ot = dgopkgpath(s, ot, t.Sym.Pkg)
} else { } else {
ot = dgostringptr(s, ot, "") ot = dgostringptr(s, ot, "")
} }
} else {
ot = dgostringptr(s, ot, "")
ot = dgostringptr(s, ot, "")
}
// slice header // slice header
ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint) ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint)
@ -700,12 +694,14 @@ func dcommontype(s *Sym, ot int, t *Type) int {
algsym = dalgsym(t) algsym = dalgsym(t)
} }
var sptr *Sym
tptr := Ptrto(t) tptr := Ptrto(t)
if !Isptr[t.Etype] && (t.Sym != nil || methods(tptr) != nil) { if !Isptr[t.Etype] && (t.Sym != nil || methods(tptr) != nil) {
sptr = dtypesym(tptr) sptr := dtypesym(tptr)
} else { r := obj.Addrel(Linksym(s))
sptr = weaktypesym(tptr) r.Off = 0
r.Siz = 0
r.Sym = sptr.Lsym
r.Type = obj.R_USETYPE
} }
gcsym, useGCProg, ptrdata := dgcsym(t) gcsym, useGCProg, ptrdata := dgcsym(t)
@ -724,7 +720,6 @@ func dcommontype(s *Sym, ot int, t *Type) int {
// gcdata *byte // gcdata *byte
// string *string // string *string
// *uncommonType // *uncommonType
// ptrToThis *rtype
// } // }
ot = duintptr(s, ot, uint64(t.Width)) ot = duintptr(s, ot, uint64(t.Width))
ot = duintptr(s, ot, uint64(ptrdata)) ot = duintptr(s, ot, uint64(ptrdata))
@ -763,12 +758,14 @@ func dcommontype(s *Sym, ot int, t *Type) int {
} else { } else {
ot = dsymptr(s, ot, algsym, 0) 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) p := Tconv(t, obj.FmtLeft|obj.FmtUnsigned)
//print("dcommontype: %s\n", p); _, symdata := stringsym(p) // string
ot = dgostringptr(s, ot, 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, // skip pointer to extraType,
// which follows the rest of this type structure. // 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. // otherwise linker will assume 0.
ot += Widthptr ot += Widthptr
ot = dsymptr(s, ot, sptr, 0) // ptrto type
return ot return ot
} }
@ -1006,7 +1002,7 @@ ok:
switch t.Etype { switch t.Etype {
default: default:
ot = dcommontype(s, ot, t) ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr xt = ot - 1*Widthptr
case TARRAY: case TARRAY:
if t.Bound >= 0 { if t.Bound >= 0 {
@ -1018,7 +1014,7 @@ ok:
t2.Bound = -1 // slice t2.Bound = -1 // slice
s2 := dtypesym(t2) s2 := dtypesym(t2)
ot = dcommontype(s, ot, t) ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s1, 0) ot = dsymptr(s, ot, s1, 0)
ot = dsymptr(s, ot, s2, 0) ot = dsymptr(s, ot, s2, 0)
ot = duintptr(s, ot, uint64(t.Bound)) ot = duintptr(s, ot, uint64(t.Bound))
@ -1027,7 +1023,7 @@ ok:
s1 := dtypesym(t.Type) s1 := dtypesym(t.Type)
ot = dcommontype(s, ot, t) ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s1, 0) ot = dsymptr(s, ot, s1, 0)
} }
@ -1036,7 +1032,7 @@ ok:
s1 := dtypesym(t.Type) s1 := dtypesym(t.Type)
ot = dcommontype(s, ot, t) ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s1, 0) ot = dsymptr(s, ot, s1, 0)
ot = duintptr(s, ot, uint64(t.Chan)) ot = duintptr(s, ot, uint64(t.Chan))
@ -1055,7 +1051,7 @@ ok:
} }
ot = dcommontype(s, ot, t) ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr xt = ot - 1*Widthptr
ot = duint8(s, ot, uint8(obj.Bool2int(isddd))) ot = duint8(s, ot, uint8(obj.Bool2int(isddd)))
// two slice headers: in and out. // two slice headers: in and out.
@ -1093,7 +1089,7 @@ ok:
// ../../../../runtime/type.go:/interfaceType // ../../../../runtime/type.go:/interfaceType
ot = dcommontype(s, ot, t) ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint) ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint)
ot = duintxx(s, ot, uint64(n), Widthint) ot = duintxx(s, ot, uint64(n), Widthint)
ot = duintxx(s, ot, uint64(n), Widthint) ot = duintxx(s, ot, uint64(n), Widthint)
@ -1113,7 +1109,7 @@ ok:
s3 := dtypesym(mapbucket(t)) s3 := dtypesym(mapbucket(t))
s4 := dtypesym(hmap(t)) s4 := dtypesym(hmap(t))
ot = dcommontype(s, ot, t) ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s1, 0) ot = dsymptr(s, ot, s1, 0)
ot = dsymptr(s, ot, s2, 0) ot = dsymptr(s, ot, s2, 0)
ot = dsymptr(s, ot, s3, 0) ot = dsymptr(s, ot, s3, 0)
@ -1150,7 +1146,7 @@ ok:
s1 := dtypesym(t.Type) s1 := dtypesym(t.Type)
ot = dcommontype(s, ot, t) ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s1, 0) ot = dsymptr(s, ot, s1, 0)
// ../../../../runtime/type.go:/structType // ../../../../runtime/type.go:/structType
@ -1164,7 +1160,7 @@ ok:
} }
ot = dcommontype(s, ot, t) ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint) ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint)
ot = duintxx(s, ot, uint64(n), Widthint) ot = duintxx(s, ot, uint64(n), 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. // we want be able to find.
if t.Sym == nil { if t.Sym == nil {
switch t.Etype { switch t.Etype {
case TPTR32, TPTR64: case TPTR32, TPTR64, TARRAY, TCHAN, TFUNC, TMAP:
// 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:
slink := typelinksym(t) slink := typelinksym(t)
dsymptr(slink, 0, s, 0) dsymptr(slink, 0, s, 0)
ggloblsym(slink, int32(Widthptr), int16(dupok|obj.RODATA)) 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 // 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. // 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 // 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 // is 32 pointers, the bits for which fit in 4 bytes. So maxPtrmaskBytes
// must be >= 4. // must be >= 4.
// //

View File

@ -475,7 +475,7 @@ func staticassign(l *Node, r *Node, out **NodeList) bool {
break break
case OCLOSURE: case OCLOSURE:
if r.Func.Cvars == nil { if len(r.Func.Cvars.Slice()) == 0 {
// Closures with no captured variables are globals, // Closures with no captured variables are globals,
// so the assignment can be done at link time. // so the assignment can be done at link time.
n := *l n := *l

View File

@ -84,9 +84,9 @@ func buildssa(fn *Node) *ssa.Func {
printssa := strings.HasSuffix(name, "_ssa") || strings.Contains(name, "_ssa.") || name == os.Getenv("GOSSAFUNC") printssa := strings.HasSuffix(name, "_ssa") || strings.Contains(name, "_ssa.") || name == os.Getenv("GOSSAFUNC")
if printssa { if printssa {
fmt.Println("generating SSA for", name) 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-body", fn.Nbody)
dumplist("buildssa-exit", fn.Func.Exit) dumpslice("buildssa-exit", fn.Func.Exit.Slice())
} }
var s state var s state
@ -132,8 +132,7 @@ func buildssa(fn *Node) *ssa.Func {
// Generate addresses of local declarations // Generate addresses of local declarations
s.decladdrs = map[*Node]*ssa.Value{} s.decladdrs = map[*Node]*ssa.Value{}
for d := fn.Func.Dcl; d != nil; d = d.Next { for _, n := range fn.Func.Dcl {
n := d.N
switch n.Class { switch n.Class {
case PPARAM: case PPARAM:
aux := s.lookupSymbol(n, &ssa.ArgSymbol{Typ: n.Type, Node: n}) 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 // Convert the AST-based IR to the SSA-based IR
s.stmtList(fn.Func.Enter) s.stmts(fn.Func.Enter)
s.stmtList(fn.Nbody) s.stmtList(fn.Nbody)
// fallthrough to exit // fallthrough to exit
if s.curBlock != nil { if s.curBlock != nil {
s.stmtList(s.exitCode) s.stmts(s.exitCode)
m := s.mem() m := s.mem()
b := s.endBlock() b := s.endBlock()
b.Kind = ssa.BlockRet b.Kind = ssa.BlockRet
@ -201,7 +200,7 @@ func buildssa(fn *Node) *ssa.Func {
s.linkForwardReferences() s.linkForwardReferences()
// Don't carry reference this around longer than necessary // Don't carry reference this around longer than necessary
s.exitCode = nil s.exitCode = Nodes{}
// Main call to ssa package to compile function // Main call to ssa package to compile function
ssa.Compile(s.f) ssa.Compile(s.f)
@ -224,7 +223,7 @@ type state struct {
fwdGotos []*Node fwdGotos []*Node
// Code that must precede any return // Code that must precede any return
// (e.g., copying value of heap-escaped paramout back to true paramout) // (e.g., copying value of heap-escaped paramout back to true paramout)
exitCode *NodeList exitCode Nodes
// unlabeled break and continue statement tracking // unlabeled break and continue statement tracking
breakTo *ssa.Block // current target for plain break statement 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)) 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. // ssaStmtList converts the statement n to SSA and adds it to s.
func (s *state) stmtList(l *NodeList) { func (s *state) stmtList(l *NodeList) {
for ; l != nil; l = l.Next { for ; l != nil; l = l.Next {
@ -697,14 +702,14 @@ func (s *state) stmt(n *Node) {
case ORETURN: case ORETURN:
s.stmtList(n.List) s.stmtList(n.List)
s.stmtList(s.exitCode) s.stmts(s.exitCode)
m := s.mem() m := s.mem()
b := s.endBlock() b := s.endBlock()
b.Kind = ssa.BlockRet b.Kind = ssa.BlockRet
b.Control = m b.Control = m
case ORETJMP: case ORETJMP:
s.stmtList(n.List) s.stmtList(n.List)
s.stmtList(s.exitCode) s.stmts(s.exitCode)
m := s.mem() m := s.mem()
b := s.endBlock() b := s.endBlock()
b.Kind = ssa.BlockRetJmp b.Kind = ssa.BlockRetJmp

View File

@ -19,7 +19,6 @@ import (
type Error struct { type Error struct {
lineno int lineno int
seq int
msg string msg string
} }
@ -49,35 +48,24 @@ func adderrorname(n *Node) {
func adderr(line int, format string, args ...interface{}) { func adderr(line int, format string, args ...interface{}) {
errors = append(errors, Error{ errors = append(errors, Error{
seq: len(errors),
lineno: line, lineno: line,
msg: fmt.Sprintf("%v: %s\n", Ctxt.Line(line), fmt.Sprintf(format, args...)), msg: fmt.Sprintf("%v: %s\n", Ctxt.Line(line), fmt.Sprintf(format, args...)),
}) })
} }
// errcmp sorts errors by line, then seq, then message. // byLineno sorts errors by lineno.
type errcmp []Error type byLineno []Error
func (x errcmp) Len() int { return len(x) } func (x byLineno) Len() int { return len(x) }
func (x errcmp) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x byLineno) Less(i, j int) bool { return x[i].lineno < x[j].lineno }
func (x errcmp) Less(i, j int) bool { func (x byLineno) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
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 Flusherrors() { func Flusherrors() {
bstdout.Flush() bstdout.Flush()
if len(errors) == 0 { if len(errors) == 0 {
return return
} }
sort.Sort(errcmp(errors)) sort.Stable(byLineno(errors))
for i := 0; i < len(errors); i++ { for i := 0; i < len(errors); i++ {
if i == 0 || errors[i].msg != errors[i-1].msg { if i == 0 || errors[i].msg != errors[i-1].msg {
fmt.Printf("%s", errors[i].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{}) { func Yyerror(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...) msg := fmt.Sprintf(format, args...)
@ -117,18 +105,12 @@ func Yyerror(format string, args ...interface{}) {
nsyntaxerrors++ nsyntaxerrors++
// only one syntax error per line // only one syntax error per line
if int32(yyerror_lastsyntax) == lexlineno { if yyerror_lastsyntax == lineno {
return return
} }
yyerror_lastsyntax = int(lexlineno) yyerror_lastsyntax = lineno
// plain "syntax error" gets "near foo" added yyerrorl(int(lineno), "%s", msg)
if msg == "syntax error" {
yyerrorl(int(lexlineno), "syntax error near %s", lexbuf.String())
return
}
yyerrorl(int(lexlineno), "%s", msg)
return return
} }
@ -258,7 +240,6 @@ func (pkg *Pkg) Lookup(name string) *Sym {
s := &Sym{ s := &Sym{
Name: name, Name: name,
Pkg: pkg, Pkg: pkg,
Lexical: LNAME,
} }
if name == "init" { if name == "init" {
initSyms = append(initSyms, s) initSyms = append(initSyms, s)
@ -367,162 +348,6 @@ func saveorignode(n *Node) {
n.Orig = norig 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 { func maptype(key *Type, val *Type) *Type {
if key != nil { if key != nil {
var bad *Type var bad *Type
@ -1561,8 +1386,8 @@ func frame(context int) {
if Curfn != nil { if Curfn != nil {
fmt.Printf("--- %v frame ---\n", Curfn.Func.Nname.Sym) fmt.Printf("--- %v frame ---\n", Curfn.Func.Nname.Sym)
for l := Curfn.Func.Dcl; l != nil; l = l.Next { for _, ln := range Curfn.Func.Dcl {
printframenode(l.N) printframenode(ln)
} }
} }
} }
@ -2414,457 +2239,6 @@ func hashmem(t *Type) *Node {
return n 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 { func ifacelookdot(s *Sym, t *Type, followptr *bool, ignorecase int) *Type {
*followptr = false *followptr = false

View File

@ -149,11 +149,11 @@ type Param struct {
// Func holds Node fields used only with function-like nodes. // Func holds Node fields used only with function-like nodes.
type Func struct { type Func struct {
Shortname *Node Shortname *Node
Enter *NodeList // for example, allocate and initialize memory for escaping parameters Enter Nodes // for example, allocate and initialize memory for escaping parameters
Exit *NodeList Exit Nodes
Cvars *NodeList // closure params Cvars Nodes // closure params
Dcl *NodeList // autodcl for this func/closure Dcl []*Node // autodcl for this func/closure
Inldcl *NodeList // copy of dcl for use in inlining Inldcl *[]*Node // copy of dcl for use in inlining
Closgen int Closgen int
Outerfunc *Node Outerfunc *Node
Fieldtrack []*Type Fieldtrack []*Type
@ -169,18 +169,12 @@ type Func struct {
Depth int32 Depth int32
Endlineno int32 Endlineno int32
WBLineno int32 // line number of first write barrier
Norace bool // func must not have race detector annotations Pragma Pragma // go:xxx function 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 Dupok bool // duplicate definitions ok
Wrapper bool // is method wrapper Wrapper bool // is method wrapper
Needctxt bool // function uses context register (has closure variables) Needctxt bool // function uses context register (has closure variables)
Systemstack bool // must run on system stack
WBLineno int32 // line number of first write barrier
} }
type Op uint8 type Op uint8
@ -491,3 +485,55 @@ func count(l *NodeList) int {
} }
return int(n) 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...)
}
}

View File

@ -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{ var _typekind = []string{
TINT: "int", TINT: "int",
TUINT: "uint", TUINT: "uint",
@ -3433,9 +3439,9 @@ func typecheckfunc(n *Node) {
addmethod(n.Func.Shortname.Sym, t, true, n.Func.Nname.Nointerface) addmethod(n.Func.Shortname.Sym, t, true, n.Func.Nname.Nointerface)
} }
for l := n.Func.Dcl; l != nil; l = l.Next { for _, ln := range n.Func.Dcl {
if l.N.Op == ONAME && (l.N.Class == PPARAM || l.N.Class == PPARAMOUT) { if ln.Op == ONAME && (ln.Class == PPARAM || ln.Class == PPARAMOUT) {
l.N.Name.Decldepth = 1 ln.Name.Decldepth = 1
} }
} }
} }

View File

@ -29,33 +29,34 @@ func walk(fn *Node) {
// Final typecheck for any unused variables. // 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. // 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 { for i, ln := range fn.Func.Dcl {
if l.N.Op == ONAME && l.N.Class&^PHEAP == PAUTO { if ln.Op == ONAME && ln.Class&^PHEAP == PAUTO {
typecheck(&l.N, Erv|Easgn) typecheck(&ln, Erv|Easgn)
fn.Func.Dcl[i] = ln
} }
} }
// Propagate the used flag for typeswitch variables up to the NONAME in it's definition. // 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 { for _, ln := range fn.Func.Dcl {
if l.N.Op == ONAME && l.N.Class&^PHEAP == PAUTO && l.N.Name.Defn != nil && l.N.Name.Defn.Op == OTYPESW && l.N.Used { if ln.Op == ONAME && ln.Class&^PHEAP == PAUTO && ln.Name.Defn != nil && ln.Name.Defn.Op == OTYPESW && ln.Used {
l.N.Name.Defn.Left.Used = true ln.Name.Defn.Left.Used = true
} }
} }
for l := fn.Func.Dcl; l != nil; l = l.Next { for _, ln := range fn.Func.Dcl {
if l.N.Op != ONAME || l.N.Class&^PHEAP != PAUTO || l.N.Sym.Name[0] == '&' || l.N.Used { if ln.Op != ONAME || ln.Class&^PHEAP != PAUTO || ln.Sym.Name[0] == '&' || ln.Used {
continue 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 { if defn.Left.Used {
continue continue
} }
lineno = defn.Left.Lineno 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 defn.Left.Used = true // suppress repeats
} else { } else {
lineno = l.N.Lineno lineno = ln.Lineno
Yyerror("%v declared and not used", l.N.Sym) Yyerror("%v declared and not used", ln.Sym)
} }
} }
@ -70,9 +71,9 @@ func walk(fn *Node) {
} }
heapmoves() 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) 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 { func samelist(a *NodeList, b *NodeList) bool {
for ; a != nil && b != nil; a, b = a.Next, b.Next { for ; a != nil && b != nil; a, b = a.Next, b.Next {
if a.N != b.N { if a.N != b.N {
@ -92,11 +99,11 @@ func samelist(a *NodeList, b *NodeList) bool {
} }
func paramoutheap(fn *Node) bool { func paramoutheap(fn *Node) bool {
for l := fn.Func.Dcl; l != nil; l = l.Next { for _, ln := range fn.Func.Dcl {
switch l.N.Class { switch ln.Class {
case PPARAMOUT, case PPARAMOUT,
PPARAMOUT | PHEAP: PPARAMOUT | PHEAP:
return l.N.Addrtaken return ln.Addrtaken
// stop early - parameters are over // stop early - parameters are over
case PAUTO, case PAUTO,
@ -290,13 +297,13 @@ func walkstmt(np **Node) {
var rl *NodeList var rl *NodeList
var cl Class var cl Class
for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { for _, ln := range Curfn.Func.Dcl {
cl = ll.N.Class &^ PHEAP cl = ln.Class &^ PHEAP
if cl == PAUTO { if cl == PAUTO {
break break
} }
if cl == PPARAMOUT { 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) ll := ascompatee(n.Op, rl, n.List, &n.Ninit)
n.List = reorder3(ll) n.List = reorder3(ll)
for lr := n.List; lr != nil; lr = lr.Next { for lr := n.List; lr != nil; lr = lr.Next {
lr.N = applywritebarrier(lr.N, &n.Ninit) lr.N = applywritebarrier(lr.N)
} }
break break
} }
@ -587,9 +594,9 @@ opswitch:
// transformclosure already did all preparation work. // transformclosure already did all preparation work.
// Prepend captured variables to argument list. // 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. // Replace OCLOSURE with ONAME/PFUNC.
n.Left = n.Left.Func.Closure.Func.Nname n.Left = n.Left.Func.Closure.Func.Nname
@ -723,7 +730,7 @@ opswitch:
r := convas(Nod(OAS, n.Left, n.Right), init) r := convas(Nod(OAS, n.Left, n.Right), init)
r.Dodata = n.Dodata r.Dodata = n.Dodata
n = r n = r
n = applywritebarrier(n, init) n = applywritebarrier(n)
} }
case OAS2: case OAS2:
@ -734,7 +741,7 @@ opswitch:
ll := ascompatee(OAS, n.List, n.Rlist, init) ll := ascompatee(OAS, n.List, n.Rlist, init)
ll = reorder3(ll) ll = reorder3(ll)
for lr := ll; lr != nil; lr = lr.Next { for lr := ll; lr != nil; lr = lr.Next {
lr.N = applywritebarrier(lr.N, init) lr.N = applywritebarrier(lr.N)
} }
n = liststmt(ll) n = liststmt(ll)
@ -749,7 +756,7 @@ opswitch:
ll := ascompatet(n.Op, n.List, &r.Type, 0, init) ll := ascompatet(n.Op, n.List, &r.Type, 0, init)
for lr := ll; lr != nil; lr = lr.Next { 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)) 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. // 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 n.Left != nil && n.Right != nil && needwritebarrier(n.Left, n.Right) {
if Debug_wb > 1 { if Debug_wb > 1 {
Warnl(int(n.Lineno), "marking %v for barrier", Nconv(n.Left, 0)) 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. // walk through argin parameters.
// generate and return code to allocate // generate and return code to allocate
// copies of escaped parameters to the heap. // 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 savet Iter
var v *Node var v *Node
var as *Node var as *Node
var nn *NodeList var nn []*Node
for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) { for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) {
v = t.Nname v = t.Nname
if v != nil && v.Sym != nil && v.Sym.Name[0] == '~' && v.Sym.Name[1] == 'r' { // unnamed result 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 // Defer might stop a panic and show the
// return values as they exist at the time of panic. // return values as they exist at the time of panic.
// Make sure to zero them on entry to the function. // 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 { if v == nil || v.Class&PHEAP == 0 {
@ -2573,13 +2580,13 @@ func paramstoheap(argin **Type, out int) *NodeList {
if prealloc[v] == nil { if prealloc[v] == nil {
prealloc[v] = callnew(v.Type) 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 { if v.Class&^PHEAP != PPARAMOUT {
as = Nod(OAS, v, v.Name.Param.Stackparam) as = Nod(OAS, v, v.Name.Param.Stackparam)
v.Name.Param.Stackparam.Typecheck = 1 v.Name.Param.Stackparam.Typecheck = 1
typecheck(&as, Etop) typecheck(&as, Etop)
as = applywritebarrier(as, &nn) as = applywritebarrier(as)
nn = list(nn, as) nn = append(nn, as)
} }
} }
@ -2587,17 +2594,17 @@ func paramstoheap(argin **Type, out int) *NodeList {
} }
// walk through argout parameters copying back to stack // walk through argout parameters copying back to stack
func returnsfromheap(argin **Type) *NodeList { func returnsfromheap(argin **Type) []*Node {
var savet Iter var savet Iter
var v *Node var v *Node
var nn *NodeList var nn []*Node
for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) { for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) {
v = t.Nname v = t.Nname
if v == nil || v.Class != PHEAP|PPARAMOUT { if v == nil || v.Class != PHEAP|PPARAMOUT {
continue continue
} }
nn = list(nn, Nod(OAS, v.Name.Param.Stackparam, v)) nn = append(nn, Nod(OAS, v.Name.Param.Stackparam, v))
} }
return nn return nn
@ -2610,11 +2617,11 @@ func heapmoves() {
lno := lineno lno := lineno
lineno = Curfn.Lineno lineno = Curfn.Lineno
nn := paramstoheap(getthis(Curfn.Type), 0) nn := paramstoheap(getthis(Curfn.Type), 0)
nn = concat(nn, paramstoheap(getinarg(Curfn.Type), 0)) nn = append(nn, paramstoheap(getinarg(Curfn.Type), 0)...)
nn = concat(nn, paramstoheap(Getoutarg(Curfn.Type), 1)) nn = append(nn, paramstoheap(Getoutarg(Curfn.Type), 1)...)
Curfn.Func.Enter = concat(Curfn.Func.Enter, nn) Curfn.Func.Enter.Append(nn...)
lineno = Curfn.Func.Endlineno lineno = Curfn.Func.Endlineno
Curfn.Func.Exit = returnsfromheap(Getoutarg(Curfn.Type)) Curfn.Func.Exit.Append(returnsfromheap(Getoutarg(Curfn.Type))...)
lineno = lno lineno = lno
} }

View File

@ -12,8 +12,6 @@ import (
) )
func defframe(ptxt *obj.Prog) { func defframe(ptxt *obj.Prog) {
var n *gc.Node
// fill in argument size, stack size // fill in argument size, stack size
ptxt.To.Type = obj.TYPE_TEXTSIZE ptxt.To.Type = obj.TYPE_TEXTSIZE
@ -30,8 +28,7 @@ func defframe(ptxt *obj.Prog) {
lo := hi lo := hi
// iterate through declarations - they are sorted in decreasing xoffset order. // iterate through declarations - they are sorted in decreasing xoffset order.
for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next { for _, n := range gc.Curfn.Func.Dcl {
n = l.N
if !n.Name.Needzero { if !n.Name.Needzero {
continue continue
} }

View File

@ -12,8 +12,6 @@ import (
) )
func defframe(ptxt *obj.Prog) { func defframe(ptxt *obj.Prog) {
var n *gc.Node
// fill in argument size, stack size // fill in argument size, stack size
ptxt.To.Type = obj.TYPE_TEXTSIZE ptxt.To.Type = obj.TYPE_TEXTSIZE
@ -30,8 +28,7 @@ func defframe(ptxt *obj.Prog) {
lo := hi lo := hi
// iterate through declarations - they are sorted in decreasing xoffset order. // iterate through declarations - they are sorted in decreasing xoffset order.
for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next { for _, n := range gc.Curfn.Func.Dcl {
n = l.N
if !n.Name.Needzero { if !n.Name.Needzero {
continue 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. // The hardware will generate undefined result.
// Also need to explicitly trap on division on zero, // Also need to explicitly trap on division on zero,
// the hardware will silently generate undefined result. // 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. // so always use DIVD/DIVDU.
t := nl.Type t := nl.Type

View File

@ -11,8 +11,6 @@ import (
) )
func defframe(ptxt *obj.Prog) { func defframe(ptxt *obj.Prog) {
var n *gc.Node
// fill in argument size, stack size // fill in argument size, stack size
ptxt.To.Type = obj.TYPE_TEXTSIZE ptxt.To.Type = obj.TYPE_TEXTSIZE
@ -28,8 +26,7 @@ func defframe(ptxt *obj.Prog) {
hi := int64(0) hi := int64(0)
lo := hi lo := hi
ax := uint32(0) ax := uint32(0)
for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next { for _, n := range gc.Curfn.Func.Dcl {
n = l.N
if !n.Name.Needzero { if !n.Name.Needzero {
continue continue
} }

View File

@ -284,7 +284,7 @@ func elimshortmov(g *gc.Graph) {
} }
if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST { 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. // from another register or constant can be movl.
// we don't switch to 32-bit arithmetic if it can // we don't switch to 32-bit arithmetic if it can
// change how the carry bit is set (and the carry bit is needed). // change how the carry bit is set (and the carry bit is needed).

66
src/cmd/dist/build.go vendored
View File

@ -6,6 +6,7 @@ package main
import ( import (
"bytes" "bytes"
"encoding/json"
"flag" "flag"
"fmt" "fmt"
"os" "os"
@ -463,6 +464,9 @@ var deptab = []struct {
{"runtime/internal/sys", []string{ {"runtime/internal/sys", []string{
"zversion.go", "zversion.go",
}}, }},
{"go/build", []string{
"zcgo.go",
}},
} }
// depsuffix records the allowed suffixes for source files. // depsuffix records the allowed suffixes for source files.
@ -478,6 +482,7 @@ var gentab = []struct {
}{ }{
{"zdefaultcc.go", mkzdefaultcc}, {"zdefaultcc.go", mkzdefaultcc},
{"zversion.go", mkzversion}, {"zversion.go", mkzversion},
{"zcgo.go", mkzcgo},
// not generated anymore, but delete the file if we see it // not generated anymore, but delete the file if we see it
{"enam.c", nil}, {"enam.c", nil},
@ -933,6 +938,7 @@ func usage() {
"clean deletes all built files\n" + "clean deletes all built files\n" +
"env [-p] print environment (-p: include $PATH)\n" + "env [-p] print environment (-p: include $PATH)\n" +
"install [dir] install individual directory\n" + "install [dir] install individual directory\n" +
"list [-json] list all supported platforms\n" +
"test [-h] run Go test(s)\n" + "test [-h] run Go test(s)\n" +
"version print Go version\n" + "version print Go version\n" +
"\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 // 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. // 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{ var cgoEnabled = map[string]bool{
"darwin/386": true, "darwin/386": true,
"darwin/amd64": true, "darwin/amd64": true,
@ -1072,19 +1082,31 @@ var cgoEnabled = map[string]bool{
"dragonfly/amd64": true, "dragonfly/amd64": true,
"freebsd/386": true, "freebsd/386": true,
"freebsd/amd64": true, "freebsd/amd64": true,
"freebsd/arm": false,
"linux/386": true, "linux/386": true,
"linux/amd64": true, "linux/amd64": true,
"linux/arm": true, "linux/arm": true,
"linux/arm64": true, "linux/arm64": true,
"linux/ppc64": false,
"linux/ppc64le": true, "linux/ppc64le": true,
"linux/mips64": false,
"linux/mips64le": false,
"android/386": true, "android/386": true,
"android/amd64": true, "android/amd64": true,
"android/arm": true, "android/arm": true,
"android/arm64": true,
"nacl/386": false,
"nacl/amd64p32": false,
"nacl/arm": false,
"netbsd/386": true, "netbsd/386": true,
"netbsd/amd64": true, "netbsd/amd64": true,
"netbsd/arm": true, "netbsd/arm": true,
"openbsd/386": true, "openbsd/386": true,
"openbsd/amd64": true, "openbsd/amd64": true,
"openbsd/arm": false,
"plan9/386": false,
"plan9/amd64": false,
"plan9/arm": false,
"solaris/amd64": true, "solaris/amd64": true,
"windows/386": true, "windows/386": true,
"windows/amd64": true, "windows/amd64": true,
@ -1128,7 +1150,7 @@ func defaulttarg() string {
fatal("current directory %s is not under %s", pwd, real_src) fatal("current directory %s is not under %s", pwd, real_src)
} }
pwd = pwd[len(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, "/") pwd = strings.TrimPrefix(pwd, "/")
return pwd return pwd
@ -1195,3 +1217,43 @@ func cmdversion() {
xflagparse(0) xflagparse(0)
xprintf("%s\n", findgoversion()) 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)
}
}

View File

@ -4,7 +4,10 @@
package main package main
import "fmt" import (
"bytes"
"fmt"
)
/* /*
* Helpers for building cmd/go and cmd/cgo. * Helpers for building cmd/go and cmd/cgo.
@ -37,3 +40,28 @@ func mkzdefaultcc(dir, file string) {
file = file[:i] + "c" + file[i:] file = file[:i] + "c" + file[i:]
writefile(out, file, writeSkipSame) 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)
}

View File

@ -21,6 +21,7 @@ var cmdtab = []struct {
{"clean", cmdclean}, {"clean", cmdclean},
{"env", cmdenv}, {"env", cmdenv},
{"install", cmdinstall}, {"install", cmdinstall},
{"list", cmdlist},
{"test", cmdtest}, {"test", cmdtest},
{"version", cmdversion}, {"version", cmdversion},
} }

14
src/cmd/dist/test.go vendored
View File

@ -441,6 +441,20 @@ func (t *tester) registerTests() {
return nil 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() { if t.cgoEnabled && t.goos != "android" && !t.iOS() {
// TODO(crawshaw): reenable on android and iOS // TODO(crawshaw): reenable on android and iOS

View File

@ -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", 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, ",")) 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() { defer func() {
if err != nil && err != errPrintedOutput { if err != nil && err != errPrintedOutput {
err = fmt.Errorf("go build %s: %v", a.p.ImportPath, err) 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 != "" { if a.cgo != nil && a.cgo.target != "" {
cgoExe = 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 { if err != nil {
return err 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. // so that it can give good error messages about forward declarations.
// Exceptions: a few standard packages have forward declarations for // Exceptions: a few standard packages have forward declarations for
// pieces supplied behind-the-scenes by package runtime. // 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 { if p.Standard {
switch p.ImportPath { switch p.ImportPath {
case "bytes", "net", "os", "runtime/pprof", "sync", "time": 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 usesCgo := false
cxx := len(root.p.CXXFiles) > 0 || len(root.p.SwigCXXFiles) > 0 cxx := len(root.p.CXXFiles) > 0 || len(root.p.SwigCXXFiles) > 0
objc := len(root.p.MFiles) > 0 objc := len(root.p.MFiles) > 0
fortran := len(root.p.FFiles) > 0
actionsSeen := make(map[*action]bool) actionsSeen := make(map[*action]bool)
// Make a pre-order depth-first traversal of the action graph, taking note of // 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 { if len(a.p.MFiles) > 0 {
objc = true objc = true
} }
if len(a.p.FFiles) > 0 {
fortran = true
}
} }
ldflags = append(ldflags, "-Wl,--whole-archive") ldflags = append(ldflags, "-Wl,--whole-archive")
@ -2768,6 +2777,17 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions
if objc { if objc {
ldflags = append(ldflags, "-lobjc") 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 { 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)) 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. // 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 { func (b *builder) ccompile(p *Package, out string, flags []string, file string, compiler []string) error {
file = mkAbs(p.Dir, file) file = mkAbs(p.Dir, file)
@ -2891,6 +2916,11 @@ func (b *builder) gxxCmd(objdir string) []string {
return b.ccompilerCmd("CXX", defaultCXX, objdir) 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 // ccompilerCmd returns a command line prefix for the given environment
// variable and using the default command when the variable is empty. // variable and using the default command when the variable is empty.
func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string { func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string {
@ -3009,8 +3039,8 @@ func envList(key, def string) []string {
return strings.Fields(v) return strings.Fields(v)
} }
// Return the flags to use when invoking the C or C++ compilers, or cgo. // 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, ldflags []string) { func (b *builder) cflags(p *Package, def bool) (cppflags, cflags, cxxflags, fflags, ldflags []string) {
var defaults string var defaults string
if def { if def {
defaults = "-g -O2" 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) cppflags = stringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS)
cflags = stringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS) cflags = stringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS)
cxxflags = stringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS) cxxflags = stringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS)
fflags = stringList(envList("CGO_FFLAGS", defaults), p.CgoFFLAGS)
ldflags = stringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS) ldflags = stringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS)
return return
} }
var cgoRe = regexp.MustCompile(`[/\\:]`) 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) { 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, cgoLDFLAGS := b.cflags(p, true) cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoFFLAGS, cgoLDFLAGS := b.cflags(p, true)
_, cgoexeCFLAGS, _, _ := b.cflags(p, false) _, cgoexeCFLAGS, _, _, _ := b.cflags(p, false)
cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...) cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...)
cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...) cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...)
// If we are compiling Objective-C code, then we need to link against libobjc // 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") 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" { if buildMSan && p.ImportPath != "runtime/cgo" {
cgoCFLAGS = append([]string{"-fsanitize=memory"}, cgoCFLAGS...) cgoCFLAGS = append([]string{"-fsanitize=memory"}, cgoCFLAGS...)
cgoLDFLAGS = append([]string{"-fsanitize=memory"}, cgoLDFLAGS...) 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) 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...) linkobj = append(linkobj, p.SysoFiles...)
dynobj := obj + "_cgo_.o" dynobj := obj + "_cgo_.o"
pie := (goarch == "arm" && goos == "linux") || goos == "android" 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. // 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) { 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 var cflags []string
if cxx { if cxx {
cflags = stringList(cgoCPPFLAGS, pcCFLAGS, cgoCXXFLAGS) cflags = stringList(cgoCPPFLAGS, pcCFLAGS, cgoCXXFLAGS)

View File

@ -119,6 +119,14 @@ func runGet(cmd *Command, args []string) {
delete(packageCache, name) 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) args = importPaths(args)
packagesForBuild(args) packagesForBuild(args)

View File

@ -2122,7 +2122,7 @@ func TestIssue7108(t *testing.T) {
// cmd/go: go test -a foo does not rebuild regexp. // cmd/go: go test -a foo does not rebuild regexp.
func TestIssue6844(t *testing.T) { func TestIssue6844(t *testing.T) {
if testing.Short() { 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) tg := testgo(t)
@ -2763,6 +2763,10 @@ func TestCgoConsistentResults(t *testing.T) {
if !canCgo { if !canCgo {
t.Skip("skipping because cgo not enabled") 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) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -2785,3 +2789,15 @@ func TestCgoConsistentResults(t *testing.T) {
t.Error("building cgotest twice did not produce the same output") 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")
}

View File

@ -51,6 +51,7 @@ syntax of package template. The default output is equivalent to -f
CXXFiles []string // .cc, .cxx and .cpp source files CXXFiles []string // .cc, .cxx and .cpp source files
MFiles []string // .m source files MFiles []string // .m source files
HFiles []string // .h, .hh, .hpp and .hxx 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 SFiles []string // .s source files
SwigFiles []string // .swig files SwigFiles []string // .swig files
SwigCXXFiles []string // .swigcxx 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 CgoCFLAGS []string // cgo: flags for C compiler
CgoCPPFLAGS []string // cgo: flags for C preprocessor CgoCPPFLAGS []string // cgo: flags for C preprocessor
CgoCXXFLAGS []string // cgo: flags for C++ compiler CgoCXXFLAGS []string // cgo: flags for C++ compiler
CgoFFLAGS []string // cgo: flags for Fortran compiler
CgoLDFLAGS []string // cgo: flags for linker CgoLDFLAGS []string // cgo: flags for linker
CgoPkgConfig []string // cgo: pkg-config names CgoPkgConfig []string // cgo: pkg-config names

View File

@ -50,6 +50,7 @@ type Package struct {
CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files
MFiles []string `json:",omitempty"` // .m source files MFiles []string `json:",omitempty"` // .m source files
HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx 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 SFiles []string `json:",omitempty"` // .s source files
SwigFiles []string `json:",omitempty"` // .swig files SwigFiles []string `json:",omitempty"` // .swig files
SwigCXXFiles []string `json:",omitempty"` // .swigcxx files SwigCXXFiles []string `json:",omitempty"` // .swigcxx files
@ -59,6 +60,7 @@ type Package struct {
CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler
CgoCPPFLAGS []string `json:",omitempty"` // cgo: flags for C preprocessor CgoCPPFLAGS []string `json:",omitempty"` // cgo: flags for C preprocessor
CgoCXXFLAGS []string `json:",omitempty"` // cgo: flags for C++ compiler 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 CgoLDFLAGS []string `json:",omitempty"` // cgo: flags for linker
CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names
@ -161,6 +163,7 @@ func (p *Package) copyBuild(pp *build.Package) {
p.CXXFiles = pp.CXXFiles p.CXXFiles = pp.CXXFiles
p.MFiles = pp.MFiles p.MFiles = pp.MFiles
p.HFiles = pp.HFiles p.HFiles = pp.HFiles
p.FFiles = pp.FFiles
p.SFiles = pp.SFiles p.SFiles = pp.SFiles
p.SwigFiles = pp.SwigFiles p.SwigFiles = pp.SwigFiles
p.SwigCXXFiles = pp.SwigCXXFiles p.SwigCXXFiles = pp.SwigCXXFiles
@ -909,6 +912,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
p.CXXFiles, p.CXXFiles,
p.MFiles, p.MFiles,
p.HFiles, p.HFiles,
p.FFiles,
p.SFiles, p.SFiles,
p.SysoFiles, p.SysoFiles,
p.SwigFiles, p.SwigFiles,
@ -1216,7 +1220,7 @@ var isGoRelease = strings.HasPrefix(runtime.Version(), "go1")
// an explicit data comparison. Specifically, we build a list of the // an explicit data comparison. Specifically, we build a list of the
// inputs to the build, compute its SHA1 hash, and record that as 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 // ``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 // object. If they differ, the list of inputs has changed, so the object
// is out of date and must be rebuilt. // 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 // 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. // 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 { for _, src := range srcs {
if olderThan(filepath.Join(p.Dir, src)) { if olderThan(filepath.Join(p.Dir, src)) {
return true return true

View File

@ -1206,11 +1206,11 @@ func (b *builder) notest(a *action) error {
return nil return nil
} }
// isTestMain tells whether fn is a TestMain(m *testing.M) function. // isTestFunc tells whether fn has the type of a testing function. arg
func isTestMain(fn *ast.FuncDecl) bool { // specifies the parameter type we look for: B, M or T.
if fn.Name.String() != "TestMain" || func isTestFunc(fn *ast.FuncDecl, arg string) bool {
fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
fn.Type.Params == nil || fn.Type.Params.List == nil ||
len(fn.Type.Params.List) != 1 || len(fn.Type.Params.List) != 1 ||
len(fn.Type.Params.List[0].Names) > 1 { len(fn.Type.Params.List[0].Names) > 1 {
return false return false
@ -1222,10 +1222,11 @@ func isTestMain(fn *ast.FuncDecl) bool {
// We can't easily check that the type is *testing.M // We can't easily check that the type is *testing.M
// because we don't know how testing has been imported, // because we don't know how testing has been imported,
// but at least check that it's *M or *something.M. // 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 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 true
} }
return false return false
@ -1344,16 +1345,24 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
} }
name := n.Name.String() name := n.Name.String()
switch { switch {
case isTestMain(n): case name == "TestMain" && isTestFunc(n, "M"):
if t.TestMain != nil { if t.TestMain != nil {
return errors.New("multiple definitions of TestMain") return errors.New("multiple definitions of TestMain")
} }
t.TestMain = &testFunc{pkg, name, ""} t.TestMain = &testFunc{pkg, name, ""}
*doImport, *seen = true, true *doImport, *seen = true, true
case isTest(name, "Test"): case isTest(name, "Test"):
err := checkTestFunc(n, "T")
if err != nil {
return err
}
t.Tests = append(t.Tests, testFunc{pkg, name, ""}) t.Tests = append(t.Tests, testFunc{pkg, name, ""})
*doImport, *seen = true, true *doImport, *seen = true, true
case isTest(name, "Benchmark"): case isTest(name, "Benchmark"):
err := checkTestFunc(n, "B")
if err != nil {
return err
}
t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, ""}) t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, ""})
*doImport, *seen = true, true *doImport, *seen = true, true
} }
@ -1372,6 +1381,15 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
return nil 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 type byOrder []*doc.Example
func (x byOrder) Len() int { return len(x) } func (x byOrder) Len() int { return len(x) }

View File

@ -530,7 +530,7 @@ func (r *objReader) parseArchive() error {
return errCorruptArchive return errCorruptArchive
} }
switch name { switch name {
case "__.SYMDEF", "__.GOSYMDEF", "__.PKGDEF": case "__.PKGDEF":
r.skip(size) r.skip(size)
default: default:
oldLimit := r.limit oldLimit := r.limit

View File

@ -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 { switch p.As {
// Treat MRC 15, 0, <reg>, C13, C0, 3 specially. // Treat MRC 15, 0, <reg>, C13, C0, 3 specially.
case AMRC: case AMRC:

View File

@ -444,6 +444,11 @@ const (
R_PLT1 R_PLT1
R_PLT2 R_PLT2
R_USEFIELD 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_POWER_TOC
R_GOTPCREL R_GOTPCREL
// R_JMPMIPS (only used on mips64) resolves to non-PC-relative target address // R_JMPMIPS (only used on mips64) resolves to non-PC-relative target address
@ -572,6 +577,7 @@ type Link struct {
Debugpcln int32 Debugpcln int32
Flag_shared int32 Flag_shared int32
Flag_dynlink bool Flag_dynlink bool
Flag_optimize bool
Bso *Biobuf Bso *Biobuf
Pathname string Pathname string
Windows int32 Windows int32
@ -617,6 +623,10 @@ type Link struct {
Data *LSym Data *LSym
Etext *LSym Etext *LSym
Edata *LSym Edata *LSym
// Cache of Progs
allocIdx int
progs [10000]Prog
} }
func (ctxt *Link) Diag(format string, args ...interface{}) { func (ctxt *Link) Diag(format string, args ...interface{}) {

View File

@ -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 * interchanged without changing semantics
*/ */
func depend(ctxt *obj.Link, sa, sb *Sch) bool { func depend(ctxt *obj.Link, sa, sb *Sch) bool {

View File

@ -116,6 +116,12 @@ func Writeobjdirect(ctxt *Link, b *Biobuf) {
} }
func Flushplist(ctxt *Link) { 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 flag int
var s *LSym var s *LSym
var p *Prog var p *Prog
@ -295,21 +301,34 @@ func Flushplist(ctxt *Link) {
for s := text; s != nil; s = s.Next { for s := text; s != nil; s = s.Next {
mkfwd(s) mkfwd(s)
linkpatch(ctxt, s) linkpatch(ctxt, s)
if ctxt.Flag_optimize {
ctxt.Arch.Follow(ctxt, s) ctxt.Arch.Follow(ctxt, s)
}
ctxt.Arch.Preprocess(ctxt, s) ctxt.Arch.Preprocess(ctxt, s)
ctxt.Arch.Assemble(ctxt, s) ctxt.Arch.Assemble(ctxt, s)
fieldtrack(ctxt, s) fieldtrack(ctxt, s)
linkpcln(ctxt, s) linkpcln(ctxt, s)
if freeProgs {
s.Text = nil
s.Etext = nil
}
} }
// Add to running list in ctxt. // Add to running list in ctxt.
if ctxt.Etext == nil { if text != nil {
if ctxt.Text == nil {
ctxt.Text = text ctxt.Text = text
} else { } else {
ctxt.Etext.Next = text ctxt.Etext.Next = text
} }
ctxt.Etext = etext ctxt.Etext = etext
}
ctxt.Plist = nil ctxt.Plist = nil
ctxt.Plast = nil
ctxt.Curp = nil
if freeProgs {
ctxt.freeProgs()
}
} }
func Writeobjfile(ctxt *Link, b *Biobuf) { func Writeobjfile(ctxt *Link, b *Biobuf) {

View File

@ -202,6 +202,7 @@ func linkpatch(ctxt *Link, sym *LSym) {
p.Pcond = q p.Pcond = q
} }
if ctxt.Flag_optimize {
for p := sym.Text; p != nil; p = p.Link { for p := sym.Text; p != nil; p = p.Link {
if p.Pcond != nil { if p.Pcond != nil {
p.Pcond = brloop(ctxt, p.Pcond) p.Pcond = brloop(ctxt, p.Pcond)
@ -213,3 +214,4 @@ func linkpatch(ctxt *Link, sym *LSym) {
} }
} }
} }
}

View File

@ -110,6 +110,7 @@ func Linknew(arch *LinkArch) *Link {
ctxt.Goarm = Getgoarm() ctxt.Goarm = Getgoarm()
} }
ctxt.Flag_optimize = true
return ctxt return ctxt
} }

View File

@ -287,6 +287,10 @@ func CConv(s uint8) string {
} }
func (p *Prog) String() string { func (p *Prog) String() string {
if p == nil {
return "<nil Prog>"
}
if p.Ctxt == nil { if p.Ctxt == nil {
return "<Prog without ctxt>" return "<Prog without ctxt>"
} }
@ -325,10 +329,23 @@ func (p *Prog) String() string {
} }
func (ctxt *Link) NewProg() *Prog { 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 p.Ctxt = ctxt
return p 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 { func (ctxt *Link) Line(n int) string {
return ctxt.LineHist.LineString(n) return ctxt.LineHist.LineString(n)

View File

@ -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. // That's what the tables expect.
switch p.As { switch p.As {
case ACMPPD, ACMPPS, ACMPSD, ACMPSS: case ACMPPD, ACMPPS, ACMPSD, ACMPSS:

View File

@ -109,7 +109,7 @@ func gentext() {
} }
// Preserve highest 8 bits of a, and do addition to lower 24-bit // 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 { func braddoff(a int32, b int32) int32 {
return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b)) return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b))
} }

View File

@ -156,7 +156,7 @@ func archinit() {
} }
case obj.Hdarwin: /* apple MACH */ case obj.Hdarwin: /* apple MACH */
ld.Debug['w'] = 1 // disable DWARF generataion ld.Debug['w'] = 1 // disable DWARF generation
ld.Machoinit() ld.Machoinit()
ld.HEADR = ld.INITIAL_MACHO_HEADR ld.HEADR = ld.INITIAL_MACHO_HEADR
if ld.INITTEXT == -1 { if ld.INITTEXT == -1 {

View File

@ -162,7 +162,7 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
rs := r.Xsym rs := r.Xsym
// ld64 has a bug handling MACHO_ARM64_RELOC_UNSIGNED with !extern relocation. // 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. // 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.Type == obj.SHOSTOBJ || r.Type == obj.R_CALLARM64 || r.Type == obj.R_ADDRARM64 || r.Type == obj.R_ADDR {
if rs.Dynid < 0 { if rs.Dynid < 0 {

View File

@ -116,7 +116,7 @@ const (
DW_CHILDREN_yes = 0x01 DW_CHILDREN_yes = 0x01
) )
// Not from the spec, but logicaly belongs here // Not from the spec, but logically belongs here
const ( const (
DW_CLS_ADDRESS = 0x01 + iota DW_CLS_ADDRESS = 0x01 + iota
DW_CLS_BLOCK DW_CLS_BLOCK

View File

@ -1066,9 +1066,9 @@ func readelfsym(elfobj *ElfObj, i int, sym *ElfSym, needSym int) (err error) {
} }
if needSym != 0 { if needSym != 0 {
// local names and hidden visiblity global names are unique // local names and hidden global names are unique
// and should only reference by its index, not name, so we // and should only be referenced by their index, not name, so we
// don't bother to add them into hash table // don't bother to add them into the hash table
s = linknewsym(Ctxt, sym.name, Ctxt.Version) s = linknewsym(Ctxt, sym.name, Ctxt.Version)
s.Type |= obj.SHIDDEN s.Type |= obj.SHIDDEN

View File

@ -255,10 +255,7 @@ var coutbuf struct {
f *os.File f *os.File
} }
const ( const pkgname = "__.PKGDEF"
symname = "__.GOSYMDEF"
pkgname = "__.PKGDEF"
)
var ( var (
// Set if we see an object compiled by the host compiler that is not // Set if we see an object compiled by the host compiler that is not
@ -781,7 +778,7 @@ func objfile(lib *Library) {
return return
} }
/* skip over optional __.GOSYMDEF and process __.PKGDEF */ /* process __.PKGDEF */
off := obj.Boffset(f) off := obj.Boffset(f)
var arhdr ArHdr var arhdr ArHdr
@ -792,15 +789,6 @@ func objfile(lib *Library) {
goto out 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) { if !strings.HasPrefix(arhdr.name, pkgname) {
Diag("%s: cannot find package header", lib.File) Diag("%s: cannot find package header", lib.File)
goto out goto out
@ -829,7 +817,7 @@ func objfile(lib *Library) {
* the individual symbols that are unused. * the individual symbols that are unused.
* *
* loading every object will also make it possible to * loading every object will also make it possible to
* load foreign objects not referenced by __.GOSYMDEF. * load foreign objects not referenced by __.PKGDEF.
*/ */
for { for {
l = nextar(f, off, &arhdr) l = nextar(f, off, &arhdr)

View File

@ -569,7 +569,7 @@ func Asmbmacho() {
if Linkmode == LinkInternal { if Linkmode == LinkInternal {
// For lldb, must say LC_VERSION_MIN_MACOSX or else // For lldb, must say LC_VERSION_MIN_MACOSX or else
// it won't know that this Mach-O binary is from OS X // 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 // Go on iOS uses linkmode=external, and linkmode=external
// adds this itself. So we only need this code for linkmode=internal // adds this itself. So we only need this code for linkmode=internal
// and we can assume OS X. // and we can assume OS X.

View File

@ -1340,7 +1340,7 @@ const (
addressOrder 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 // ordering criteria. The result is sorted in decreasing order for
// numeric quantities, alphabetically for text, and increasing for // numeric quantities, alphabetically for text, and increasing for
// addresses. // addresses.

View File

@ -408,7 +408,7 @@ func getMissingFunctionSource(filename string, asm map[int]nodes, start, end int
return fnodes, filename 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 // known prefixes and searching for the file on all parents of the
// current working dir. // current working dir.
func adjustSourcePath(path string) (*os.File, string, error) { func adjustSourcePath(path string) (*os.File, string, error) {

View File

@ -85,12 +85,12 @@ Flag: -copylocks
Locks that are erroneously passed by value. 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 Mistakes involving tests including functions with incorrect names or signatures
function signatures, or that document identifiers not in the package. and example tests that document identifiers not in the package.
Methods Methods

View File

@ -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
}

View File

@ -1,7 +1,9 @@
// Test of examples.
package testdata package testdata
import (
"testing"
)
// Buf is a ... // Buf is a ...
type Buf []byte 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_Append() // ERROR "ExamplePuffer_Append refers to unknown identifier: Puffer"
func ExamplePuffer_suffix() // ERROR "ExamplePuffer_suffix 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"

182
src/cmd/vet/tests.go Normal file
View File

@ -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)
}
}

View File

@ -130,7 +130,7 @@ func testNonceSafety(t *testing.T, c elliptic.Curve, tag string) {
} }
if r0.Cmp(r1) == 0 { 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)
} }
} }

View File

@ -46,7 +46,7 @@ loop:
BEQ aligned // aligned detected - skip copy BEQ aligned // aligned detected - skip copy
// Copy the unaligned source data into the aligned temporary buffer // 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 $buf, Rtable // to
MOVW $64, Rc0 // n MOVW $64, Rc0 // n
MOVM.IB [Rtable,Rdata,Rc0], (R13) MOVM.IB [Rtable,Rdata,Rc0], (R13)

View File

@ -11,8 +11,9 @@ import "io"
// Reader is a global, shared instance of a cryptographically // Reader is a global, shared instance of a cryptographically
// strong pseudo-random generator. // 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 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. // On Windows systems, Reader uses the CryptGenRandom API.
var Reader io.Reader var Reader io.Reader

Some files were not shown because too many files have changed in this diff Show More