mirror of
https://github.com/golang/go.git
synced 2025-05-14 11:54:38 +00:00
Merge "[dev.ssa] Merge remote-tracking branch 'origin/master' into ssamerge" into dev.ssa
This commit is contained in:
commit
acdb0da47d
18
.github/ISSUE_TEMPLATE
vendored
Normal file
18
.github/ISSUE_TEMPLATE
vendored
Normal 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
7
.github/PULL_REQUEST_TEMPLATE
vendored
Normal 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
1
.gitignore
vendored
@ -28,6 +28,7 @@ src/cmd/**/y.output
|
||||
src/cmd/cgo/zdefaultcc.go
|
||||
src/cmd/go/zdefaultcc.go
|
||||
src/cmd/internal/obj/zbootstrap.go
|
||||
src/go/build/zcgo.go
|
||||
src/go/doc/headscan
|
||||
src/runtime/internal/sys/zversion.go
|
||||
src/unicode/maketables
|
||||
|
@ -1,14 +1,20 @@
|
||||
Tools:
|
||||
|
||||
cmd/dist: add list subcommand to list all supported platforms (CL 19837)
|
||||
cmd/go: GO15VENDOREXPERIMENT gone, assumed on (CL 19615)
|
||||
cmd/link: "-X name value" form gone (CL 19614)
|
||||
cmd/compile: smaller binaries (many CLs)
|
||||
cmd/go, go/build: add support for Fortran (CL 19670, CL 4114)
|
||||
cmd/dist: introduce list subcommand to list all supported platforms (CL 19837)
|
||||
|
||||
Ports:
|
||||
|
||||
SOMETHING WILL HAPPEN
|
||||
We now require OpenBSD 5.6+ (CL 18219, crypto/rand using getentropy)
|
||||
plan9/arm support? Start at least.
|
||||
|
||||
API additions and behavior changes:
|
||||
|
||||
SOMETHING WILL HAPPEN
|
||||
|
||||
|
||||
runtime: add CallerFrames and Frames (CL 19869)
|
||||
testing/quick: now generates nil values (CL 16470)
|
||||
net/url: support query string without values (CL 19931)
|
||||
net/textproto: permit all valid token chars in CanonicalMIMEHeaderKey input (CL 18725)
|
||||
|
@ -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>
|
||||
</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>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -122,6 +122,16 @@ var ptrTests = []ptrTest{
|
||||
body: `i := 0; p := &S{p:&i}; s := p.a[:]; C.f(unsafe.Pointer(&s[0]))`,
|
||||
fail: false,
|
||||
},
|
||||
{
|
||||
// Passing the address of a slice of an array that is
|
||||
// an element in a struct, with a type conversion.
|
||||
name: "slice-ok-4",
|
||||
c: `typedef void* PV; void f(PV p) {}`,
|
||||
imports: []string{"unsafe"},
|
||||
support: `type S struct { p *int; a [4]byte }`,
|
||||
body: `i := 0; p := &S{p:&i}; C.f(C.PV(unsafe.Pointer(&p.a[0])))`,
|
||||
fail: false,
|
||||
},
|
||||
{
|
||||
// Passing the address of a static variable with no
|
||||
// pointers doesn't matter.
|
||||
|
9
misc/cgo/fortran/answer.f90
Normal file
9
misc/cgo/fortran/answer.f90
Normal 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
|
12
misc/cgo/fortran/fortran.go
Normal file
12
misc/cgo/fortran/fortran.go
Normal 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())
|
||||
}
|
13
misc/cgo/fortran/fortran_test.go
Normal file
13
misc/cgo/fortran/fortran_test.go
Normal 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)
|
||||
}
|
||||
}
|
3
misc/cgo/fortran/helloworld/helloworld.f90
Normal file
3
misc/cgo/fortran/helloworld/helloworld.f90
Normal file
@ -0,0 +1,3 @@
|
||||
program HelloWorldF90
|
||||
write(*,*) "Hello World!"
|
||||
end program HelloWorldF90
|
22
misc/cgo/fortran/test.bash
Executable file
22
misc/cgo/fortran/test.bash
Executable 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
|
@ -10,7 +10,7 @@ set -e
|
||||
|
||||
if [ ! -f src/libgo/libgo.go ]; then
|
||||
cwd=$(pwd)
|
||||
echo 'misc/cgo/testcshared/test.bash is running in $cwd' 1>&2
|
||||
echo "misc/cgo/testcshared/test.bash is running in $cwd" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
#
|
||||
# This script does not handle file names that contain spaces.
|
||||
|
||||
gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$')
|
||||
gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '\.go$')
|
||||
[ -z "$gofiles" ] && exit 0
|
||||
|
||||
unformatted=$(gofmt -l $gofiles)
|
||||
|
@ -705,7 +705,7 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
}
|
||||
}
|
||||
if err == io.EOF {
|
||||
// If we filled the buffer exactly, flush pre-emptively.
|
||||
// If we filled the buffer exactly, flush preemptively.
|
||||
if b.Available() == 0 {
|
||||
err = b.flush()
|
||||
} else {
|
||||
|
@ -351,7 +351,7 @@ func TestSplitError(t *testing.T) {
|
||||
// Test that an EOF is overridden by a user-generated scan error.
|
||||
func TestErrAtEOF(t *testing.T) {
|
||||
s := NewScanner(strings.NewReader("1 2 33"))
|
||||
// This spitter will fail on last entry, after s.err==EOF.
|
||||
// This splitter will fail on last entry, after s.err==EOF.
|
||||
split := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||
advance, token, err = ScanWords(data, atEOF)
|
||||
if len(token) > 1 {
|
||||
|
@ -32,13 +32,13 @@ if [ "$pattern" = "" ]; then
|
||||
pattern=.
|
||||
fi
|
||||
|
||||
# put linux, nacl first in the target list to get all the architectures up front.
|
||||
targets="$((ls runtime | sed -n 's/^rt0_\(.*\)_\(.*\)\.s/\1-\2/p'; echo linux-386-387 linux-arm-arm5) | sort | sed -e 's|linux-mips64x|linux-mips64 linux-mips64le|' | egrep -v android-arm | egrep "$pattern" | egrep 'linux|nacl')
|
||||
$(ls runtime | sed -n 's/^rt0_\(.*\)_\(.*\)\.s/\1-\2/p' | egrep -v 'android-arm|darwin-arm' | egrep "$pattern" | egrep -v 'linux|nacl')"
|
||||
|
||||
./make.bash || exit 1
|
||||
GOROOT="$(cd .. && pwd)"
|
||||
|
||||
# put linux, nacl first in the target list to get all the architectures up front.
|
||||
targets="$((../bin/go tool dist list | sed -n 's/^\(.*\)\/\(.*\)/\1-\2/p'; echo linux-386-387 linux-arm-arm5) | sort | egrep -v android-arm | egrep "$pattern" | egrep 'linux|nacl')
|
||||
$(../bin/go tool dist list | sed -n 's/^\(.*\)\/\(.*\)/\1-\2/p' | egrep -v 'android-arm|darwin-arm' | egrep "$pattern" | egrep -v 'linux|nacl')"
|
||||
|
||||
failed=false
|
||||
for target in $targets
|
||||
do
|
||||
|
@ -335,6 +335,41 @@ func TestIndexByteBig(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// test a small index across all page offsets
|
||||
func TestIndexByteSmall(t *testing.T) {
|
||||
b := make([]byte, 5015) // bigger than a page
|
||||
// Make sure we find the correct byte even when straddling a page.
|
||||
for i := 0; i <= len(b)-15; i++ {
|
||||
for j := 0; j < 15; j++ {
|
||||
b[i+j] = byte(100 + j)
|
||||
}
|
||||
for j := 0; j < 15; j++ {
|
||||
p := IndexByte(b[i:i+15], byte(100+j))
|
||||
if p != j {
|
||||
t.Errorf("IndexByte(%q, %d) = %d", b[i:i+15], 100+j, p)
|
||||
}
|
||||
}
|
||||
for j := 0; j < 15; j++ {
|
||||
b[i+j] = 0
|
||||
}
|
||||
}
|
||||
// Make sure matches outside the slice never trigger.
|
||||
for i := 0; i <= len(b)-15; i++ {
|
||||
for j := 0; j < 15; j++ {
|
||||
b[i+j] = 1
|
||||
}
|
||||
for j := 0; j < 15; j++ {
|
||||
p := IndexByte(b[i:i+15], byte(0))
|
||||
if p != -1 {
|
||||
t.Errorf("IndexByte(%q, %d) = %d", b[i:i+15], 0, p)
|
||||
}
|
||||
}
|
||||
for j := 0; j < 15; j++ {
|
||||
b[i+j] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexRune(t *testing.T) {
|
||||
for _, tt := range indexRuneTests {
|
||||
a := []byte(tt.a)
|
||||
@ -348,10 +383,12 @@ func TestIndexRune(t *testing.T) {
|
||||
|
||||
var bmbuf []byte
|
||||
|
||||
func BenchmarkIndexByte10(b *testing.B) { bmIndexByte(b, IndexByte, 10) }
|
||||
func BenchmarkIndexByte32(b *testing.B) { bmIndexByte(b, IndexByte, 32) }
|
||||
func BenchmarkIndexByte4K(b *testing.B) { bmIndexByte(b, IndexByte, 4<<10) }
|
||||
func BenchmarkIndexByte4M(b *testing.B) { bmIndexByte(b, IndexByte, 4<<20) }
|
||||
func BenchmarkIndexByte64M(b *testing.B) { bmIndexByte(b, IndexByte, 64<<20) }
|
||||
func BenchmarkIndexBytePortable10(b *testing.B) { bmIndexByte(b, IndexBytePortable, 10) }
|
||||
func BenchmarkIndexBytePortable32(b *testing.B) { bmIndexByte(b, IndexBytePortable, 32) }
|
||||
func BenchmarkIndexBytePortable4K(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<10) }
|
||||
func BenchmarkIndexBytePortable4M(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<20) }
|
||||
|
@ -20,7 +20,7 @@ The GOOS and GOARCH environment variables set the desired target.
|
||||
Flags:
|
||||
|
||||
-D value
|
||||
predefined symbol with optional simple value -D=identifer=value;
|
||||
predefined symbol with optional simple value -D=identifier=value;
|
||||
can be set multiple times
|
||||
-I value
|
||||
include directory; can be set multiple times
|
||||
|
@ -179,7 +179,7 @@ Diff:
|
||||
t.Errorf(format, args...)
|
||||
ok = false
|
||||
}
|
||||
obj.Flushplist(ctxt)
|
||||
obj.FlushplistNoFree(ctxt)
|
||||
|
||||
for p := top; p != nil; p = p.Link {
|
||||
if p.As == obj.ATEXT {
|
||||
|
@ -297,7 +297,7 @@ func (p *Parser) operand(a *obj.Addr) bool {
|
||||
p.errorf("illegal use of register list")
|
||||
}
|
||||
p.registerList(a)
|
||||
p.expect(scanner.EOF)
|
||||
p.expectOperandEnd()
|
||||
return true
|
||||
}
|
||||
|
||||
@ -331,7 +331,7 @@ func (p *Parser) operand(a *obj.Addr) bool {
|
||||
}
|
||||
}
|
||||
// fmt.Printf("REG %s\n", obj.Dconv(&emptyProg, 0, a))
|
||||
p.expect(scanner.EOF)
|
||||
p.expectOperandEnd()
|
||||
return true
|
||||
}
|
||||
|
||||
@ -363,7 +363,7 @@ func (p *Parser) operand(a *obj.Addr) bool {
|
||||
a.Type = obj.TYPE_FCONST
|
||||
a.Val = p.floatExpr()
|
||||
// fmt.Printf("FCONST %s\n", obj.Dconv(&emptyProg, 0, a))
|
||||
p.expect(scanner.EOF)
|
||||
p.expectOperandEnd()
|
||||
return true
|
||||
}
|
||||
if p.have(scanner.String) {
|
||||
@ -378,7 +378,7 @@ func (p *Parser) operand(a *obj.Addr) bool {
|
||||
a.Type = obj.TYPE_SCONST
|
||||
a.Val = str
|
||||
// fmt.Printf("SCONST %s\n", obj.Dconv(&emptyProg, 0, a))
|
||||
p.expect(scanner.EOF)
|
||||
p.expectOperandEnd()
|
||||
return true
|
||||
}
|
||||
a.Offset = int64(p.expr())
|
||||
@ -392,7 +392,7 @@ func (p *Parser) operand(a *obj.Addr) bool {
|
||||
a.Type = obj.TYPE_MEM
|
||||
}
|
||||
// fmt.Printf("CONST %d %s\n", a.Offset, obj.Dconv(&emptyProg, 0, a))
|
||||
p.expect(scanner.EOF)
|
||||
p.expectOperandEnd()
|
||||
return true
|
||||
}
|
||||
// fmt.Printf("offset %d \n", a.Offset)
|
||||
@ -402,7 +402,7 @@ func (p *Parser) operand(a *obj.Addr) bool {
|
||||
p.registerIndirect(a, prefix)
|
||||
// fmt.Printf("DONE %s\n", p.arch.Dconv(&emptyProg, 0, a))
|
||||
|
||||
p.expect(scanner.EOF)
|
||||
p.expectOperandEnd()
|
||||
return true
|
||||
}
|
||||
|
||||
@ -983,14 +983,19 @@ func (p *Parser) more() bool {
|
||||
|
||||
// get verifies that the next item has the expected type and returns it.
|
||||
func (p *Parser) get(expected lex.ScanToken) lex.Token {
|
||||
p.expect(expected)
|
||||
p.expect(expected, expected.String())
|
||||
return p.next()
|
||||
}
|
||||
|
||||
// expectOperandEnd verifies that the parsing state is properly at the end of an operand.
|
||||
func (p *Parser) expectOperandEnd() {
|
||||
p.expect(scanner.EOF, "end of operand")
|
||||
}
|
||||
|
||||
// expect verifies that the next item has the expected type. It does not consume it.
|
||||
func (p *Parser) expect(expected lex.ScanToken) {
|
||||
if p.peek() != expected {
|
||||
p.errorf("expected %s, found %s", expected, p.next())
|
||||
func (p *Parser) expect(expectedToken lex.ScanToken, expectedMessage string) {
|
||||
if p.peek() != expectedToken {
|
||||
p.errorf("expected %s, found %s", expectedMessage, p.next())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ func TestErroneous(t *testing.T) {
|
||||
{"TEXT", "%", "expect two or three operands for TEXT"},
|
||||
{"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", "$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.
|
||||
{"FUNCDATA", "", "expect two operands for FUNCDATA"},
|
||||
{"FUNCDATA", "(SB ", "expect two operands for FUNCDATA"},
|
||||
|
2
src/cmd/asm/internal/asm/testdata/arm64.s
vendored
2
src/cmd/asm/internal/asm/testdata/arm64.s
vendored
@ -237,7 +237,7 @@ again:
|
||||
//
|
||||
// LSTXR reg ',' addr ',' reg
|
||||
// {
|
||||
// outtcode($1, &$2, &$4, &$6);
|
||||
// outcode($1, &$2, &$4, &$6);
|
||||
// }
|
||||
LDAXRW (R0), R2
|
||||
STLXRW R1, (R0), R3
|
||||
|
@ -29,7 +29,7 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.Var(&D, "D", "predefined symbol with optional simple value -D=identifer=value; can be set multiple times")
|
||||
flag.Var(&D, "D", "predefined symbol with optional simple value -D=identifier=value; can be set multiple times")
|
||||
flag.Var(&I, "I", "include directory; can be set multiple times")
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ type Input struct {
|
||||
peekText string
|
||||
}
|
||||
|
||||
// NewInput returns a
|
||||
// NewInput returns an Input from the given path.
|
||||
func NewInput(name string) *Input {
|
||||
return &Input{
|
||||
// include directories: look in source dir, then -I directories.
|
||||
|
@ -77,7 +77,7 @@ func NewLexer(name string, ctxt *obj.Link) TokenReader {
|
||||
input := NewInput(name)
|
||||
fd, err := os.Open(name)
|
||||
if err != nil {
|
||||
log.Fatalf("asm: %s\n", err)
|
||||
log.Fatalf("%s\n", err)
|
||||
}
|
||||
input.Push(NewTokenizer(name, fd, fd))
|
||||
return input
|
||||
|
@ -26,7 +26,7 @@ func main() {
|
||||
|
||||
architecture := arch.Set(GOARCH)
|
||||
if architecture == nil {
|
||||
log.Fatalf("asm: unrecognized architecture %s", GOARCH)
|
||||
log.Fatalf("unrecognized architecture %s", GOARCH)
|
||||
}
|
||||
|
||||
flags.Parse()
|
||||
@ -66,7 +66,7 @@ func main() {
|
||||
obj.Writeobjdirect(ctxt, output)
|
||||
}
|
||||
if !ok || diag {
|
||||
log.Printf("asm: assembly of %s failed", flag.Arg(0))
|
||||
log.Printf("assembly of %s failed", flag.Arg(0))
|
||||
os.Remove(*flags.OutputFile)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
@ -31,9 +31,9 @@ See $GOROOT/misc/cgo/stdio and $GOROOT/misc/cgo/gmp for examples. See
|
||||
"C? Go? Cgo!" for an introduction to using cgo:
|
||||
https://golang.org/doc/articles/c_go_cgo.html.
|
||||
|
||||
CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS may be defined with pseudo #cgo
|
||||
directives within these comments to tweak the behavior of the C or C++
|
||||
compiler. Values defined in multiple directives are concatenated
|
||||
CFLAGS, CPPFLAGS, CXXFLAGS, FFLAGS and LDFLAGS may be defined with pseudo
|
||||
#cgo directives within these comments to tweak the behavior of the C, C++
|
||||
or Fortran compiler. Values defined in multiple directives are concatenated
|
||||
together. The directive can include a list of build constraints limiting its
|
||||
effect to systems satisfying one of the constraints
|
||||
(see https://golang.org/pkg/go/build/#hdr-Build_Constraints for details about the constraint syntax).
|
||||
@ -53,7 +53,7 @@ For example:
|
||||
// #include <png.h>
|
||||
import "C"
|
||||
|
||||
When building, the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS and
|
||||
When building, the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS and
|
||||
CGO_LDFLAGS environment variables are added to the flags derived from
|
||||
these directives. Package-specific flags should be set using the
|
||||
directives, not the environment variables, so that builds work in
|
||||
@ -62,10 +62,11 @@ unmodified environments.
|
||||
All the cgo CPPFLAGS and CFLAGS directives in a package are concatenated and
|
||||
used to compile C files in that package. All the CPPFLAGS and CXXFLAGS
|
||||
directives in a package are concatenated and used to compile C++ files in that
|
||||
package. All the LDFLAGS directives in any package in the program are
|
||||
concatenated and used at link time. All the pkg-config directives are
|
||||
concatenated and sent to pkg-config simultaneously to add to each appropriate
|
||||
set of command-line flags.
|
||||
package. All the CPPFLAGS and FFLAGS directives in a package are concatenated
|
||||
and used to compile Fortran files in that package. All the LDFLAGS directives
|
||||
in any package in the program are concatenated and used at link time. All the
|
||||
pkg-config directives are concatenated and sent to pkg-config simultaneously
|
||||
to add to each appropriate set of command-line flags.
|
||||
|
||||
When the cgo directives are parsed, any occurrence of the string ${SRCDIR}
|
||||
will be replaced by the absolute path to the directory containing the source
|
||||
@ -83,7 +84,8 @@ When the Go tool sees that one or more Go files use the special import
|
||||
"C", it will look for other non-Go files in the directory and compile
|
||||
them as part of the Go package. Any .c, .s, or .S files will be
|
||||
compiled with the C compiler. Any .cc, .cpp, or .cxx files will be
|
||||
compiled with the C++ compiler. Any .h, .hh, .hpp, or .hxx files will
|
||||
compiled with the C++ compiler. Any .f, .F, .for or .f90 files will be
|
||||
compiled with the fortran compiler. Any .h, .hh, .hpp, or .hxx files will
|
||||
not be compiled separately, but, if these header files are changed,
|
||||
the C and C++ files will be recompiled. The default C and C++
|
||||
compilers may be changed by the CC and CXX environment variables,
|
||||
|
@ -819,14 +819,17 @@ func (p *Package) hasSideEffects(f *File, x ast.Expr) bool {
|
||||
func (p *Package) isType(t ast.Expr) bool {
|
||||
switch t := t.(type) {
|
||||
case *ast.SelectorExpr:
|
||||
if t.Sel.Name != "Pointer" {
|
||||
return false
|
||||
}
|
||||
id, ok := t.X.(*ast.Ident)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return id.Name == "unsafe"
|
||||
if id.Name == "unsafe" && t.Sel.Name == "Pointer" {
|
||||
return true
|
||||
}
|
||||
if id.Name == "C" && typedef["_Ctype_"+t.Sel.Name] != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
case *ast.Ident:
|
||||
// TODO: This ignores shadowing.
|
||||
switch t.Name {
|
||||
|
@ -10,9 +10,10 @@ import (
|
||||
"cmd/internal/obj/x86"
|
||||
)
|
||||
|
||||
func defframe(ptxt *obj.Prog) {
|
||||
var n *gc.Node
|
||||
// no floating point in note handlers on Plan 9
|
||||
var isPlan9 = obj.Getgoos() == "plan9"
|
||||
|
||||
func defframe(ptxt *obj.Prog) {
|
||||
// fill in argument size, stack size
|
||||
ptxt.To.Type = obj.TYPE_TEXTSIZE
|
||||
|
||||
@ -31,8 +32,7 @@ func defframe(ptxt *obj.Prog) {
|
||||
x0 := uint32(0)
|
||||
|
||||
// iterate through declarations - they are sorted in decreasing xoffset order.
|
||||
for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
|
||||
n = l.N
|
||||
for _, n := range gc.Curfn.Func.Dcl {
|
||||
if !n.Name.Needzero {
|
||||
continue
|
||||
}
|
||||
@ -126,7 +126,7 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32, x0 *uin
|
||||
*ax = 1
|
||||
}
|
||||
p = appendpp(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo)
|
||||
} else if cnt <= int64(8*gc.Widthreg) {
|
||||
} else if !isPlan9 && cnt <= int64(8*gc.Widthreg) {
|
||||
if *x0 == 0 {
|
||||
p = appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
|
||||
*x0 = 1
|
||||
@ -139,12 +139,11 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32, x0 *uin
|
||||
if cnt%16 != 0 {
|
||||
p = appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo+cnt-int64(16))
|
||||
}
|
||||
} else if !gc.Nacl && (cnt <= int64(128*gc.Widthreg)) {
|
||||
} else if !gc.Nacl && !isPlan9 && (cnt <= int64(128*gc.Widthreg)) {
|
||||
if *x0 == 0 {
|
||||
p = appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
|
||||
*x0 = 1
|
||||
}
|
||||
|
||||
p = appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, frame+lo+dzDI(cnt), obj.TYPE_REG, x86.REG_DI, 0)
|
||||
p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, dzOff(cnt))
|
||||
p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg))
|
||||
@ -563,7 +562,7 @@ func clearfat(nl *gc.Node) {
|
||||
|
||||
w := nl.Type.Width
|
||||
|
||||
if w > 1024 || (gc.Nacl && w >= 64) {
|
||||
if w > 1024 || (w >= 64 && (gc.Nacl || isPlan9)) {
|
||||
var oldn1 gc.Node
|
||||
var n1 gc.Node
|
||||
savex(x86.REG_DI, &n1, &oldn1, nil, gc.Types[gc.Tptr])
|
||||
@ -630,6 +629,22 @@ func clearfat(nl *gc.Node) {
|
||||
}
|
||||
|
||||
func clearfat_tail(n1 *gc.Node, b int64) {
|
||||
if b >= 16 && isPlan9 {
|
||||
var z gc.Node
|
||||
gc.Nodconst(&z, gc.Types[gc.TUINT64], 0)
|
||||
q := b / 8
|
||||
for ; q > 0; q-- {
|
||||
n1.Type = z.Type
|
||||
gins(x86.AMOVQ, &z, n1)
|
||||
n1.Xoffset += 8
|
||||
b -= 8
|
||||
}
|
||||
if b != 0 {
|
||||
n1.Xoffset -= 8 - b
|
||||
gins(x86.AMOVQ, &z, n1)
|
||||
}
|
||||
return
|
||||
}
|
||||
if b >= 16 {
|
||||
var vec_zero gc.Node
|
||||
gc.Regalloc(&vec_zero, gc.Types[gc.TFLOAT64], nil)
|
||||
|
@ -427,7 +427,7 @@ func elimshortmov(g *gc.Graph) {
|
||||
}
|
||||
|
||||
if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST {
|
||||
// move or artihmetic into partial register.
|
||||
// move or arithmetic into partial register.
|
||||
// from another register or constant can be movl.
|
||||
// we don't switch to 64-bit arithmetic if it can
|
||||
// change how the carry bit is set (and the carry bit is needed).
|
||||
|
@ -11,8 +11,6 @@ import (
|
||||
)
|
||||
|
||||
func defframe(ptxt *obj.Prog) {
|
||||
var n *gc.Node
|
||||
|
||||
// fill in argument size, stack size
|
||||
ptxt.To.Type = obj.TYPE_TEXTSIZE
|
||||
|
||||
@ -28,8 +26,7 @@ func defframe(ptxt *obj.Prog) {
|
||||
hi := int64(0)
|
||||
lo := hi
|
||||
r0 := uint32(0)
|
||||
for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
|
||||
n = l.N
|
||||
for _, n := range gc.Curfn.Func.Dcl {
|
||||
if !n.Name.Needzero {
|
||||
continue
|
||||
}
|
||||
|
@ -12,8 +12,6 @@ import (
|
||||
)
|
||||
|
||||
func defframe(ptxt *obj.Prog) {
|
||||
var n *gc.Node
|
||||
|
||||
// fill in argument size, stack size
|
||||
ptxt.To.Type = obj.TYPE_TEXTSIZE
|
||||
|
||||
@ -37,8 +35,7 @@ func defframe(ptxt *obj.Prog) {
|
||||
lo := hi
|
||||
|
||||
// iterate through declarations - they are sorted in decreasing xoffset order.
|
||||
for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
|
||||
n = l.N
|
||||
for _, n := range gc.Curfn.Func.Dcl {
|
||||
if !n.Name.Needzero {
|
||||
continue
|
||||
}
|
||||
@ -146,7 +143,7 @@ func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) {
|
||||
// The hardware will generate undefined result.
|
||||
// Also need to explicitly trap on division on zero,
|
||||
// the hardware will silently generate undefined result.
|
||||
// DIVW will leave unpredicable result in higher 32-bit,
|
||||
// DIVW will leave unpredictable result in higher 32-bit,
|
||||
// so always use DIVD/DIVDU.
|
||||
t := nl.Type
|
||||
|
||||
|
626
src/cmd/compile/internal/gc/alg.go
Normal file
626
src/cmd/compile/internal/gc/alg.go
Normal 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
|
||||
}
|
@ -72,7 +72,7 @@ amount of space to hold the list without the need to grow it later.
|
||||
|
||||
All integer values use a variable-length encoding for compact representation.
|
||||
|
||||
If debugFormat is set, each integer and string value is preceeded by a marker
|
||||
If debugFormat is set, each integer and string value is preceded by a marker
|
||||
and position information in the encoding. This mechanism permits an importer
|
||||
to recognize immediately when it is out of sync. The importer recognizes this
|
||||
mode automatically (i.e., it can import export data produced with debugging
|
||||
|
@ -240,10 +240,9 @@ func (p *importer) typ() *Type {
|
||||
{
|
||||
saved := structpkg
|
||||
structpkg = tsym.Pkg
|
||||
addmethod(sym, n.Type, false, nointerface)
|
||||
addmethod(sym, n.Type, false, false)
|
||||
structpkg = saved
|
||||
}
|
||||
nointerface = false
|
||||
funchdr(n)
|
||||
|
||||
// (comment from go.y)
|
||||
|
@ -781,7 +781,7 @@ var sys_wbptr *Node
|
||||
|
||||
func cgen_wbptr(n, res *Node) {
|
||||
if Curfn != nil {
|
||||
if Curfn.Func.Nowritebarrier {
|
||||
if Curfn.Func.Pragma&Nowritebarrier != 0 {
|
||||
Yyerror("write barrier prohibited")
|
||||
}
|
||||
if Curfn.Func.WBLineno == 0 {
|
||||
@ -831,7 +831,7 @@ func cgen_wbptr(n, res *Node) {
|
||||
|
||||
func cgen_wbfat(n, res *Node) {
|
||||
if Curfn != nil {
|
||||
if Curfn.Func.Nowritebarrier {
|
||||
if Curfn.Func.Pragma&Nowritebarrier != 0 {
|
||||
Yyerror("write barrier prohibited")
|
||||
}
|
||||
if Curfn.Func.WBLineno == 0 {
|
||||
@ -2263,9 +2263,9 @@ func sgen_wb(n *Node, ns *Node, w int64, wb bool) {
|
||||
// If copying .args, that's all the results, so record definition sites
|
||||
// for them for the liveness analysis.
|
||||
if ns.Op == ONAME && ns.Sym.Name == ".args" {
|
||||
for l := Curfn.Func.Dcl; l != nil; l = l.Next {
|
||||
if l.N.Class == PPARAMOUT {
|
||||
Gvardef(l.N)
|
||||
for _, ln := range Curfn.Func.Dcl {
|
||||
if ln.Class == PPARAMOUT {
|
||||
Gvardef(ln)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2621,7 +2621,7 @@ func cgen_ret(n *Node) {
|
||||
if hasdefer {
|
||||
Ginscall(Deferreturn, 0)
|
||||
}
|
||||
Genlist(Curfn.Func.Exit)
|
||||
Genslice(Curfn.Func.Exit.Slice())
|
||||
p := Thearch.Gins(obj.ARET, nil, nil)
|
||||
if n != nil && n.Op == ORETJMP {
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
|
@ -67,9 +67,7 @@ func closurebody(body *NodeList) *Node {
|
||||
// ordinary ones in the symbol table; see oldname.
|
||||
// unhook them.
|
||||
// make the list of pointers for the closure call.
|
||||
var v *Node
|
||||
for l := func_.Func.Cvars; l != nil; l = l.Next {
|
||||
v = l.N
|
||||
for _, v := range func_.Func.Cvars.Slice() {
|
||||
v.Name.Param.Closure.Name.Param.Closure = v.Name.Param.Outer
|
||||
v.Name.Param.Outerexpr = oldname(v.Sym)
|
||||
}
|
||||
@ -78,10 +76,8 @@ func closurebody(body *NodeList) *Node {
|
||||
}
|
||||
|
||||
func typecheckclosure(func_ *Node, top int) {
|
||||
var n *Node
|
||||
|
||||
for l := func_.Func.Cvars; l != nil; l = l.Next {
|
||||
n = l.N.Name.Param.Closure
|
||||
for _, ln := range func_.Func.Cvars.Slice() {
|
||||
n := ln.Name.Param.Closure
|
||||
if !n.Name.Captured {
|
||||
n.Name.Captured = true
|
||||
if n.Name.Decldepth == 0 {
|
||||
@ -96,9 +92,9 @@ func typecheckclosure(func_ *Node, top int) {
|
||||
}
|
||||
}
|
||||
|
||||
for l := func_.Func.Dcl; l != nil; l = l.Next {
|
||||
if l.N.Op == ONAME && (l.N.Class == PPARAM || l.N.Class == PPARAMOUT) {
|
||||
l.N.Name.Decldepth = 1
|
||||
for _, ln := range func_.Func.Dcl {
|
||||
if ln.Op == ONAME && (ln.Class == PPARAM || ln.Class == PPARAMOUT) {
|
||||
ln.Name.Decldepth = 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,7 +194,8 @@ func makeclosure(func_ *Node) *Node {
|
||||
makefuncsym(xfunc.Func.Nname.Sym)
|
||||
|
||||
xfunc.Nbody = func_.Nbody
|
||||
xfunc.Func.Dcl = concat(func_.Func.Dcl, xfunc.Func.Dcl)
|
||||
xfunc.Func.Dcl = append(func_.Func.Dcl, xfunc.Func.Dcl...)
|
||||
func_.Func.Dcl = nil
|
||||
if xfunc.Nbody == nil {
|
||||
Fatalf("empty body - won't generate any code")
|
||||
}
|
||||
@ -220,16 +217,14 @@ func makeclosure(func_ *Node) *Node {
|
||||
// We use value capturing for values <= 128 bytes that are never reassigned
|
||||
// after capturing (effectively constant).
|
||||
func capturevars(xfunc *Node) {
|
||||
var v *Node
|
||||
var outer *Node
|
||||
|
||||
lno := int(lineno)
|
||||
lineno = xfunc.Lineno
|
||||
|
||||
func_ := xfunc.Func.Closure
|
||||
func_.Func.Enter = nil
|
||||
for l := func_.Func.Cvars; l != nil; l = l.Next {
|
||||
v = l.N
|
||||
func_.Func.Enter.Set(nil)
|
||||
for _, v := range func_.Func.Cvars.Slice() {
|
||||
if v.Type == nil {
|
||||
// if v->type is nil, it means v looked like it was
|
||||
// going to be used in the closure but wasn't.
|
||||
@ -270,7 +265,7 @@ func capturevars(xfunc *Node) {
|
||||
}
|
||||
|
||||
typecheck(&outer, Erv)
|
||||
func_.Func.Enter = list(func_.Func.Enter, outer)
|
||||
func_.Func.Enter.Append(outer)
|
||||
}
|
||||
|
||||
lineno = int32(lno)
|
||||
@ -309,11 +304,9 @@ func transformclosure(xfunc *Node) {
|
||||
original_dcl := xfunc.Func.Dcl
|
||||
xfunc.Func.Dcl = nil
|
||||
|
||||
var v *Node
|
||||
var addr *Node
|
||||
var fld *Type
|
||||
for l := func_.Func.Cvars; l != nil; l = l.Next {
|
||||
v = l.N
|
||||
for _, v := range func_.Func.Cvars.Slice() {
|
||||
if v.Op == OXXX {
|
||||
continue
|
||||
}
|
||||
@ -341,13 +334,13 @@ func transformclosure(xfunc *Node) {
|
||||
fld.Sym = fld.Nname.Sym
|
||||
|
||||
// Declare the new param and add it the first part of the input arguments.
|
||||
xfunc.Func.Dcl = list(xfunc.Func.Dcl, fld.Nname)
|
||||
xfunc.Func.Dcl = append(xfunc.Func.Dcl, fld.Nname)
|
||||
|
||||
*param = fld
|
||||
param = &fld.Down
|
||||
}
|
||||
*param = original_args
|
||||
xfunc.Func.Dcl = concat(xfunc.Func.Dcl, original_dcl)
|
||||
xfunc.Func.Dcl = append(xfunc.Func.Dcl, original_dcl...)
|
||||
|
||||
// Recalculate param offsets.
|
||||
if f.Type.Width > 0 {
|
||||
@ -357,19 +350,14 @@ func transformclosure(xfunc *Node) {
|
||||
xfunc.Type = f.Type // update type of ODCLFUNC
|
||||
} else {
|
||||
// The closure is not called, so it is going to stay as closure.
|
||||
nvar := 0
|
||||
|
||||
var body *NodeList
|
||||
var body []*Node
|
||||
offset := int64(Widthptr)
|
||||
var addr *Node
|
||||
var v *Node
|
||||
var cv *Node
|
||||
for l := func_.Func.Cvars; l != nil; l = l.Next {
|
||||
v = l.N
|
||||
for _, v := range func_.Func.Cvars.Slice() {
|
||||
if v.Op == OXXX {
|
||||
continue
|
||||
}
|
||||
nvar++
|
||||
|
||||
// cv refers to the field inside of closure OSTRUCTLIT.
|
||||
cv = Nod(OCLOSUREVAR, nil, nil)
|
||||
@ -386,8 +374,8 @@ func transformclosure(xfunc *Node) {
|
||||
// If it is a small variable captured by value, downgrade it to PAUTO.
|
||||
v.Class = PAUTO
|
||||
v.Ullman = 1
|
||||
xfunc.Func.Dcl = list(xfunc.Func.Dcl, v)
|
||||
body = list(body, Nod(OAS, v, cv))
|
||||
xfunc.Func.Dcl = append(xfunc.Func.Dcl, v)
|
||||
body = append(body, Nod(OAS, v, cv))
|
||||
} else {
|
||||
// Declare variable holding addresses taken from closure
|
||||
// and initialize in entry prologue.
|
||||
@ -396,19 +384,21 @@ func transformclosure(xfunc *Node) {
|
||||
addr.Class = PAUTO
|
||||
addr.Used = true
|
||||
addr.Name.Curfn = xfunc
|
||||
xfunc.Func.Dcl = list(xfunc.Func.Dcl, addr)
|
||||
xfunc.Func.Dcl = append(xfunc.Func.Dcl, addr)
|
||||
v.Name.Heapaddr = addr
|
||||
if v.Name.Byval {
|
||||
cv = Nod(OADDR, cv, nil)
|
||||
}
|
||||
body = list(body, Nod(OAS, addr, cv))
|
||||
body = append(body, Nod(OAS, addr, cv))
|
||||
}
|
||||
}
|
||||
|
||||
typechecklist(body, Etop)
|
||||
walkstmtlist(body)
|
||||
xfunc.Func.Enter = body
|
||||
xfunc.Func.Needctxt = nvar > 0
|
||||
if len(body) > 0 {
|
||||
typecheckslice(body, Etop)
|
||||
walkstmtslice(body)
|
||||
xfunc.Func.Enter.Set(body)
|
||||
xfunc.Func.Needctxt = true
|
||||
}
|
||||
}
|
||||
|
||||
lineno = int32(lno)
|
||||
@ -416,7 +406,7 @@ func transformclosure(xfunc *Node) {
|
||||
|
||||
func walkclosure(func_ *Node, init **NodeList) *Node {
|
||||
// If no closure vars, don't bother wrapping.
|
||||
if func_.Func.Cvars == nil {
|
||||
if len(func_.Func.Cvars.Slice()) == 0 {
|
||||
return func_.Func.Closure.Func.Nname
|
||||
}
|
||||
|
||||
@ -438,9 +428,7 @@ func walkclosure(func_ *Node, init **NodeList) *Node {
|
||||
|
||||
typ.List = list1(Nod(ODCLFIELD, newname(Lookup(".F")), typenod(Types[TUINTPTR])))
|
||||
var typ1 *Node
|
||||
var v *Node
|
||||
for l := func_.Func.Cvars; l != nil; l = l.Next {
|
||||
v = l.N
|
||||
for _, v := range func_.Func.Cvars.Slice() {
|
||||
if v.Op == OXXX {
|
||||
continue
|
||||
}
|
||||
@ -454,7 +442,7 @@ func walkclosure(func_ *Node, init **NodeList) *Node {
|
||||
clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil))
|
||||
clos.Esc = func_.Esc
|
||||
clos.Right.Implicit = true
|
||||
clos.List = concat(list1(Nod(OCFUNC, func_.Func.Closure.Func.Nname, nil)), func_.Func.Enter)
|
||||
clos.List = concat(list1(Nod(OCFUNC, func_.Func.Closure.Func.Nname, nil)), func_.Func.Enter.NodeList())
|
||||
|
||||
// Force type conversion from *struct to the func type.
|
||||
clos = Nod(OCONVNOP, clos, nil)
|
||||
@ -551,7 +539,7 @@ func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node {
|
||||
n = newname(Lookupf("a%d", i))
|
||||
i++
|
||||
n.Class = PPARAM
|
||||
xfunc.Func.Dcl = list(xfunc.Func.Dcl, n)
|
||||
xfunc.Func.Dcl = append(xfunc.Func.Dcl, n)
|
||||
callargs = list(callargs, n)
|
||||
fld = Nod(ODCLFIELD, n, typenod(t.Type))
|
||||
if t.Isddd {
|
||||
@ -570,7 +558,7 @@ func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node {
|
||||
n = newname(Lookupf("r%d", i))
|
||||
i++
|
||||
n.Class = PPARAMOUT
|
||||
xfunc.Func.Dcl = list(xfunc.Func.Dcl, n)
|
||||
xfunc.Func.Dcl = append(xfunc.Func.Dcl, n)
|
||||
retargs = list(retargs, n)
|
||||
l = list(l, Nod(ODCLFIELD, n, typenod(t.Type)))
|
||||
}
|
||||
@ -601,7 +589,7 @@ func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node {
|
||||
ptr.Used = true
|
||||
ptr.Name.Curfn = xfunc
|
||||
ptr.Xoffset = 0
|
||||
xfunc.Func.Dcl = list(xfunc.Func.Dcl, ptr)
|
||||
xfunc.Func.Dcl = append(xfunc.Func.Dcl, ptr)
|
||||
var body *NodeList
|
||||
if Isptr[rcvrtype.Etype] || Isinter(rcvrtype) {
|
||||
ptr.Name.Param.Ntype = typenod(rcvrtype)
|
||||
|
@ -187,7 +187,7 @@ func declare(n *Node, ctxt Class) {
|
||||
Fatalf("automatic outside function")
|
||||
}
|
||||
if Curfn != nil {
|
||||
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
|
||||
Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
|
||||
}
|
||||
if n.Op == OTYPE {
|
||||
declare_typegen++
|
||||
@ -426,7 +426,7 @@ func oldname(s *Sym) *Node {
|
||||
n.Name.Param.Closure = c
|
||||
c.Name.Param.Closure = n
|
||||
c.Xoffset = 0
|
||||
Curfn.Func.Cvars = list(Curfn.Func.Cvars, c)
|
||||
Curfn.Func.Cvars.Append(c)
|
||||
}
|
||||
|
||||
// return ref to closure var, not original
|
||||
@ -1449,8 +1449,13 @@ func funccompile(n *Node) {
|
||||
Funcdepth = n.Func.Depth + 1
|
||||
compile(n)
|
||||
Curfn = nil
|
||||
Pc = nil
|
||||
continpc = nil
|
||||
breakpc = nil
|
||||
Funcdepth = 0
|
||||
dclcontext = PEXTERN
|
||||
flushdata()
|
||||
obj.Flushplist(Ctxt) // convert from Prog list to machine code
|
||||
}
|
||||
|
||||
func funcsym(s *Sym) *Sym {
|
||||
@ -1525,7 +1530,7 @@ func checknowritebarrierrec() {
|
||||
|
||||
// Check nowritebarrierrec functions.
|
||||
for _, n := range list {
|
||||
if !n.Func.Nowritebarrierrec {
|
||||
if n.Func.Pragma&Nowritebarrierrec == 0 {
|
||||
continue
|
||||
}
|
||||
call, hasWB := c.best[n]
|
||||
|
@ -476,35 +476,35 @@ func escfunc(e *EscState, func_ *Node) {
|
||||
savefn := Curfn
|
||||
Curfn = func_
|
||||
|
||||
for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
|
||||
if ll.N.Op != ONAME {
|
||||
for _, ln := range Curfn.Func.Dcl {
|
||||
if ln.Op != ONAME {
|
||||
continue
|
||||
}
|
||||
llNE := e.nodeEscState(ll.N)
|
||||
switch ll.N.Class {
|
||||
llNE := e.nodeEscState(ln)
|
||||
switch ln.Class {
|
||||
// out params are in a loopdepth between the sink and all local variables
|
||||
case PPARAMOUT:
|
||||
llNE.Escloopdepth = 0
|
||||
|
||||
case PPARAM:
|
||||
llNE.Escloopdepth = 1
|
||||
if ll.N.Type != nil && !haspointers(ll.N.Type) {
|
||||
if ln.Type != nil && !haspointers(ln.Type) {
|
||||
break
|
||||
}
|
||||
if Curfn.Nbody == nil && !Curfn.Noescape {
|
||||
ll.N.Esc = EscHeap
|
||||
ln.Esc = EscHeap
|
||||
} else {
|
||||
ll.N.Esc = EscNone // prime for escflood later
|
||||
ln.Esc = EscNone // prime for escflood later
|
||||
}
|
||||
e.noesc = list(e.noesc, ll.N)
|
||||
e.noesc = list(e.noesc, ln)
|
||||
}
|
||||
}
|
||||
|
||||
// in a mutually recursive group we lose track of the return values
|
||||
if e.recursive {
|
||||
for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
|
||||
if ll.N.Op == ONAME && ll.N.Class == PPARAMOUT {
|
||||
escflows(e, &e.theSink, ll.N)
|
||||
for _, ln := range Curfn.Func.Dcl {
|
||||
if ln.Op == ONAME && ln.Class == PPARAMOUT {
|
||||
escflows(e, &e.theSink, ln)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -779,11 +779,14 @@ func esc(e *EscState, n *Node, up *Node) {
|
||||
ll = e.nodeEscState(n.List.N).Escretval
|
||||
}
|
||||
|
||||
for lr := Curfn.Func.Dcl; lr != nil && ll != nil; lr = lr.Next {
|
||||
if lr.N.Op != ONAME || lr.N.Class != PPARAMOUT {
|
||||
for _, lrn := range Curfn.Func.Dcl {
|
||||
if ll == nil {
|
||||
break
|
||||
}
|
||||
if lrn.Op != ONAME || lrn.Class != PPARAMOUT {
|
||||
continue
|
||||
}
|
||||
escassign(e, lr.N, ll.N)
|
||||
escassign(e, lrn, ll.N)
|
||||
ll = ll.Next
|
||||
}
|
||||
|
||||
@ -861,9 +864,7 @@ func esc(e *EscState, n *Node, up *Node) {
|
||||
// Link addresses of captured variables to closure.
|
||||
case OCLOSURE:
|
||||
var a *Node
|
||||
var v *Node
|
||||
for ll := n.Func.Cvars; ll != nil; ll = ll.Next {
|
||||
v = ll.N
|
||||
for _, v := range n.Func.Cvars.Slice() {
|
||||
if v.Op == OXXX { // unnamed out argument; see dcl.go:/^funcargs
|
||||
continue
|
||||
}
|
||||
@ -1870,16 +1871,16 @@ func esctag(e *EscState, func_ *Node) {
|
||||
savefn := Curfn
|
||||
Curfn = func_
|
||||
|
||||
for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
|
||||
if ll.N.Op != ONAME {
|
||||
for _, ln := range Curfn.Func.Dcl {
|
||||
if ln.Op != ONAME {
|
||||
continue
|
||||
}
|
||||
|
||||
switch ll.N.Esc & EscMask {
|
||||
switch ln.Esc & EscMask {
|
||||
case EscNone, // not touched by escflood
|
||||
EscReturn:
|
||||
if haspointers(ll.N.Type) { // don't bother tagging for scalars
|
||||
ll.N.Name.Param.Field.Note = mktag(int(ll.N.Esc))
|
||||
if haspointers(ln.Type) { // don't bother tagging for scalars
|
||||
ln.Name.Param.Field.Note = mktag(int(ln.Esc))
|
||||
}
|
||||
|
||||
case EscHeap, // touched by escflood, moved to heap
|
||||
|
@ -1730,10 +1730,42 @@ func Hconv(l *NodeList, flag int) string {
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func Hconvslice(l []*Node, flag int) string {
|
||||
if len(l) == 0 && fmtmode == FDbg {
|
||||
return "<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) {
|
||||
fmt.Printf("%s%v\n", s, Hconv(l, obj.FmtSign))
|
||||
}
|
||||
|
||||
func dumpslice(s string, l []*Node) {
|
||||
fmt.Printf("%s%v\n", s, Hconvslice(l, obj.FmtSign))
|
||||
}
|
||||
|
||||
func Dump(s string, n *Node) {
|
||||
fmt.Printf("%s [%p]%v\n", s, n, Nconv(n, obj.FmtSign))
|
||||
}
|
||||
|
@ -76,6 +76,9 @@ func addrescapes(n *Node) {
|
||||
oldfn := Curfn
|
||||
|
||||
Curfn = n.Name.Curfn
|
||||
if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE {
|
||||
Curfn = Curfn.Func.Closure
|
||||
}
|
||||
n.Name.Heapaddr = temp(Ptrto(n.Type))
|
||||
buf := fmt.Sprintf("&%v", n.Sym)
|
||||
n.Name.Heapaddr.Sym = Lookup(buf)
|
||||
@ -218,6 +221,12 @@ func Genlist(l *NodeList) {
|
||||
}
|
||||
}
|
||||
|
||||
func Genslice(l []*Node) {
|
||||
for _, n := range l {
|
||||
gen(n)
|
||||
}
|
||||
}
|
||||
|
||||
// generate code to start new proc running call n.
|
||||
func cgen_proc(n *Node, proc int) {
|
||||
switch n.Left.Op {
|
||||
@ -587,6 +596,10 @@ func Tempname(nn *Node, t *Type) {
|
||||
if Curfn == nil {
|
||||
Fatalf("no curfn for tempname")
|
||||
}
|
||||
if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE {
|
||||
Dump("Tempname", Curfn)
|
||||
Fatalf("adding tempname to wrong closure function")
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
Yyerror("tempname called with nil type")
|
||||
@ -606,7 +619,7 @@ func Tempname(nn *Node, t *Type) {
|
||||
n.Ullman = 1
|
||||
n.Esc = EscNever
|
||||
n.Name.Curfn = Curfn
|
||||
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
|
||||
Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
|
||||
|
||||
dowidth(t)
|
||||
n.Xoffset = 0
|
||||
|
@ -11,7 +11,8 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@ -28,7 +29,7 @@ func TestScanfRemoval(t *testing.T) {
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// Create source.
|
||||
src := path.Join(dir, "test.go")
|
||||
src := filepath.Join(dir, "test.go")
|
||||
f, err := os.Create(src)
|
||||
if err != nil {
|
||||
log.Fatalf("could not create source file: %v", err)
|
||||
@ -43,7 +44,7 @@ func main() {
|
||||
f.Close()
|
||||
|
||||
// Name of destination.
|
||||
dst := path.Join(dir, "test")
|
||||
dst := filepath.Join(dir, "test")
|
||||
|
||||
// Compile source.
|
||||
cmd := exec.Command("go", "build", "-o", dst, src)
|
||||
@ -62,3 +63,53 @@ func main() {
|
||||
log.Fatalf("scanf code not removed from helloworld")
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure -S prints assembly code. See issue 14515.
|
||||
func TestDashS(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
// Make a directory to work in.
|
||||
dir, err := ioutil.TempDir("", "issue14515-")
|
||||
if err != nil {
|
||||
log.Fatalf("could not create directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// Create source.
|
||||
src := filepath.Join(dir, "test.go")
|
||||
f, err := os.Create(src)
|
||||
if err != nil {
|
||||
log.Fatalf("could not create source file: %v", err)
|
||||
}
|
||||
f.Write([]byte(`
|
||||
package main
|
||||
import "fmt"
|
||||
func main() {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
`))
|
||||
f.Close()
|
||||
|
||||
// Compile source.
|
||||
cmd := exec.Command("go", "build", "-gcflags", "-S", "-o", filepath.Join(dir, "test"), src)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Fatalf("could not build target: %v", err)
|
||||
}
|
||||
|
||||
patterns := []string{
|
||||
// It is hard to look for actual instructions in an
|
||||
// arch-independent way. So we'll just look for
|
||||
// pseudo-ops that are arch-independent.
|
||||
"\tTEXT\t",
|
||||
"\tFUNCDATA\t",
|
||||
"\tPCDATA\t",
|
||||
}
|
||||
outstr := string(out)
|
||||
for _, p := range patterns {
|
||||
if !strings.Contains(outstr, p) {
|
||||
println(outstr)
|
||||
panic("can't find pattern " + p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,25 +26,6 @@ const (
|
||||
MaxStackVarSize = 10 * 1024 * 1024
|
||||
)
|
||||
|
||||
const (
|
||||
// These values are known by runtime.
|
||||
ANOEQ = iota
|
||||
AMEM0
|
||||
AMEM8
|
||||
AMEM16
|
||||
AMEM32
|
||||
AMEM64
|
||||
AMEM128
|
||||
ASTRING
|
||||
AINTER
|
||||
ANILINTER
|
||||
AFLOAT32
|
||||
AFLOAT64
|
||||
ACPLX64
|
||||
ACPLX128
|
||||
AMEM = 100
|
||||
)
|
||||
|
||||
const (
|
||||
// Maximum size in bits for Mpints before signalling
|
||||
// overflow and also mantissa precision for Mpflts.
|
||||
@ -122,10 +103,9 @@ type Pkg struct {
|
||||
}
|
||||
|
||||
type Sym struct {
|
||||
Lexical uint16
|
||||
Flags uint8
|
||||
Link *Sym
|
||||
Uniqgen uint32
|
||||
Link *Sym
|
||||
Importdef *Pkg // where imported definition was found
|
||||
Linkname string // link name
|
||||
|
||||
@ -425,12 +405,13 @@ var sizeof_String int // runtime sizeof(String)
|
||||
|
||||
var dotlist [10]Dlist // size is max depth of embeddeds
|
||||
|
||||
// lexlineno is the line number _after_ the most recently read rune.
|
||||
// In particular, it's advanced (or rewound) as newlines are read (or unread).
|
||||
var lexlineno int32
|
||||
|
||||
// lineno is the line number at the start of the most recently lexed token.
|
||||
var lineno int32
|
||||
|
||||
var prevlineno int32
|
||||
|
||||
var pragcgobuf string
|
||||
|
||||
var infile string
|
||||
@ -616,23 +597,10 @@ var flag_largemodel int
|
||||
// when the race detector is enabled.
|
||||
var instrumenting bool
|
||||
|
||||
// Pending annotations for next func declaration.
|
||||
var (
|
||||
noescape bool
|
||||
noinline bool
|
||||
norace bool
|
||||
nosplit bool
|
||||
nowritebarrier bool
|
||||
nowritebarrierrec bool
|
||||
systemstack bool
|
||||
)
|
||||
|
||||
var debuglive int
|
||||
|
||||
var Ctxt *obj.Link
|
||||
|
||||
var nointerface bool
|
||||
|
||||
var writearchive int
|
||||
|
||||
var bstdout obj.Biobuf
|
||||
|
@ -173,6 +173,18 @@ func dumpdata() {
|
||||
Clearp(Pc)
|
||||
}
|
||||
|
||||
func flushdata() {
|
||||
if dfirst == nil {
|
||||
return
|
||||
}
|
||||
newplist()
|
||||
*Pc = *dfirst
|
||||
Pc = dpc
|
||||
Clearp(Pc)
|
||||
dfirst = nil
|
||||
dpc = nil
|
||||
}
|
||||
|
||||
// Fixup instructions after allocauto (formerly compactframe) has moved all autos around.
|
||||
func fixautoused(p *obj.Prog) {
|
||||
for lp := &p; ; {
|
||||
@ -554,9 +566,7 @@ func nodarg(t *Type, fp int) *Node {
|
||||
}
|
||||
|
||||
if fp == 1 || fp == -1 {
|
||||
var n *Node
|
||||
for l := Curfn.Func.Dcl; l != nil; l = l.Next {
|
||||
n = l.N
|
||||
for _, n := range Curfn.Func.Dcl {
|
||||
if (n.Class == PPARAM || n.Class == PPARAMOUT) && !isblanksym(t.Sym) && n.Sym == t.Sym {
|
||||
return n
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ func caninl(fn *Node) {
|
||||
}
|
||||
|
||||
// If marked "go:noinline", don't inline
|
||||
if fn.Func.Noinline {
|
||||
if fn.Func.Pragma&Noinline != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
@ -150,7 +150,10 @@ func caninl(fn *Node) {
|
||||
|
||||
fn.Func.Nname.Func.Inl = fn.Nbody
|
||||
fn.Nbody = inlcopylist(fn.Func.Nname.Func.Inl)
|
||||
fn.Func.Nname.Func.Inldcl = inlcopylist(fn.Func.Nname.Name.Defn.Func.Dcl)
|
||||
inldcl := inlcopyslice(fn.Func.Nname.Name.Defn.Func.Dcl)
|
||||
if len(inldcl) > 0 {
|
||||
fn.Func.Nname.Func.Inldcl = &inldcl
|
||||
}
|
||||
fn.Func.Nname.Func.InlCost = int32(maxBudget - budget)
|
||||
|
||||
// hack, TODO, check for better way to link method nodes back to the thing with the ->inl
|
||||
@ -275,6 +278,18 @@ func inlcopy(n *Node) *Node {
|
||||
return m
|
||||
}
|
||||
|
||||
// Inlcopyslice is like inlcopylist, but for a slice.
|
||||
func inlcopyslice(ll []*Node) []*Node {
|
||||
r := make([]*Node, 0, len(ll))
|
||||
for _, ln := range ll {
|
||||
c := inlcopy(ln)
|
||||
if c != nil {
|
||||
r = append(r, c)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
|
||||
// calls made to inlineable functions. This is the external entry point.
|
||||
func inlcalls(fn *Node) {
|
||||
@ -556,10 +571,14 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) {
|
||||
|
||||
//dumplist("ninit pre", ninit);
|
||||
|
||||
var dcl *NodeList
|
||||
if fn.Name.Defn != nil { // local function
|
||||
dcl = fn.Func.Inldcl // imported function
|
||||
var dcl []*Node
|
||||
if fn.Name.Defn != nil {
|
||||
// local function
|
||||
if fn.Func.Inldcl != nil {
|
||||
dcl = *fn.Func.Inldcl
|
||||
}
|
||||
} else {
|
||||
// imported function
|
||||
dcl = fn.Func.Dcl
|
||||
}
|
||||
|
||||
@ -567,18 +586,18 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) {
|
||||
i := 0
|
||||
|
||||
// Make temp names to use instead of the originals
|
||||
for ll := dcl; ll != nil; ll = ll.Next {
|
||||
if ll.N.Class == PPARAMOUT { // return values handled below.
|
||||
for _, ln := range dcl {
|
||||
if ln.Class == PPARAMOUT { // return values handled below.
|
||||
continue
|
||||
}
|
||||
if ll.N.Op == ONAME {
|
||||
ll.N.Name.Inlvar = inlvar(ll.N)
|
||||
if ln.Op == ONAME {
|
||||
ln.Name.Inlvar = inlvar(ln)
|
||||
|
||||
// Typecheck because inlvar is not necessarily a function parameter.
|
||||
typecheck(&ll.N.Name.Inlvar, Erv)
|
||||
typecheck(&ln.Name.Inlvar, Erv)
|
||||
|
||||
if ll.N.Class&^PHEAP != PAUTO {
|
||||
ninit = list(ninit, Nod(ODCL, ll.N.Name.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs
|
||||
if ln.Class&^PHEAP != PAUTO {
|
||||
ninit = list(ninit, Nod(ODCL, ln.Name.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -852,7 +871,7 @@ func inlvar(var_ *Node) *Node {
|
||||
addrescapes(n)
|
||||
}
|
||||
|
||||
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
|
||||
Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
|
||||
return n
|
||||
}
|
||||
|
||||
@ -863,7 +882,7 @@ func retvar(t *Type, i int) *Node {
|
||||
n.Class = PAUTO
|
||||
n.Used = true
|
||||
n.Name.Curfn = Curfn // the calling function, not the called one
|
||||
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
|
||||
Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
|
||||
return n
|
||||
}
|
||||
|
||||
@ -875,7 +894,7 @@ func argvar(t *Type, i int) *Node {
|
||||
n.Class = PAUTO
|
||||
n.Used = true
|
||||
n.Name.Curfn = Curfn // the calling function, not the called one
|
||||
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
|
||||
Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
|
||||
return n
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -217,7 +217,11 @@ func mplshfixfix(a, b *Mpint) {
|
||||
|
||||
s := Mpgetfix(b)
|
||||
if s < 0 || s >= Mpprec {
|
||||
Yyerror("stupid shift: %d", s)
|
||||
msg := "shift count too large"
|
||||
if s < 0 {
|
||||
msg = "invalid negative shift count"
|
||||
}
|
||||
Yyerror("%s: %d", msg, s)
|
||||
Mpmovecfix(a, 0)
|
||||
return
|
||||
}
|
||||
@ -236,7 +240,7 @@ func mprshfixfix(a, b *Mpint) {
|
||||
|
||||
s := Mpgetfix(b)
|
||||
if s < 0 {
|
||||
Yyerror("stupid shift: %d", s)
|
||||
Yyerror("invalid negative shift count: %d", s)
|
||||
if a.Val.Sign() < 0 {
|
||||
Mpmovecfix(a, -1)
|
||||
} else {
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
"math"
|
||||
)
|
||||
|
||||
/// implements float arihmetic
|
||||
// implements float arithmetic
|
||||
|
||||
func newMpflt() *Mpflt {
|
||||
var a Mpflt
|
||||
|
@ -387,7 +387,7 @@ func ordercall(n *Node, order *Order) {
|
||||
|
||||
// Ordermapassign appends n to order->out, introducing temporaries
|
||||
// to make sure that all map assignments have the form m[k] = x,
|
||||
// where x is adressable.
|
||||
// where x is addressable.
|
||||
// (Orderexpr has already been called on n, so we know k is addressable.)
|
||||
//
|
||||
// If n is m[k] = x where x is not addressable, the rewrite is:
|
||||
@ -1138,7 +1138,7 @@ func orderexpr(np **Node, order *Order, lhs *Node) {
|
||||
}
|
||||
|
||||
case OCLOSURE:
|
||||
if n.Noescape && n.Func.Cvars != nil {
|
||||
if n.Noescape && len(n.Func.Cvars.Slice()) > 0 {
|
||||
prealloc[n] = ordertemp(Types[TUINT8], order, false) // walk will fill in correct type
|
||||
}
|
||||
|
||||
|
@ -102,6 +102,8 @@ func (p *parser) syntax_error(msg string) {
|
||||
}
|
||||
case LASOP:
|
||||
tok = goopnames[p.op] + "="
|
||||
case LINCOP:
|
||||
tok = goopnames[p.op] + goopnames[p.op]
|
||||
default:
|
||||
tok = tokstring(p.tok)
|
||||
}
|
||||
@ -110,11 +112,11 @@ func (p *parser) syntax_error(msg string) {
|
||||
}
|
||||
|
||||
// Like syntax_error, but reports error at given line rather than current lexer line.
|
||||
func (p *parser) syntax_error_at(lineno int32, msg string) {
|
||||
defer func(lineno int32) {
|
||||
lexlineno = lineno
|
||||
}(lexlineno)
|
||||
lexlineno = lineno
|
||||
func (p *parser) syntax_error_at(lno int32, msg string) {
|
||||
defer func(lno int32) {
|
||||
lineno = lno
|
||||
}(lineno)
|
||||
lineno = lno
|
||||
p.syntax_error(msg)
|
||||
}
|
||||
|
||||
@ -219,12 +221,11 @@ var tokstrings = map[int32]string{
|
||||
LANDAND: "&&",
|
||||
LANDNOT: "&^",
|
||||
LCOMM: "<-",
|
||||
LDEC: "--",
|
||||
LEQ: "==",
|
||||
LGE: ">=",
|
||||
LGT: ">",
|
||||
LIGNORE: "LIGNORE", // we should never see this one
|
||||
LINC: "++",
|
||||
LINCOP: "opop",
|
||||
LLE: "<=",
|
||||
LLSH: "<<",
|
||||
LLT: "<",
|
||||
@ -280,13 +281,11 @@ func (p *parser) package_() {
|
||||
defer p.trace("package_")()
|
||||
}
|
||||
|
||||
if p.got(LPACKAGE) {
|
||||
mkpackage(p.sym().Name)
|
||||
} else {
|
||||
prevlineno = lineno // see issue #13267
|
||||
if !p.got(LPACKAGE) {
|
||||
p.syntax_error("package statement must be first")
|
||||
errorexit()
|
||||
}
|
||||
mkpackage(p.sym().Name)
|
||||
}
|
||||
|
||||
// ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
|
||||
@ -335,20 +334,22 @@ func (p *parser) importdcl() {
|
||||
}
|
||||
|
||||
line := int32(parserline())
|
||||
path := p.val
|
||||
p.next()
|
||||
|
||||
importfile(&path, p.indent)
|
||||
if importpkg == nil {
|
||||
// We need to clear importpkg before calling p.next(),
|
||||
// otherwise it will affect lexlineno.
|
||||
// TODO(mdempsky): Fix this clumsy API.
|
||||
importfile(&p.val, p.indent)
|
||||
ipkg := importpkg
|
||||
importpkg = nil
|
||||
|
||||
p.next()
|
||||
if ipkg == nil {
|
||||
if nerrors == 0 {
|
||||
Fatalf("phase error in import")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ipkg := importpkg
|
||||
importpkg = nil
|
||||
|
||||
ipkg.Direct = true
|
||||
|
||||
if my == nil {
|
||||
@ -562,22 +563,13 @@ func (p *parser) simple_stmt(labelOk, rangeOk bool) *Node {
|
||||
stmt.Etype = EType(op) // rathole to pass opcode
|
||||
return stmt
|
||||
|
||||
case LINC:
|
||||
// expr LINC
|
||||
case LINCOP:
|
||||
// expr LINCOP
|
||||
p.next()
|
||||
|
||||
stmt := Nod(OASOP, lhs, Nodintconst(1))
|
||||
stmt.Implicit = true
|
||||
stmt.Etype = EType(OADD)
|
||||
return stmt
|
||||
|
||||
case LDEC:
|
||||
// expr LDEC
|
||||
p.next()
|
||||
|
||||
stmt := Nod(OASOP, lhs, Nodintconst(1))
|
||||
stmt.Implicit = true
|
||||
stmt.Etype = EType(OSUB)
|
||||
stmt.Etype = EType(p.op)
|
||||
return stmt
|
||||
|
||||
case ':':
|
||||
@ -689,7 +681,7 @@ func (p *parser) labeled_stmt(label *Node) *Node {
|
||||
ls = p.stmt()
|
||||
if ls == missing_stmt {
|
||||
// report error at line of ':' token
|
||||
p.syntax_error_at(prevlineno, "missing statement after label")
|
||||
p.syntax_error_at(label.Lineno, "missing statement after label")
|
||||
// we are already at the end of the labeled statement - no need to advance
|
||||
return missing_stmt
|
||||
}
|
||||
@ -1104,55 +1096,18 @@ func (p *parser) select_stmt() *Node {
|
||||
return hdr
|
||||
}
|
||||
|
||||
// TODO(gri) should have lexer return this info - no need for separate lookup
|
||||
// (issue 13244)
|
||||
var prectab = map[int32]struct {
|
||||
prec int // > 0 (0 indicates not found)
|
||||
op Op
|
||||
}{
|
||||
// not an expression anymore, but left in so we can give a good error
|
||||
// message when used in expression context
|
||||
LCOMM: {1, OSEND},
|
||||
|
||||
LOROR: {2, OOROR},
|
||||
|
||||
LANDAND: {3, OANDAND},
|
||||
|
||||
LEQ: {4, OEQ},
|
||||
LNE: {4, ONE},
|
||||
LLE: {4, OLE},
|
||||
LGE: {4, OGE},
|
||||
LLT: {4, OLT},
|
||||
LGT: {4, OGT},
|
||||
|
||||
'+': {5, OADD},
|
||||
'-': {5, OSUB},
|
||||
'|': {5, OOR},
|
||||
'^': {5, OXOR},
|
||||
|
||||
'*': {6, OMUL},
|
||||
'/': {6, ODIV},
|
||||
'%': {6, OMOD},
|
||||
'&': {6, OAND},
|
||||
LLSH: {6, OLSH},
|
||||
LRSH: {6, ORSH},
|
||||
LANDNOT: {6, OANDNOT},
|
||||
}
|
||||
|
||||
// Expression = UnaryExpr | Expression binary_op Expression .
|
||||
func (p *parser) bexpr(prec int) *Node {
|
||||
func (p *parser) bexpr(prec OpPrec) *Node {
|
||||
// don't trace bexpr - only leads to overly nested trace output
|
||||
|
||||
// prec is precedence of the prior/enclosing binary operator (if any),
|
||||
// so we only want to parse tokens of greater precedence.
|
||||
|
||||
x := p.uexpr()
|
||||
t := prectab[p.tok]
|
||||
for tprec := t.prec; tprec >= prec; tprec-- {
|
||||
for tprec == prec {
|
||||
for p.prec > prec {
|
||||
op, prec1 := p.op, p.prec
|
||||
p.next()
|
||||
y := p.bexpr(t.prec + 1)
|
||||
x = Nod(t.op, x, y)
|
||||
t = prectab[p.tok]
|
||||
tprec = t.prec
|
||||
}
|
||||
x = Nod(op, x, p.bexpr(prec1))
|
||||
}
|
||||
return x
|
||||
}
|
||||
@ -1162,7 +1117,7 @@ func (p *parser) expr() *Node {
|
||||
defer p.trace("expr")()
|
||||
}
|
||||
|
||||
return p.bexpr(1)
|
||||
return p.bexpr(0)
|
||||
}
|
||||
|
||||
func unparen(x *Node) *Node {
|
||||
@ -1611,13 +1566,15 @@ func (p *parser) new_name(sym *Sym) *Node {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *parser) dcl_name(sym *Sym) *Node {
|
||||
func (p *parser) dcl_name() *Node {
|
||||
if trace && Debug['x'] != 0 {
|
||||
defer p.trace("dcl_name")()
|
||||
}
|
||||
|
||||
symlineno := lineno
|
||||
sym := p.sym()
|
||||
if sym == nil {
|
||||
yyerrorl(int(prevlineno), "invalid declaration")
|
||||
yyerrorl(int(symlineno), "invalid declaration")
|
||||
return nil
|
||||
}
|
||||
return dclname(sym)
|
||||
@ -1894,25 +1851,21 @@ func (p *parser) xfndcl() *Node {
|
||||
}
|
||||
|
||||
p.want(LFUNC)
|
||||
f := p.fndcl()
|
||||
f := p.fndcl(p.pragma&Nointerface != 0)
|
||||
body := p.fnbody()
|
||||
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
if noescape && body != nil {
|
||||
Yyerror("can only use //go:noescape with external func implementations")
|
||||
}
|
||||
|
||||
f.Nbody = body
|
||||
f.Noescape = p.pragma&Noescape != 0
|
||||
if f.Noescape && body != nil {
|
||||
Yyerror("can only use //go:noescape with external func implementations")
|
||||
}
|
||||
f.Func.Pragma = p.pragma
|
||||
f.Func.Endlineno = lineno
|
||||
f.Noescape = noescape
|
||||
f.Func.Norace = norace
|
||||
f.Func.Nosplit = nosplit
|
||||
f.Func.Noinline = noinline
|
||||
f.Func.Nowritebarrier = nowritebarrier
|
||||
f.Func.Nowritebarrierrec = nowritebarrierrec
|
||||
f.Func.Systemstack = systemstack
|
||||
|
||||
funcbody(f)
|
||||
|
||||
return f
|
||||
@ -1923,7 +1876,7 @@ func (p *parser) xfndcl() *Node {
|
||||
// Function = Signature FunctionBody .
|
||||
// MethodDecl = "func" Receiver MethodName ( Function | Signature ) .
|
||||
// Receiver = Parameters .
|
||||
func (p *parser) fndcl() *Node {
|
||||
func (p *parser) fndcl(nointerface bool) *Node {
|
||||
if trace && Debug['x'] != 0 {
|
||||
defer p.trace("fndcl")()
|
||||
}
|
||||
@ -2059,8 +2012,7 @@ func (p *parser) hidden_fndcl() *Node {
|
||||
ss.Type = functype(s2.N, s6, s8)
|
||||
|
||||
checkwidth(ss.Type)
|
||||
addmethod(s4, ss.Type, false, nointerface)
|
||||
nointerface = false
|
||||
addmethod(s4, ss.Type, false, false)
|
||||
funchdr(ss)
|
||||
|
||||
// inl.C's inlnode in on a dotmeth node expects to find the inlineable body as
|
||||
@ -2141,18 +2093,10 @@ loop:
|
||||
testdclstack()
|
||||
}
|
||||
|
||||
noescape = false
|
||||
noinline = false
|
||||
nointerface = false
|
||||
norace = false
|
||||
nosplit = false
|
||||
nowritebarrier = false
|
||||
nowritebarrierrec = false
|
||||
systemstack = false
|
||||
// Reset p.pragma BEFORE advancing to the next token (consuming ';')
|
||||
// since comments before may set pragmas for the next function decl.
|
||||
p.pragma = 0
|
||||
|
||||
// Consume ';' AFTER resetting the above flags since
|
||||
// it may read the subsequent comment line which may
|
||||
// set the flags for the next function declaration.
|
||||
if p.tok != EOF && !p.got(';') {
|
||||
p.syntax_error("after top level declaration")
|
||||
p.advance(LVAR, LCONST, LTYPE, LFUNC)
|
||||
@ -2567,15 +2511,15 @@ func (p *parser) stmt() *Node {
|
||||
stmt := Nod(ORETURN, nil, nil)
|
||||
stmt.List = results
|
||||
if stmt.List == nil && Curfn != nil {
|
||||
for l := Curfn.Func.Dcl; l != nil; l = l.Next {
|
||||
if l.N.Class == PPARAM {
|
||||
for _, ln := range Curfn.Func.Dcl {
|
||||
if ln.Class == PPARAM {
|
||||
continue
|
||||
}
|
||||
if l.N.Class != PPARAMOUT {
|
||||
if ln.Class != PPARAMOUT {
|
||||
break
|
||||
}
|
||||
if l.N.Sym.Def != l.N {
|
||||
Yyerror("%s is shadowed during return", l.N.Sym.Name)
|
||||
if ln.Sym.Def != ln {
|
||||
Yyerror("%s is shadowed during return", ln.Sym.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2639,9 +2583,9 @@ func (p *parser) dcl_name_list() *NodeList {
|
||||
defer p.trace("dcl_name_list")()
|
||||
}
|
||||
|
||||
l := list1(p.dcl_name(p.sym()))
|
||||
l := list1(p.dcl_name())
|
||||
for p.got(',') {
|
||||
l = list(l, p.dcl_name(p.sym()))
|
||||
l = list(l, p.dcl_name())
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"cmd/internal/obj"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -218,6 +219,13 @@ func cmpstackvarlt(a, b *Node) bool {
|
||||
return a.Sym.Name < b.Sym.Name
|
||||
}
|
||||
|
||||
// byStackvar implements sort.Interface for []*Node using cmpstackvarlt.
|
||||
type byStackVar []*Node
|
||||
|
||||
func (s byStackVar) Len() int { return len(s) }
|
||||
func (s byStackVar) Less(i, j int) bool { return cmpstackvarlt(s[i], s[j]) }
|
||||
func (s byStackVar) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
// stkdelta records the stack offset delta for a node
|
||||
// during the compaction of the stack frame to remove
|
||||
// unused stack slots.
|
||||
@ -228,25 +236,23 @@ func allocauto(ptxt *obj.Prog) {
|
||||
Stksize = 0
|
||||
stkptrsize = 0
|
||||
|
||||
if Curfn.Func.Dcl == nil {
|
||||
if len(Curfn.Func.Dcl) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Mark the PAUTO's unused.
|
||||
for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
|
||||
if ll.N.Class == PAUTO {
|
||||
ll.N.Used = false
|
||||
for _, ln := range Curfn.Func.Dcl {
|
||||
if ln.Class == PAUTO {
|
||||
ln.Used = false
|
||||
}
|
||||
}
|
||||
|
||||
markautoused(ptxt)
|
||||
|
||||
listsort(&Curfn.Func.Dcl, cmpstackvarlt)
|
||||
sort.Sort(byStackVar(Curfn.Func.Dcl))
|
||||
|
||||
// Unused autos are at the end, chop 'em off.
|
||||
ll := Curfn.Func.Dcl
|
||||
|
||||
n := ll.N
|
||||
n := Curfn.Func.Dcl[0]
|
||||
if n.Class == PAUTO && n.Op == ONAME && !n.Used {
|
||||
// No locals used at all
|
||||
Curfn.Func.Dcl = nil
|
||||
@ -255,19 +261,17 @@ func allocauto(ptxt *obj.Prog) {
|
||||
return
|
||||
}
|
||||
|
||||
for ll := Curfn.Func.Dcl; ll.Next != nil; ll = ll.Next {
|
||||
n = ll.Next.N
|
||||
for i := 1; i < len(Curfn.Func.Dcl); i++ {
|
||||
n = Curfn.Func.Dcl[i]
|
||||
if n.Class == PAUTO && n.Op == ONAME && !n.Used {
|
||||
ll.Next = nil
|
||||
Curfn.Func.Dcl.End = ll
|
||||
Curfn.Func.Dcl = Curfn.Func.Dcl[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Reassign stack offsets of the locals that are still there.
|
||||
var w int64
|
||||
for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
|
||||
n = ll.N
|
||||
for _, n := range Curfn.Func.Dcl {
|
||||
if n.Class != PAUTO || n.Op != ONAME {
|
||||
continue
|
||||
}
|
||||
@ -299,12 +303,12 @@ func allocauto(ptxt *obj.Prog) {
|
||||
fixautoused(ptxt)
|
||||
|
||||
// The debug information needs accurate offsets on the symbols.
|
||||
for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
|
||||
if ll.N.Class != PAUTO || ll.N.Op != ONAME {
|
||||
for _, ln := range Curfn.Func.Dcl {
|
||||
if ln.Class != PAUTO || ln.Op != ONAME {
|
||||
continue
|
||||
}
|
||||
ll.N.Xoffset += stkdelta[ll.N]
|
||||
delete(stkdelta, ll.N)
|
||||
ln.Xoffset += stkdelta[ln]
|
||||
delete(stkdelta, ln)
|
||||
}
|
||||
}
|
||||
|
||||
@ -442,10 +446,10 @@ func compile(fn *Node) {
|
||||
if fn.Func.Needctxt {
|
||||
ptxt.From3.Offset |= obj.NEEDCTXT
|
||||
}
|
||||
if fn.Func.Nosplit {
|
||||
if fn.Func.Pragma&Nosplit != 0 {
|
||||
ptxt.From3.Offset |= obj.NOSPLIT
|
||||
}
|
||||
if fn.Func.Systemstack {
|
||||
if fn.Func.Pragma&Systemstack != 0 {
|
||||
ptxt.From.Sym.Cfunc = 1
|
||||
}
|
||||
|
||||
@ -467,16 +471,15 @@ func compile(fn *Node) {
|
||||
gtrack(tracksym(t))
|
||||
}
|
||||
|
||||
for l := fn.Func.Dcl; l != nil; l = l.Next {
|
||||
n = l.N
|
||||
for _, n := range fn.Func.Dcl {
|
||||
if n.Op != ONAME { // might be OTYPE or OLITERAL
|
||||
continue
|
||||
}
|
||||
switch n.Class {
|
||||
case PAUTO, PPARAM, PPARAMOUT:
|
||||
Nodconst(&nod1, Types[TUINTPTR], l.N.Type.Width)
|
||||
p = Thearch.Gins(obj.ATYPE, l.N, &nod1)
|
||||
p.From.Gotype = Linksym(ngotype(l.N))
|
||||
Nodconst(&nod1, Types[TUINTPTR], n.Type.Width)
|
||||
p = Thearch.Gins(obj.ATYPE, n, &nod1)
|
||||
p.From.Gotype = Linksym(ngotype(n))
|
||||
}
|
||||
}
|
||||
|
||||
@ -488,7 +491,7 @@ func compile(fn *Node) {
|
||||
ssafn.Free()
|
||||
return
|
||||
}
|
||||
Genlist(Curfn.Func.Enter)
|
||||
Genslice(Curfn.Func.Enter.Slice())
|
||||
Genlist(Curfn.Nbody)
|
||||
gclean()
|
||||
checklabels()
|
||||
|
@ -122,7 +122,7 @@ func addedge(from *BasicBlock, to *BasicBlock) {
|
||||
}
|
||||
|
||||
// Inserts prev before curr in the instruction
|
||||
// stream. Any control flow, such as branches or fall throughs, that target the
|
||||
// stream. Any control flow, such as branches or fall-throughs, that target the
|
||||
// existing instruction are adjusted to target the new instruction.
|
||||
func splicebefore(lv *Liveness, bb *BasicBlock, prev *obj.Prog, curr *obj.Prog) {
|
||||
// There may be other instructions pointing at curr,
|
||||
@ -198,8 +198,8 @@ func blockany(bb *BasicBlock, f func(*obj.Prog) bool) bool {
|
||||
// variables.
|
||||
func getvariables(fn *Node) []*Node {
|
||||
result := make([]*Node, 0, 0)
|
||||
for ll := fn.Func.Dcl; ll != nil; ll = ll.Next {
|
||||
if ll.N.Op == ONAME {
|
||||
for _, ln := range fn.Func.Dcl {
|
||||
if ln.Op == ONAME {
|
||||
// In order for GODEBUG=gcdead=1 to work, each bitmap needs
|
||||
// to contain information about all variables covered by the bitmap.
|
||||
// For local variables, the bitmap only covers the stkptrsize
|
||||
@ -219,24 +219,24 @@ func getvariables(fn *Node) []*Node {
|
||||
// Later, when we want to find the index of a node in the variables list,
|
||||
// we will check that n->curfn == curfn and n->opt > 0. Then n->opt - 1
|
||||
// is the index in the variables list.
|
||||
ll.N.SetOpt(nil)
|
||||
ln.SetOpt(nil)
|
||||
|
||||
// The compiler doesn't emit initializations for zero-width parameters or results.
|
||||
if ll.N.Type.Width == 0 {
|
||||
if ln.Type.Width == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
ll.N.Name.Curfn = Curfn
|
||||
switch ll.N.Class {
|
||||
ln.Name.Curfn = Curfn
|
||||
switch ln.Class {
|
||||
case PAUTO:
|
||||
if haspointers(ll.N.Type) {
|
||||
ll.N.SetOpt(int32(len(result)))
|
||||
result = append(result, ll.N)
|
||||
if haspointers(ln.Type) {
|
||||
ln.SetOpt(int32(len(result)))
|
||||
result = append(result, ln)
|
||||
}
|
||||
|
||||
case PPARAM, PPARAMOUT:
|
||||
ll.N.SetOpt(int32(len(result)))
|
||||
result = append(result, ll.N)
|
||||
ln.SetOpt(int32(len(result)))
|
||||
result = append(result, ln)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -798,8 +798,8 @@ func livenessprintcfg(lv *Liveness) {
|
||||
}
|
||||
|
||||
func checkauto(fn *Node, p *obj.Prog, n *Node) {
|
||||
for l := fn.Func.Dcl; l != nil; l = l.Next {
|
||||
if l.N.Op == ONAME && l.N.Class == PAUTO && l.N == n {
|
||||
for _, ln := range fn.Func.Dcl {
|
||||
if ln.Op == ONAME && ln.Class == PAUTO && ln == n {
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -810,8 +810,8 @@ func checkauto(fn *Node, p *obj.Prog, n *Node) {
|
||||
}
|
||||
|
||||
fmt.Printf("checkauto %v: %v (%p; class=%d) not found in %p %v\n", funcSym(Curfn), n, n, n.Class, p, p)
|
||||
for l := fn.Func.Dcl; l != nil; l = l.Next {
|
||||
fmt.Printf("\t%v (%p; class=%d)\n", l.N, l.N, l.N.Class)
|
||||
for _, ln := range fn.Func.Dcl {
|
||||
fmt.Printf("\t%v (%p; class=%d)\n", ln, ln, ln.Class)
|
||||
}
|
||||
Yyerror("checkauto: invariant lost")
|
||||
}
|
||||
@ -820,10 +820,8 @@ func checkparam(fn *Node, p *obj.Prog, n *Node) {
|
||||
if isfunny(n) {
|
||||
return
|
||||
}
|
||||
var a *Node
|
||||
var class Class
|
||||
for l := fn.Func.Dcl; l != nil; l = l.Next {
|
||||
a = l.N
|
||||
for _, a := range fn.Func.Dcl {
|
||||
class = a.Class &^ PHEAP
|
||||
if a.Op == ONAME && (class == PPARAM || class == PPARAMOUT) && a == n {
|
||||
return
|
||||
@ -831,8 +829,8 @@ func checkparam(fn *Node, p *obj.Prog, n *Node) {
|
||||
}
|
||||
|
||||
fmt.Printf("checkparam %v: %v (%p; class=%d) not found in %v\n", Curfn, n, n, n.Class, p)
|
||||
for l := fn.Func.Dcl; l != nil; l = l.Next {
|
||||
fmt.Printf("\t%v (%p; class=%d)\n", l.N, l.N, l.N.Class)
|
||||
for _, ln := range fn.Func.Dcl {
|
||||
fmt.Printf("\t%v (%p; class=%d)\n", ln, ln, ln.Class)
|
||||
}
|
||||
Yyerror("checkparam: invariant lost")
|
||||
}
|
||||
@ -1815,9 +1813,9 @@ func liveness(fn *Node, firstp *obj.Prog, argssym *Sym, livesym *Sym) {
|
||||
onebitwritesymbol(lv.argslivepointers, argssym)
|
||||
|
||||
// Free everything.
|
||||
for l := fn.Func.Dcl; l != nil; l = l.Next {
|
||||
if l.N != nil {
|
||||
l.N.SetOpt(nil)
|
||||
for _, ln := range fn.Func.Dcl {
|
||||
if ln != nil {
|
||||
ln.SetOpt(nil)
|
||||
}
|
||||
}
|
||||
freeliveness(lv)
|
||||
|
@ -138,15 +138,16 @@ func fixjmp(firstp *obj.Prog) {
|
||||
fmt.Printf("%v\n", p)
|
||||
}
|
||||
if p.As != obj.ACALL && p.To.Type == obj.TYPE_BRANCH && p.To.Val.(*obj.Prog) != nil && p.To.Val.(*obj.Prog).As == obj.AJMP {
|
||||
if Debug['N'] == 0 {
|
||||
p.To.Val = chasejmp(p.To.Val.(*obj.Prog), &jmploop)
|
||||
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||||
fmt.Printf("->%v\n", p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p.Opt = dead
|
||||
}
|
||||
|
||||
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
@ -186,7 +187,7 @@ func fixjmp(firstp *obj.Prog) {
|
||||
|
||||
// pass 4: elide JMP to next instruction.
|
||||
// only safe if there are no jumps to JMPs anymore.
|
||||
if jmploop == 0 {
|
||||
if jmploop == 0 && Debug['N'] == 0 {
|
||||
var last *obj.Prog
|
||||
for p := firstp; p != nil; p = p.Link {
|
||||
if p.As == obj.AJMP && p.To.Type == obj.TYPE_BRANCH && p.To.Val == p.Link {
|
||||
@ -217,22 +218,22 @@ func fixjmp(firstp *obj.Prog) {
|
||||
// Control flow analysis. The Flow structures hold predecessor and successor
|
||||
// information as well as basic loop analysis.
|
||||
//
|
||||
// graph = flowstart(firstp, 0);
|
||||
// graph = Flowstart(firstp, nil)
|
||||
// ... use flow graph ...
|
||||
// flowend(graph); // free graph
|
||||
// Flowend(graph) // free graph
|
||||
//
|
||||
// Typical uses of the flow graph are to iterate over all the flow-relevant instructions:
|
||||
//
|
||||
// for(f = graph->start; f != nil; f = f->link)
|
||||
// for f := graph.Start; f != nil; f = f.Link {}
|
||||
//
|
||||
// or, given an instruction f, to iterate over all the predecessors, which is
|
||||
// f->p1 and this list:
|
||||
// f.P1 and this list:
|
||||
//
|
||||
// for(f2 = f->p2; f2 != nil; f2 = f2->p2link)
|
||||
// for f2 := f.P2; f2 != nil; f2 = f2.P2link {}
|
||||
//
|
||||
// The size argument to flowstart specifies an amount of zeroed memory
|
||||
// to allocate in every f->data field, for use by the client.
|
||||
// If size == 0, f->data will be nil.
|
||||
// The second argument (newData) to Flowstart specifies a func to create object
|
||||
// for every f.Data field, for use by the client.
|
||||
// If newData is nil, f.Data will be nil.
|
||||
|
||||
var flowmark int
|
||||
|
||||
@ -471,8 +472,8 @@ func flowrpo(g *Graph) {
|
||||
me = r1.Rpo
|
||||
d = -1
|
||||
|
||||
// rpo2r[r->rpo] == r protects against considering dead code,
|
||||
// which has r->rpo == 0.
|
||||
// rpo2r[r.Rpo] == r protects against considering dead code,
|
||||
// which has r.Rpo == 0.
|
||||
if r1.P1 != nil && rpo2r[r1.P1.Rpo] == r1.P1 && r1.P1.Rpo < me {
|
||||
d = r1.P1.Rpo
|
||||
}
|
||||
@ -588,8 +589,8 @@ func mergetemp(firstp *obj.Prog) {
|
||||
|
||||
// Build list of all mergeable variables.
|
||||
var vars []*TempVar
|
||||
for l := Curfn.Func.Dcl; l != nil; l = l.Next {
|
||||
if n := l.N; canmerge(n) {
|
||||
for _, n := range Curfn.Func.Dcl {
|
||||
if canmerge(n) {
|
||||
v := &TempVar{}
|
||||
vars = append(vars, v)
|
||||
n.SetOpt(v)
|
||||
@ -684,7 +685,7 @@ func mergetemp(firstp *obj.Prog) {
|
||||
|
||||
// Traverse live range of each variable to set start, end.
|
||||
// Each flood uses a new value of gen so that we don't have
|
||||
// to clear all the r->active words after each variable.
|
||||
// to clear all the r.Active words after each variable.
|
||||
gen := uint32(0)
|
||||
|
||||
for _, v := range vars {
|
||||
@ -818,22 +819,15 @@ func mergetemp(firstp *obj.Prog) {
|
||||
}
|
||||
|
||||
// Delete merged nodes from declaration list.
|
||||
for lp := &Curfn.Func.Dcl; ; {
|
||||
l := *lp
|
||||
if l == nil {
|
||||
break
|
||||
}
|
||||
|
||||
Curfn.Func.Dcl.End = l
|
||||
n := l.N
|
||||
dcl := make([]*Node, 0, len(Curfn.Func.Dcl)-nkill)
|
||||
for _, n := range Curfn.Func.Dcl {
|
||||
v, _ := n.Opt().(*TempVar)
|
||||
if v != nil && (v.merge != nil || v.removed) {
|
||||
*lp = l.Next
|
||||
continue
|
||||
}
|
||||
|
||||
lp = &l.Next
|
||||
dcl = append(dcl, n)
|
||||
}
|
||||
Curfn.Func.Dcl = dcl
|
||||
|
||||
// Clear aux structures.
|
||||
for _, v := range vars {
|
||||
@ -910,7 +904,7 @@ func varkillwalk(v *TempVar, f0 *Flow, gen uint32) {
|
||||
// from memory without being rechecked. Other variables need to be checked on
|
||||
// each load.
|
||||
|
||||
var killed int // f->data is either nil or &killed
|
||||
var killed int // f.Data is either nil or &killed
|
||||
|
||||
func nilopt(firstp *obj.Prog) {
|
||||
g := Flowstart(firstp, nil)
|
||||
|
@ -50,7 +50,7 @@ func ispkgin(pkgs []string) bool {
|
||||
}
|
||||
|
||||
func instrument(fn *Node) {
|
||||
if ispkgin(omit_pkgs) || fn.Func.Norace {
|
||||
if ispkgin(omit_pkgs) || fn.Func.Pragma&Norace != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@ func instrument(fn *Node) {
|
||||
instrumentlist(fn.Nbody, nil)
|
||||
|
||||
// nothing interesting for race detector in fn->enter
|
||||
instrumentlist(fn.Func.Exit, nil)
|
||||
instrumentslice(fn.Func.Exit.Slice(), nil)
|
||||
}
|
||||
|
||||
if flag_race != 0 {
|
||||
@ -71,18 +71,18 @@ func instrument(fn *Node) {
|
||||
nodpc.Type = Types[TUINTPTR]
|
||||
nodpc.Xoffset = int64(-Widthptr)
|
||||
nd := mkcall("racefuncenter", nil, nil, nodpc)
|
||||
fn.Func.Enter = concat(list1(nd), fn.Func.Enter)
|
||||
fn.Func.Enter.Set(append([]*Node{nd}, fn.Func.Enter.Slice()...))
|
||||
nd = mkcall("racefuncexit", nil, nil)
|
||||
fn.Func.Exit = list(fn.Func.Exit, nd)
|
||||
fn.Func.Exit.Append(nd)
|
||||
}
|
||||
|
||||
if Debug['W'] != 0 {
|
||||
s := fmt.Sprintf("after instrument %v", fn.Func.Nname.Sym)
|
||||
dumplist(s, fn.Nbody)
|
||||
s = fmt.Sprintf("enter %v", fn.Func.Nname.Sym)
|
||||
dumplist(s, fn.Func.Enter)
|
||||
dumpslice(s, fn.Func.Enter.Slice())
|
||||
s = fmt.Sprintf("exit %v", fn.Func.Nname.Sym)
|
||||
dumplist(s, fn.Func.Exit)
|
||||
dumpslice(s, fn.Func.Exit.Slice())
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,6 +100,18 @@ func instrumentlist(l *NodeList, init **NodeList) {
|
||||
}
|
||||
}
|
||||
|
||||
func instrumentslice(l []*Node, init **NodeList) {
|
||||
for i := range l {
|
||||
var instr *NodeList
|
||||
instrumentnode(&l[i], &instr, 0, 0)
|
||||
if init == nil {
|
||||
l[i].Ninit = concat(l[i].Ninit, instr)
|
||||
} else {
|
||||
*init = concat(*init, instr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// walkexpr and walkstmt combined
|
||||
// walks the tree and adds calls to the
|
||||
// instrumentation code to top-level (statement) nodes' init
|
||||
|
@ -491,17 +491,11 @@ func dextratype(sym *Sym, off int, t *Type, ptroff int) int {
|
||||
|
||||
ot := off
|
||||
s := sym
|
||||
if t.Sym != nil {
|
||||
ot = dgostringptr(s, ot, t.Sym.Name)
|
||||
if t != Types[t.Etype] && t != errortype {
|
||||
if t.Sym != nil && t != Types[t.Etype] && t != errortype {
|
||||
ot = dgopkgpath(s, ot, t.Sym.Pkg)
|
||||
} else {
|
||||
ot = dgostringptr(s, ot, "")
|
||||
}
|
||||
} else {
|
||||
ot = dgostringptr(s, ot, "")
|
||||
ot = dgostringptr(s, ot, "")
|
||||
}
|
||||
|
||||
// slice header
|
||||
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)
|
||||
}
|
||||
|
||||
var sptr *Sym
|
||||
tptr := Ptrto(t)
|
||||
if !Isptr[t.Etype] && (t.Sym != nil || methods(tptr) != nil) {
|
||||
sptr = dtypesym(tptr)
|
||||
} else {
|
||||
sptr = weaktypesym(tptr)
|
||||
sptr := dtypesym(tptr)
|
||||
r := obj.Addrel(Linksym(s))
|
||||
r.Off = 0
|
||||
r.Siz = 0
|
||||
r.Sym = sptr.Lsym
|
||||
r.Type = obj.R_USETYPE
|
||||
}
|
||||
|
||||
gcsym, useGCProg, ptrdata := dgcsym(t)
|
||||
@ -724,7 +720,6 @@ func dcommontype(s *Sym, ot int, t *Type) int {
|
||||
// gcdata *byte
|
||||
// string *string
|
||||
// *uncommonType
|
||||
// ptrToThis *rtype
|
||||
// }
|
||||
ot = duintptr(s, ot, uint64(t.Width))
|
||||
ot = duintptr(s, ot, uint64(ptrdata))
|
||||
@ -763,12 +758,14 @@ func dcommontype(s *Sym, ot int, t *Type) int {
|
||||
} else {
|
||||
ot = dsymptr(s, ot, algsym, 0)
|
||||
}
|
||||
ot = dsymptr(s, ot, gcsym, 0)
|
||||
ot = dsymptr(s, ot, gcsym, 0) // gcdata
|
||||
|
||||
p := Tconv(t, obj.FmtLeft|obj.FmtUnsigned)
|
||||
|
||||
//print("dcommontype: %s\n", p);
|
||||
ot = dgostringptr(s, ot, p) // string
|
||||
_, symdata := stringsym(p) // string
|
||||
ot = dsymptr(s, ot, symdata, 0)
|
||||
ot = duintxx(s, ot, uint64(len(p)), Widthint)
|
||||
//fmt.Printf("dcommontype: %s\n", p)
|
||||
|
||||
// skip pointer to extraType,
|
||||
// which follows the rest of this type structure.
|
||||
@ -776,7 +773,6 @@ func dcommontype(s *Sym, ot int, t *Type) int {
|
||||
// otherwise linker will assume 0.
|
||||
ot += Widthptr
|
||||
|
||||
ot = dsymptr(s, ot, sptr, 0) // ptrto type
|
||||
return ot
|
||||
}
|
||||
|
||||
@ -1006,7 +1002,7 @@ ok:
|
||||
switch t.Etype {
|
||||
default:
|
||||
ot = dcommontype(s, ot, t)
|
||||
xt = ot - 2*Widthptr
|
||||
xt = ot - 1*Widthptr
|
||||
|
||||
case TARRAY:
|
||||
if t.Bound >= 0 {
|
||||
@ -1018,7 +1014,7 @@ ok:
|
||||
t2.Bound = -1 // slice
|
||||
s2 := dtypesym(t2)
|
||||
ot = dcommontype(s, ot, t)
|
||||
xt = ot - 2*Widthptr
|
||||
xt = ot - 1*Widthptr
|
||||
ot = dsymptr(s, ot, s1, 0)
|
||||
ot = dsymptr(s, ot, s2, 0)
|
||||
ot = duintptr(s, ot, uint64(t.Bound))
|
||||
@ -1027,7 +1023,7 @@ ok:
|
||||
s1 := dtypesym(t.Type)
|
||||
|
||||
ot = dcommontype(s, ot, t)
|
||||
xt = ot - 2*Widthptr
|
||||
xt = ot - 1*Widthptr
|
||||
ot = dsymptr(s, ot, s1, 0)
|
||||
}
|
||||
|
||||
@ -1036,7 +1032,7 @@ ok:
|
||||
s1 := dtypesym(t.Type)
|
||||
|
||||
ot = dcommontype(s, ot, t)
|
||||
xt = ot - 2*Widthptr
|
||||
xt = ot - 1*Widthptr
|
||||
ot = dsymptr(s, ot, s1, 0)
|
||||
ot = duintptr(s, ot, uint64(t.Chan))
|
||||
|
||||
@ -1055,7 +1051,7 @@ ok:
|
||||
}
|
||||
|
||||
ot = dcommontype(s, ot, t)
|
||||
xt = ot - 2*Widthptr
|
||||
xt = ot - 1*Widthptr
|
||||
ot = duint8(s, ot, uint8(obj.Bool2int(isddd)))
|
||||
|
||||
// two slice headers: in and out.
|
||||
@ -1093,7 +1089,7 @@ ok:
|
||||
// ../../../../runtime/type.go:/interfaceType
|
||||
ot = dcommontype(s, ot, t)
|
||||
|
||||
xt = ot - 2*Widthptr
|
||||
xt = ot - 1*Widthptr
|
||||
ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint)
|
||||
ot = duintxx(s, ot, uint64(n), Widthint)
|
||||
ot = duintxx(s, ot, uint64(n), Widthint)
|
||||
@ -1113,7 +1109,7 @@ ok:
|
||||
s3 := dtypesym(mapbucket(t))
|
||||
s4 := dtypesym(hmap(t))
|
||||
ot = dcommontype(s, ot, t)
|
||||
xt = ot - 2*Widthptr
|
||||
xt = ot - 1*Widthptr
|
||||
ot = dsymptr(s, ot, s1, 0)
|
||||
ot = dsymptr(s, ot, s2, 0)
|
||||
ot = dsymptr(s, ot, s3, 0)
|
||||
@ -1150,7 +1146,7 @@ ok:
|
||||
s1 := dtypesym(t.Type)
|
||||
|
||||
ot = dcommontype(s, ot, t)
|
||||
xt = ot - 2*Widthptr
|
||||
xt = ot - 1*Widthptr
|
||||
ot = dsymptr(s, ot, s1, 0)
|
||||
|
||||
// ../../../../runtime/type.go:/structType
|
||||
@ -1164,7 +1160,7 @@ ok:
|
||||
}
|
||||
|
||||
ot = dcommontype(s, ot, t)
|
||||
xt = ot - 2*Widthptr
|
||||
xt = ot - 1*Widthptr
|
||||
ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint)
|
||||
ot = duintxx(s, ot, uint64(n), Widthint)
|
||||
ot = duintxx(s, ot, uint64(n), Widthint)
|
||||
@ -1203,21 +1199,7 @@ ok:
|
||||
// we want be able to find.
|
||||
if t.Sym == nil {
|
||||
switch t.Etype {
|
||||
case TPTR32, TPTR64:
|
||||
// The ptrto field of the type data cannot be relied on when
|
||||
// dynamic linking: a type T may be defined in a module that makes
|
||||
// no use of pointers to that type, but another module can contain
|
||||
// a package that imports the first one and does use *T pointers.
|
||||
// The second module will end up defining type data for *T and a
|
||||
// type.*T symbol pointing at it. It's important that calling
|
||||
// .PtrTo() on the reflect.Type for T returns this type data and
|
||||
// not some synthesized object, so we need reflect to be able to
|
||||
// find it!
|
||||
if !Ctxt.Flag_dynlink {
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case TARRAY, TCHAN, TFUNC, TMAP:
|
||||
case TPTR32, TPTR64, TARRAY, TCHAN, TFUNC, TMAP:
|
||||
slink := typelinksym(t)
|
||||
dsymptr(slink, 0, s, 0)
|
||||
ggloblsym(slink, int32(Widthptr), int16(dupok|obj.RODATA))
|
||||
@ -1377,7 +1359,7 @@ func dalgsym(t *Type) *Sym {
|
||||
// be multiples of four words. On 32-bit systems that's 16 bytes, and
|
||||
// all size classes >= 16 bytes are 16-byte aligned, so no real constraint.
|
||||
// On 64-bit systems, that's 32 bytes, and 32-byte alignment is guaranteed
|
||||
// for size classes >= 256 bytes. On a 64-bit sytem, 256 bytes allocated
|
||||
// for size classes >= 256 bytes. On a 64-bit system, 256 bytes allocated
|
||||
// is 32 pointers, the bits for which fit in 4 bytes. So maxPtrmaskBytes
|
||||
// must be >= 4.
|
||||
//
|
||||
|
@ -475,7 +475,7 @@ func staticassign(l *Node, r *Node, out **NodeList) bool {
|
||||
break
|
||||
|
||||
case OCLOSURE:
|
||||
if r.Func.Cvars == nil {
|
||||
if len(r.Func.Cvars.Slice()) == 0 {
|
||||
// Closures with no captured variables are globals,
|
||||
// so the assignment can be done at link time.
|
||||
n := *l
|
||||
|
@ -84,9 +84,9 @@ func buildssa(fn *Node) *ssa.Func {
|
||||
printssa := strings.HasSuffix(name, "_ssa") || strings.Contains(name, "_ssa.") || name == os.Getenv("GOSSAFUNC")
|
||||
if printssa {
|
||||
fmt.Println("generating SSA for", name)
|
||||
dumplist("buildssa-enter", fn.Func.Enter)
|
||||
dumpslice("buildssa-enter", fn.Func.Enter.Slice())
|
||||
dumplist("buildssa-body", fn.Nbody)
|
||||
dumplist("buildssa-exit", fn.Func.Exit)
|
||||
dumpslice("buildssa-exit", fn.Func.Exit.Slice())
|
||||
}
|
||||
|
||||
var s state
|
||||
@ -132,8 +132,7 @@ func buildssa(fn *Node) *ssa.Func {
|
||||
|
||||
// Generate addresses of local declarations
|
||||
s.decladdrs = map[*Node]*ssa.Value{}
|
||||
for d := fn.Func.Dcl; d != nil; d = d.Next {
|
||||
n := d.N
|
||||
for _, n := range fn.Func.Dcl {
|
||||
switch n.Class {
|
||||
case PPARAM:
|
||||
aux := s.lookupSymbol(n, &ssa.ArgSymbol{Typ: n.Type, Node: n})
|
||||
@ -159,12 +158,12 @@ func buildssa(fn *Node) *ssa.Func {
|
||||
}
|
||||
|
||||
// Convert the AST-based IR to the SSA-based IR
|
||||
s.stmtList(fn.Func.Enter)
|
||||
s.stmts(fn.Func.Enter)
|
||||
s.stmtList(fn.Nbody)
|
||||
|
||||
// fallthrough to exit
|
||||
if s.curBlock != nil {
|
||||
s.stmtList(s.exitCode)
|
||||
s.stmts(s.exitCode)
|
||||
m := s.mem()
|
||||
b := s.endBlock()
|
||||
b.Kind = ssa.BlockRet
|
||||
@ -201,7 +200,7 @@ func buildssa(fn *Node) *ssa.Func {
|
||||
s.linkForwardReferences()
|
||||
|
||||
// Don't carry reference this around longer than necessary
|
||||
s.exitCode = nil
|
||||
s.exitCode = Nodes{}
|
||||
|
||||
// Main call to ssa package to compile function
|
||||
ssa.Compile(s.f)
|
||||
@ -224,7 +223,7 @@ type state struct {
|
||||
fwdGotos []*Node
|
||||
// Code that must precede any return
|
||||
// (e.g., copying value of heap-escaped paramout back to true paramout)
|
||||
exitCode *NodeList
|
||||
exitCode Nodes
|
||||
|
||||
// unlabeled break and continue statement tracking
|
||||
breakTo *ssa.Block // current target for plain break statement
|
||||
@ -479,6 +478,12 @@ func (s *state) constInt(t ssa.Type, c int64) *ssa.Value {
|
||||
return s.constInt32(t, int32(c))
|
||||
}
|
||||
|
||||
func (s *state) stmts(a Nodes) {
|
||||
for _, x := range a.Slice() {
|
||||
s.stmt(x)
|
||||
}
|
||||
}
|
||||
|
||||
// ssaStmtList converts the statement n to SSA and adds it to s.
|
||||
func (s *state) stmtList(l *NodeList) {
|
||||
for ; l != nil; l = l.Next {
|
||||
@ -697,14 +702,14 @@ func (s *state) stmt(n *Node) {
|
||||
|
||||
case ORETURN:
|
||||
s.stmtList(n.List)
|
||||
s.stmtList(s.exitCode)
|
||||
s.stmts(s.exitCode)
|
||||
m := s.mem()
|
||||
b := s.endBlock()
|
||||
b.Kind = ssa.BlockRet
|
||||
b.Control = m
|
||||
case ORETJMP:
|
||||
s.stmtList(n.List)
|
||||
s.stmtList(s.exitCode)
|
||||
s.stmts(s.exitCode)
|
||||
m := s.mem()
|
||||
b := s.endBlock()
|
||||
b.Kind = ssa.BlockRetJmp
|
||||
|
@ -19,7 +19,6 @@ import (
|
||||
|
||||
type Error struct {
|
||||
lineno int
|
||||
seq int
|
||||
msg string
|
||||
}
|
||||
|
||||
@ -49,35 +48,24 @@ func adderrorname(n *Node) {
|
||||
|
||||
func adderr(line int, format string, args ...interface{}) {
|
||||
errors = append(errors, Error{
|
||||
seq: len(errors),
|
||||
lineno: line,
|
||||
msg: fmt.Sprintf("%v: %s\n", Ctxt.Line(line), fmt.Sprintf(format, args...)),
|
||||
})
|
||||
}
|
||||
|
||||
// errcmp sorts errors by line, then seq, then message.
|
||||
type errcmp []Error
|
||||
// byLineno sorts errors by lineno.
|
||||
type byLineno []Error
|
||||
|
||||
func (x errcmp) Len() int { return len(x) }
|
||||
func (x errcmp) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
func (x errcmp) Less(i, j int) bool {
|
||||
a := &x[i]
|
||||
b := &x[j]
|
||||
if a.lineno != b.lineno {
|
||||
return a.lineno < b.lineno
|
||||
}
|
||||
if a.seq != b.seq {
|
||||
return a.seq < b.seq
|
||||
}
|
||||
return a.msg < b.msg
|
||||
}
|
||||
func (x byLineno) Len() int { return len(x) }
|
||||
func (x byLineno) Less(i, j int) bool { return x[i].lineno < x[j].lineno }
|
||||
func (x byLineno) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
|
||||
func Flusherrors() {
|
||||
bstdout.Flush()
|
||||
if len(errors) == 0 {
|
||||
return
|
||||
}
|
||||
sort.Sort(errcmp(errors))
|
||||
sort.Stable(byLineno(errors))
|
||||
for i := 0; i < len(errors); i++ {
|
||||
if i == 0 || errors[i].msg != errors[i-1].msg {
|
||||
fmt.Printf("%s", errors[i].msg)
|
||||
@ -109,7 +97,7 @@ func yyerrorl(line int, format string, args ...interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
var yyerror_lastsyntax int
|
||||
var yyerror_lastsyntax int32
|
||||
|
||||
func Yyerror(format string, args ...interface{}) {
|
||||
msg := fmt.Sprintf(format, args...)
|
||||
@ -117,18 +105,12 @@ func Yyerror(format string, args ...interface{}) {
|
||||
nsyntaxerrors++
|
||||
|
||||
// only one syntax error per line
|
||||
if int32(yyerror_lastsyntax) == lexlineno {
|
||||
if yyerror_lastsyntax == lineno {
|
||||
return
|
||||
}
|
||||
yyerror_lastsyntax = int(lexlineno)
|
||||
yyerror_lastsyntax = lineno
|
||||
|
||||
// plain "syntax error" gets "near foo" added
|
||||
if msg == "syntax error" {
|
||||
yyerrorl(int(lexlineno), "syntax error near %s", lexbuf.String())
|
||||
return
|
||||
}
|
||||
|
||||
yyerrorl(int(lexlineno), "%s", msg)
|
||||
yyerrorl(int(lineno), "%s", msg)
|
||||
return
|
||||
}
|
||||
|
||||
@ -258,7 +240,6 @@ func (pkg *Pkg) Lookup(name string) *Sym {
|
||||
s := &Sym{
|
||||
Name: name,
|
||||
Pkg: pkg,
|
||||
Lexical: LNAME,
|
||||
}
|
||||
if name == "init" {
|
||||
initSyms = append(initSyms, s)
|
||||
@ -367,162 +348,6 @@ func saveorignode(n *Node) {
|
||||
n.Orig = norig
|
||||
}
|
||||
|
||||
// ispaddedfield reports whether the given field
|
||||
// is followed by padding. For the case where t is
|
||||
// the last field, total gives the size of the enclosing struct.
|
||||
func ispaddedfield(t *Type, total int64) bool {
|
||||
if t.Etype != TFIELD {
|
||||
Fatalf("ispaddedfield called non-field %v", t)
|
||||
}
|
||||
if t.Down == nil {
|
||||
return t.Width+t.Type.Width != total
|
||||
}
|
||||
return t.Width+t.Type.Width != t.Down.Width
|
||||
}
|
||||
|
||||
func algtype1(t *Type, bad **Type) int {
|
||||
if bad != nil {
|
||||
*bad = nil
|
||||
}
|
||||
if t.Broke {
|
||||
return AMEM
|
||||
}
|
||||
if t.Noalg {
|
||||
return ANOEQ
|
||||
}
|
||||
|
||||
switch t.Etype {
|
||||
// will be defined later.
|
||||
case TANY, TFORW:
|
||||
*bad = t
|
||||
|
||||
return -1
|
||||
|
||||
case TINT8,
|
||||
TUINT8,
|
||||
TINT16,
|
||||
TUINT16,
|
||||
TINT32,
|
||||
TUINT32,
|
||||
TINT64,
|
||||
TUINT64,
|
||||
TINT,
|
||||
TUINT,
|
||||
TUINTPTR,
|
||||
TBOOL,
|
||||
TPTR32,
|
||||
TPTR64,
|
||||
TCHAN,
|
||||
TUNSAFEPTR:
|
||||
return AMEM
|
||||
|
||||
case TFUNC, TMAP:
|
||||
if bad != nil {
|
||||
*bad = t
|
||||
}
|
||||
return ANOEQ
|
||||
|
||||
case TFLOAT32:
|
||||
return AFLOAT32
|
||||
|
||||
case TFLOAT64:
|
||||
return AFLOAT64
|
||||
|
||||
case TCOMPLEX64:
|
||||
return ACPLX64
|
||||
|
||||
case TCOMPLEX128:
|
||||
return ACPLX128
|
||||
|
||||
case TSTRING:
|
||||
return ASTRING
|
||||
|
||||
case TINTER:
|
||||
if isnilinter(t) {
|
||||
return ANILINTER
|
||||
}
|
||||
return AINTER
|
||||
|
||||
case TARRAY:
|
||||
if Isslice(t) {
|
||||
if bad != nil {
|
||||
*bad = t
|
||||
}
|
||||
return ANOEQ
|
||||
}
|
||||
|
||||
a := algtype1(t.Type, bad)
|
||||
if a == ANOEQ || a == AMEM {
|
||||
if a == ANOEQ && bad != nil {
|
||||
*bad = t
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
switch t.Bound {
|
||||
case 0:
|
||||
// We checked above that the element type is comparable.
|
||||
return AMEM
|
||||
case 1:
|
||||
// Single-element array is same as its lone element.
|
||||
return a
|
||||
}
|
||||
|
||||
return -1 // needs special compare
|
||||
|
||||
case TSTRUCT:
|
||||
if t.Type != nil && t.Type.Down == nil && !isblanksym(t.Type.Sym) {
|
||||
// One-field struct is same as that one field alone.
|
||||
return algtype1(t.Type.Type, bad)
|
||||
}
|
||||
|
||||
ret := AMEM
|
||||
var a int
|
||||
for t1 := t.Type; t1 != nil; t1 = t1.Down {
|
||||
// All fields must be comparable.
|
||||
a = algtype1(t1.Type, bad)
|
||||
|
||||
if a == ANOEQ {
|
||||
return ANOEQ
|
||||
}
|
||||
|
||||
// Blank fields, padded fields, fields with non-memory
|
||||
// equality need special compare.
|
||||
if a != AMEM || isblanksym(t1.Sym) || ispaddedfield(t1, t.Width) {
|
||||
ret = -1
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
Fatalf("algtype1: unexpected type %v", t)
|
||||
return 0
|
||||
}
|
||||
|
||||
func algtype(t *Type) int {
|
||||
a := algtype1(t, nil)
|
||||
if a == AMEM {
|
||||
switch t.Width {
|
||||
case 0:
|
||||
return AMEM0
|
||||
case 1:
|
||||
return AMEM8
|
||||
case 2:
|
||||
return AMEM16
|
||||
case 4:
|
||||
return AMEM32
|
||||
case 8:
|
||||
return AMEM64
|
||||
case 16:
|
||||
return AMEM128
|
||||
}
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func maptype(key *Type, val *Type) *Type {
|
||||
if key != nil {
|
||||
var bad *Type
|
||||
@ -1561,8 +1386,8 @@ func frame(context int) {
|
||||
|
||||
if Curfn != nil {
|
||||
fmt.Printf("--- %v frame ---\n", Curfn.Func.Nname.Sym)
|
||||
for l := Curfn.Func.Dcl; l != nil; l = l.Next {
|
||||
printframenode(l.N)
|
||||
for _, ln := range Curfn.Func.Dcl {
|
||||
printframenode(ln)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2414,457 +2239,6 @@ func hashmem(t *Type) *Node {
|
||||
return n
|
||||
}
|
||||
|
||||
func hashfor(t *Type) *Node {
|
||||
var sym *Sym
|
||||
|
||||
a := algtype1(t, nil)
|
||||
switch a {
|
||||
case AMEM:
|
||||
Fatalf("hashfor with AMEM type")
|
||||
|
||||
case AINTER:
|
||||
sym = Pkglookup("interhash", Runtimepkg)
|
||||
|
||||
case ANILINTER:
|
||||
sym = Pkglookup("nilinterhash", Runtimepkg)
|
||||
|
||||
case ASTRING:
|
||||
sym = Pkglookup("strhash", Runtimepkg)
|
||||
|
||||
case AFLOAT32:
|
||||
sym = Pkglookup("f32hash", Runtimepkg)
|
||||
|
||||
case AFLOAT64:
|
||||
sym = Pkglookup("f64hash", Runtimepkg)
|
||||
|
||||
case ACPLX64:
|
||||
sym = Pkglookup("c64hash", Runtimepkg)
|
||||
|
||||
case ACPLX128:
|
||||
sym = Pkglookup("c128hash", Runtimepkg)
|
||||
|
||||
default:
|
||||
sym = typesymprefix(".hash", t)
|
||||
}
|
||||
|
||||
n := newname(sym)
|
||||
n.Class = PFUNC
|
||||
tfn := Nod(OTFUNC, nil, nil)
|
||||
tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
|
||||
tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
|
||||
tfn.Rlist = list(tfn.Rlist, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
|
||||
typecheck(&tfn, Etype)
|
||||
n.Type = tfn.Type
|
||||
return n
|
||||
}
|
||||
|
||||
// Generate a helper function to compute the hash of a value of type t.
|
||||
func genhash(sym *Sym, t *Type) {
|
||||
if Debug['r'] != 0 {
|
||||
fmt.Printf("genhash %v %v\n", sym, t)
|
||||
}
|
||||
|
||||
lineno = 1 // less confusing than end of input
|
||||
dclcontext = PEXTERN
|
||||
markdcl()
|
||||
|
||||
// func sym(p *T, h uintptr) uintptr
|
||||
fn := Nod(ODCLFUNC, nil, nil)
|
||||
|
||||
fn.Func.Nname = newname(sym)
|
||||
fn.Func.Nname.Class = PFUNC
|
||||
tfn := Nod(OTFUNC, nil, nil)
|
||||
fn.Func.Nname.Name.Param.Ntype = tfn
|
||||
|
||||
n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
|
||||
tfn.List = list(tfn.List, n)
|
||||
np := n.Left
|
||||
n = Nod(ODCLFIELD, newname(Lookup("h")), typenod(Types[TUINTPTR]))
|
||||
tfn.List = list(tfn.List, n)
|
||||
nh := n.Left
|
||||
n = Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])) // return value
|
||||
tfn.Rlist = list(tfn.Rlist, n)
|
||||
|
||||
funchdr(fn)
|
||||
typecheck(&fn.Func.Nname.Name.Param.Ntype, Etype)
|
||||
|
||||
// genhash is only called for types that have equality but
|
||||
// cannot be handled by the standard algorithms,
|
||||
// so t must be either an array or a struct.
|
||||
switch t.Etype {
|
||||
default:
|
||||
Fatalf("genhash %v", t)
|
||||
|
||||
case TARRAY:
|
||||
if Isslice(t) {
|
||||
Fatalf("genhash %v", t)
|
||||
}
|
||||
|
||||
// An array of pure memory would be handled by the
|
||||
// standard algorithm, so the element type must not be
|
||||
// pure memory.
|
||||
hashel := hashfor(t.Type)
|
||||
|
||||
n := Nod(ORANGE, nil, Nod(OIND, np, nil))
|
||||
ni := newname(Lookup("i"))
|
||||
ni.Type = Types[TINT]
|
||||
n.List = list1(ni)
|
||||
n.Colas = true
|
||||
colasdefn(n.List, n)
|
||||
ni = n.List.N
|
||||
|
||||
// h = hashel(&p[i], h)
|
||||
call := Nod(OCALL, hashel, nil)
|
||||
|
||||
nx := Nod(OINDEX, np, ni)
|
||||
nx.Bounded = true
|
||||
na := Nod(OADDR, nx, nil)
|
||||
na.Etype = 1 // no escape to heap
|
||||
call.List = list(call.List, na)
|
||||
call.List = list(call.List, nh)
|
||||
n.Nbody = list(n.Nbody, Nod(OAS, nh, call))
|
||||
|
||||
fn.Nbody = list(fn.Nbody, n)
|
||||
|
||||
// Walk the struct using memhash for runs of AMEM
|
||||
// and calling specific hash functions for the others.
|
||||
case TSTRUCT:
|
||||
var first *Type
|
||||
|
||||
offend := int64(0)
|
||||
var size int64
|
||||
var call *Node
|
||||
var nx *Node
|
||||
var na *Node
|
||||
var hashel *Node
|
||||
for t1 := t.Type; ; t1 = t1.Down {
|
||||
if t1 != nil && algtype1(t1.Type, nil) == AMEM && !isblanksym(t1.Sym) {
|
||||
offend = t1.Width + t1.Type.Width
|
||||
if first == nil {
|
||||
first = t1
|
||||
}
|
||||
|
||||
// If it's a memory field but it's padded, stop here.
|
||||
if ispaddedfield(t1, t.Width) {
|
||||
t1 = t1.Down
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Run memhash for fields up to this one.
|
||||
if first != nil {
|
||||
size = offend - first.Width // first->width is offset
|
||||
hashel = hashmem(first.Type)
|
||||
|
||||
// h = hashel(&p.first, size, h)
|
||||
call = Nod(OCALL, hashel, nil)
|
||||
|
||||
nx = Nod(OXDOT, np, newname(first.Sym)) // TODO: fields from other packages?
|
||||
na = Nod(OADDR, nx, nil)
|
||||
na.Etype = 1 // no escape to heap
|
||||
call.List = list(call.List, na)
|
||||
call.List = list(call.List, nh)
|
||||
call.List = list(call.List, Nodintconst(size))
|
||||
fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call))
|
||||
|
||||
first = nil
|
||||
}
|
||||
|
||||
if t1 == nil {
|
||||
break
|
||||
}
|
||||
if isblanksym(t1.Sym) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Run hash for this field.
|
||||
if algtype1(t1.Type, nil) == AMEM {
|
||||
hashel = hashmem(t1.Type)
|
||||
|
||||
// h = memhash(&p.t1, h, size)
|
||||
call = Nod(OCALL, hashel, nil)
|
||||
|
||||
nx = Nod(OXDOT, np, newname(t1.Sym)) // TODO: fields from other packages?
|
||||
na = Nod(OADDR, nx, nil)
|
||||
na.Etype = 1 // no escape to heap
|
||||
call.List = list(call.List, na)
|
||||
call.List = list(call.List, nh)
|
||||
call.List = list(call.List, Nodintconst(t1.Type.Width))
|
||||
fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call))
|
||||
} else {
|
||||
hashel = hashfor(t1.Type)
|
||||
|
||||
// h = hashel(&p.t1, h)
|
||||
call = Nod(OCALL, hashel, nil)
|
||||
|
||||
nx = Nod(OXDOT, np, newname(t1.Sym)) // TODO: fields from other packages?
|
||||
na = Nod(OADDR, nx, nil)
|
||||
na.Etype = 1 // no escape to heap
|
||||
call.List = list(call.List, na)
|
||||
call.List = list(call.List, nh)
|
||||
fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r := Nod(ORETURN, nil, nil)
|
||||
r.List = list(r.List, nh)
|
||||
fn.Nbody = list(fn.Nbody, r)
|
||||
|
||||
if Debug['r'] != 0 {
|
||||
dumplist("genhash body", fn.Nbody)
|
||||
}
|
||||
|
||||
funcbody(fn)
|
||||
Curfn = fn
|
||||
fn.Func.Dupok = true
|
||||
typecheck(&fn, Etop)
|
||||
typechecklist(fn.Nbody, Etop)
|
||||
Curfn = nil
|
||||
|
||||
// Disable safemode while compiling this code: the code we
|
||||
// generate internally can refer to unsafe.Pointer.
|
||||
// In this case it can happen if we need to generate an ==
|
||||
// for a struct containing a reflect.Value, which itself has
|
||||
// an unexported field of type unsafe.Pointer.
|
||||
old_safemode := safemode
|
||||
|
||||
safemode = 0
|
||||
funccompile(fn)
|
||||
safemode = old_safemode
|
||||
}
|
||||
|
||||
// eqfield returns the node
|
||||
// p.field == q.field
|
||||
func eqfield(p *Node, q *Node, field *Node) *Node {
|
||||
nx := Nod(OXDOT, p, field)
|
||||
ny := Nod(OXDOT, q, field)
|
||||
ne := Nod(OEQ, nx, ny)
|
||||
return ne
|
||||
}
|
||||
|
||||
func eqmemfunc(size int64, type_ *Type, needsize *int) *Node {
|
||||
var fn *Node
|
||||
|
||||
switch size {
|
||||
default:
|
||||
fn = syslook("memequal", 1)
|
||||
*needsize = 1
|
||||
|
||||
case 1, 2, 4, 8, 16:
|
||||
buf := fmt.Sprintf("memequal%d", int(size)*8)
|
||||
fn = syslook(buf, 1)
|
||||
*needsize = 0
|
||||
}
|
||||
|
||||
substArgTypes(fn, type_, type_)
|
||||
return fn
|
||||
}
|
||||
|
||||
// eqmem returns the node
|
||||
// memequal(&p.field, &q.field [, size])
|
||||
func eqmem(p *Node, q *Node, field *Node, size int64) *Node {
|
||||
var needsize int
|
||||
|
||||
nx := Nod(OADDR, Nod(OXDOT, p, field), nil)
|
||||
nx.Etype = 1 // does not escape
|
||||
ny := Nod(OADDR, Nod(OXDOT, q, field), nil)
|
||||
ny.Etype = 1 // does not escape
|
||||
typecheck(&nx, Erv)
|
||||
typecheck(&ny, Erv)
|
||||
|
||||
call := Nod(OCALL, eqmemfunc(size, nx.Type.Type, &needsize), nil)
|
||||
call.List = list(call.List, nx)
|
||||
call.List = list(call.List, ny)
|
||||
if needsize != 0 {
|
||||
call.List = list(call.List, Nodintconst(size))
|
||||
}
|
||||
|
||||
return call
|
||||
}
|
||||
|
||||
// geneq generates a helper function to
|
||||
// check equality of two values of type t.
|
||||
func geneq(sym *Sym, t *Type) {
|
||||
if Debug['r'] != 0 {
|
||||
fmt.Printf("geneq %v %v\n", sym, t)
|
||||
}
|
||||
|
||||
lineno = 1 // less confusing than end of input
|
||||
dclcontext = PEXTERN
|
||||
markdcl()
|
||||
|
||||
// func sym(p, q *T) bool
|
||||
fn := Nod(ODCLFUNC, nil, nil)
|
||||
|
||||
fn.Func.Nname = newname(sym)
|
||||
fn.Func.Nname.Class = PFUNC
|
||||
tfn := Nod(OTFUNC, nil, nil)
|
||||
fn.Func.Nname.Name.Param.Ntype = tfn
|
||||
|
||||
n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
|
||||
tfn.List = list(tfn.List, n)
|
||||
np := n.Left
|
||||
n = Nod(ODCLFIELD, newname(Lookup("q")), typenod(Ptrto(t)))
|
||||
tfn.List = list(tfn.List, n)
|
||||
nq := n.Left
|
||||
n = Nod(ODCLFIELD, nil, typenod(Types[TBOOL]))
|
||||
tfn.Rlist = list(tfn.Rlist, n)
|
||||
|
||||
funchdr(fn)
|
||||
|
||||
// geneq is only called for types that have equality but
|
||||
// cannot be handled by the standard algorithms,
|
||||
// so t must be either an array or a struct.
|
||||
switch t.Etype {
|
||||
default:
|
||||
Fatalf("geneq %v", t)
|
||||
|
||||
case TARRAY:
|
||||
if Isslice(t) {
|
||||
Fatalf("geneq %v", t)
|
||||
}
|
||||
|
||||
// An array of pure memory would be handled by the
|
||||
// standard memequal, so the element type must not be
|
||||
// pure memory. Even if we unrolled the range loop,
|
||||
// each iteration would be a function call, so don't bother
|
||||
// unrolling.
|
||||
nrange := Nod(ORANGE, nil, Nod(OIND, np, nil))
|
||||
|
||||
ni := newname(Lookup("i"))
|
||||
ni.Type = Types[TINT]
|
||||
nrange.List = list1(ni)
|
||||
nrange.Colas = true
|
||||
colasdefn(nrange.List, nrange)
|
||||
ni = nrange.List.N
|
||||
|
||||
// if p[i] != q[i] { return false }
|
||||
nx := Nod(OINDEX, np, ni)
|
||||
|
||||
nx.Bounded = true
|
||||
ny := Nod(OINDEX, nq, ni)
|
||||
ny.Bounded = true
|
||||
|
||||
nif := Nod(OIF, nil, nil)
|
||||
nif.Left = Nod(ONE, nx, ny)
|
||||
r := Nod(ORETURN, nil, nil)
|
||||
r.List = list(r.List, Nodbool(false))
|
||||
nif.Nbody = list(nif.Nbody, r)
|
||||
nrange.Nbody = list(nrange.Nbody, nif)
|
||||
fn.Nbody = list(fn.Nbody, nrange)
|
||||
|
||||
// return true
|
||||
ret := Nod(ORETURN, nil, nil)
|
||||
ret.List = list(ret.List, Nodbool(true))
|
||||
fn.Nbody = list(fn.Nbody, ret)
|
||||
|
||||
// Walk the struct using memequal for runs of AMEM
|
||||
// and calling specific equality tests for the others.
|
||||
// Skip blank-named fields.
|
||||
case TSTRUCT:
|
||||
var first *Type
|
||||
|
||||
var conjuncts []*Node
|
||||
offend := int64(0)
|
||||
var size int64
|
||||
for t1 := t.Type; ; t1 = t1.Down {
|
||||
if t1 != nil && algtype1(t1.Type, nil) == AMEM && !isblanksym(t1.Sym) {
|
||||
offend = t1.Width + t1.Type.Width
|
||||
if first == nil {
|
||||
first = t1
|
||||
}
|
||||
|
||||
// If it's a memory field but it's padded, stop here.
|
||||
if ispaddedfield(t1, t.Width) {
|
||||
t1 = t1.Down
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Run memequal for fields up to this one.
|
||||
// TODO(rsc): All the calls to newname are wrong for
|
||||
// cross-package unexported fields.
|
||||
if first != nil {
|
||||
if first.Down == t1 {
|
||||
conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym)))
|
||||
} else if first.Down.Down == t1 {
|
||||
conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym)))
|
||||
first = first.Down
|
||||
if !isblanksym(first.Sym) {
|
||||
conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym)))
|
||||
}
|
||||
} else {
|
||||
// More than two fields: use memequal.
|
||||
size = offend - first.Width // first->width is offset
|
||||
conjuncts = append(conjuncts, eqmem(np, nq, newname(first.Sym), size))
|
||||
}
|
||||
|
||||
first = nil
|
||||
}
|
||||
|
||||
if t1 == nil {
|
||||
break
|
||||
}
|
||||
if isblanksym(t1.Sym) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check this field, which is not just memory.
|
||||
conjuncts = append(conjuncts, eqfield(np, nq, newname(t1.Sym)))
|
||||
}
|
||||
|
||||
var and *Node
|
||||
switch len(conjuncts) {
|
||||
case 0:
|
||||
and = Nodbool(true)
|
||||
case 1:
|
||||
and = conjuncts[0]
|
||||
default:
|
||||
and = Nod(OANDAND, conjuncts[0], conjuncts[1])
|
||||
for _, conjunct := range conjuncts[2:] {
|
||||
and = Nod(OANDAND, and, conjunct)
|
||||
}
|
||||
}
|
||||
|
||||
ret := Nod(ORETURN, nil, nil)
|
||||
ret.List = list(ret.List, and)
|
||||
fn.Nbody = list(fn.Nbody, ret)
|
||||
}
|
||||
|
||||
if Debug['r'] != 0 {
|
||||
dumplist("geneq body", fn.Nbody)
|
||||
}
|
||||
|
||||
funcbody(fn)
|
||||
Curfn = fn
|
||||
fn.Func.Dupok = true
|
||||
typecheck(&fn, Etop)
|
||||
typechecklist(fn.Nbody, Etop)
|
||||
Curfn = nil
|
||||
|
||||
// Disable safemode while compiling this code: the code we
|
||||
// generate internally can refer to unsafe.Pointer.
|
||||
// In this case it can happen if we need to generate an ==
|
||||
// for a struct containing a reflect.Value, which itself has
|
||||
// an unexported field of type unsafe.Pointer.
|
||||
old_safemode := safemode
|
||||
safemode = 0
|
||||
|
||||
// Disable checknils while compiling this code.
|
||||
// We are comparing a struct or an array,
|
||||
// neither of which can be nil, and our comparisons
|
||||
// are shallow.
|
||||
Disable_checknil++
|
||||
|
||||
funccompile(fn)
|
||||
|
||||
safemode = old_safemode
|
||||
Disable_checknil--
|
||||
}
|
||||
|
||||
func ifacelookdot(s *Sym, t *Type, followptr *bool, ignorecase int) *Type {
|
||||
*followptr = false
|
||||
|
||||
|
@ -149,11 +149,11 @@ type Param struct {
|
||||
// Func holds Node fields used only with function-like nodes.
|
||||
type Func struct {
|
||||
Shortname *Node
|
||||
Enter *NodeList // for example, allocate and initialize memory for escaping parameters
|
||||
Exit *NodeList
|
||||
Cvars *NodeList // closure params
|
||||
Dcl *NodeList // autodcl for this func/closure
|
||||
Inldcl *NodeList // copy of dcl for use in inlining
|
||||
Enter Nodes // for example, allocate and initialize memory for escaping parameters
|
||||
Exit Nodes
|
||||
Cvars Nodes // closure params
|
||||
Dcl []*Node // autodcl for this func/closure
|
||||
Inldcl *[]*Node // copy of dcl for use in inlining
|
||||
Closgen int
|
||||
Outerfunc *Node
|
||||
Fieldtrack []*Type
|
||||
@ -169,18 +169,12 @@ type Func struct {
|
||||
Depth int32
|
||||
|
||||
Endlineno int32
|
||||
WBLineno int32 // line number of first write barrier
|
||||
|
||||
Norace bool // func must not have race detector annotations
|
||||
Nosplit bool // func should not execute on separate stack
|
||||
Noinline bool // func should not be inlined
|
||||
Nowritebarrier bool // emit compiler error instead of write barrier
|
||||
Nowritebarrierrec bool // error on write barrier in this or recursive callees
|
||||
Pragma Pragma // go:xxx function annotations
|
||||
Dupok bool // duplicate definitions ok
|
||||
Wrapper bool // is method wrapper
|
||||
Needctxt bool // function uses context register (has closure variables)
|
||||
Systemstack bool // must run on system stack
|
||||
|
||||
WBLineno int32 // line number of first write barrier
|
||||
}
|
||||
|
||||
type Op uint8
|
||||
@ -491,3 +485,55 @@ func count(l *NodeList) int {
|
||||
}
|
||||
return int(n)
|
||||
}
|
||||
|
||||
// Nodes is a pointer to a slice of *Node.
|
||||
// For fields that are not used in most nodes, this is used instead of
|
||||
// a slice to save space.
|
||||
type Nodes struct{ slice *[]*Node }
|
||||
|
||||
// Slice returns the entries in Nodes as a slice.
|
||||
// Changes to the slice entries (as in s[i] = n) will be reflected in
|
||||
// the Nodes.
|
||||
func (n *Nodes) Slice() []*Node {
|
||||
if n.slice == nil {
|
||||
return nil
|
||||
}
|
||||
return *n.slice
|
||||
}
|
||||
|
||||
// NodeList returns the entries in Nodes as a NodeList.
|
||||
// Changes to the NodeList entries (as in l.N = n) will *not* be
|
||||
// reflect in the Nodes.
|
||||
// This wastes memory and should be used as little as possible.
|
||||
func (n *Nodes) NodeList() *NodeList {
|
||||
if n.slice == nil {
|
||||
return nil
|
||||
}
|
||||
var ret *NodeList
|
||||
for _, n := range *n.slice {
|
||||
ret = list(ret, n)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Set sets Nodes to a slice.
|
||||
// This takes ownership of the slice.
|
||||
func (n *Nodes) Set(s []*Node) {
|
||||
if len(s) == 0 {
|
||||
n.slice = nil
|
||||
} else {
|
||||
n.slice = &s
|
||||
}
|
||||
}
|
||||
|
||||
// Append appends entries to Nodes.
|
||||
// If a slice is passed in, this will take ownership of it.
|
||||
func (n *Nodes) Append(a ...*Node) {
|
||||
if n.slice == nil {
|
||||
if len(a) > 0 {
|
||||
n.slice = &a
|
||||
}
|
||||
} else {
|
||||
*n.slice = append(*n.slice, a...)
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,12 @@ func typechecklist(l *NodeList, top int) {
|
||||
}
|
||||
}
|
||||
|
||||
func typecheckslice(l []*Node, top int) {
|
||||
for i := range l {
|
||||
typecheck(&l[i], top)
|
||||
}
|
||||
}
|
||||
|
||||
var _typekind = []string{
|
||||
TINT: "int",
|
||||
TUINT: "uint",
|
||||
@ -3433,9 +3439,9 @@ func typecheckfunc(n *Node) {
|
||||
addmethod(n.Func.Shortname.Sym, t, true, n.Func.Nname.Nointerface)
|
||||
}
|
||||
|
||||
for l := n.Func.Dcl; l != nil; l = l.Next {
|
||||
if l.N.Op == ONAME && (l.N.Class == PPARAM || l.N.Class == PPARAMOUT) {
|
||||
l.N.Name.Decldepth = 1
|
||||
for _, ln := range n.Func.Dcl {
|
||||
if ln.Op == ONAME && (ln.Class == PPARAM || ln.Class == PPARAMOUT) {
|
||||
ln.Name.Decldepth = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,33 +29,34 @@ func walk(fn *Node) {
|
||||
|
||||
// Final typecheck for any unused variables.
|
||||
// It's hard to be on the heap when not-used, but best to be consistent about &~PHEAP here and below.
|
||||
for l := fn.Func.Dcl; l != nil; l = l.Next {
|
||||
if l.N.Op == ONAME && l.N.Class&^PHEAP == PAUTO {
|
||||
typecheck(&l.N, Erv|Easgn)
|
||||
for i, ln := range fn.Func.Dcl {
|
||||
if ln.Op == ONAME && ln.Class&^PHEAP == PAUTO {
|
||||
typecheck(&ln, Erv|Easgn)
|
||||
fn.Func.Dcl[i] = ln
|
||||
}
|
||||
}
|
||||
|
||||
// Propagate the used flag for typeswitch variables up to the NONAME in it's definition.
|
||||
for l := fn.Func.Dcl; l != nil; l = l.Next {
|
||||
if l.N.Op == ONAME && l.N.Class&^PHEAP == PAUTO && l.N.Name.Defn != nil && l.N.Name.Defn.Op == OTYPESW && l.N.Used {
|
||||
l.N.Name.Defn.Left.Used = true
|
||||
for _, ln := range fn.Func.Dcl {
|
||||
if ln.Op == ONAME && ln.Class&^PHEAP == PAUTO && ln.Name.Defn != nil && ln.Name.Defn.Op == OTYPESW && ln.Used {
|
||||
ln.Name.Defn.Left.Used = true
|
||||
}
|
||||
}
|
||||
|
||||
for l := fn.Func.Dcl; l != nil; l = l.Next {
|
||||
if l.N.Op != ONAME || l.N.Class&^PHEAP != PAUTO || l.N.Sym.Name[0] == '&' || l.N.Used {
|
||||
for _, ln := range fn.Func.Dcl {
|
||||
if ln.Op != ONAME || ln.Class&^PHEAP != PAUTO || ln.Sym.Name[0] == '&' || ln.Used {
|
||||
continue
|
||||
}
|
||||
if defn := l.N.Name.Defn; defn != nil && defn.Op == OTYPESW {
|
||||
if defn := ln.Name.Defn; defn != nil && defn.Op == OTYPESW {
|
||||
if defn.Left.Used {
|
||||
continue
|
||||
}
|
||||
lineno = defn.Left.Lineno
|
||||
Yyerror("%v declared and not used", l.N.Sym)
|
||||
Yyerror("%v declared and not used", ln.Sym)
|
||||
defn.Left.Used = true // suppress repeats
|
||||
} else {
|
||||
lineno = l.N.Lineno
|
||||
Yyerror("%v declared and not used", l.N.Sym)
|
||||
lineno = ln.Lineno
|
||||
Yyerror("%v declared and not used", ln.Sym)
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,9 +71,9 @@ func walk(fn *Node) {
|
||||
}
|
||||
|
||||
heapmoves()
|
||||
if Debug['W'] != 0 && Curfn.Func.Enter != nil {
|
||||
if Debug['W'] != 0 && len(Curfn.Func.Enter.Slice()) > 0 {
|
||||
s := fmt.Sprintf("enter %v", Curfn.Func.Nname.Sym)
|
||||
dumplist(s, Curfn.Func.Enter)
|
||||
dumpslice(s, Curfn.Func.Enter.Slice())
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,6 +83,12 @@ func walkstmtlist(l *NodeList) {
|
||||
}
|
||||
}
|
||||
|
||||
func walkstmtslice(l []*Node) {
|
||||
for i := range l {
|
||||
walkstmt(&l[i])
|
||||
}
|
||||
}
|
||||
|
||||
func samelist(a *NodeList, b *NodeList) bool {
|
||||
for ; a != nil && b != nil; a, b = a.Next, b.Next {
|
||||
if a.N != b.N {
|
||||
@ -92,11 +99,11 @@ func samelist(a *NodeList, b *NodeList) bool {
|
||||
}
|
||||
|
||||
func paramoutheap(fn *Node) bool {
|
||||
for l := fn.Func.Dcl; l != nil; l = l.Next {
|
||||
switch l.N.Class {
|
||||
for _, ln := range fn.Func.Dcl {
|
||||
switch ln.Class {
|
||||
case PPARAMOUT,
|
||||
PPARAMOUT | PHEAP:
|
||||
return l.N.Addrtaken
|
||||
return ln.Addrtaken
|
||||
|
||||
// stop early - parameters are over
|
||||
case PAUTO,
|
||||
@ -290,13 +297,13 @@ func walkstmt(np **Node) {
|
||||
var rl *NodeList
|
||||
|
||||
var cl Class
|
||||
for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
|
||||
cl = ll.N.Class &^ PHEAP
|
||||
for _, ln := range Curfn.Func.Dcl {
|
||||
cl = ln.Class &^ PHEAP
|
||||
if cl == PAUTO {
|
||||
break
|
||||
}
|
||||
if cl == PPARAMOUT {
|
||||
rl = list(rl, ll.N)
|
||||
rl = list(rl, ln)
|
||||
}
|
||||
}
|
||||
|
||||
@ -319,7 +326,7 @@ func walkstmt(np **Node) {
|
||||
ll := ascompatee(n.Op, rl, n.List, &n.Ninit)
|
||||
n.List = reorder3(ll)
|
||||
for lr := n.List; lr != nil; lr = lr.Next {
|
||||
lr.N = applywritebarrier(lr.N, &n.Ninit)
|
||||
lr.N = applywritebarrier(lr.N)
|
||||
}
|
||||
break
|
||||
}
|
||||
@ -587,9 +594,9 @@ opswitch:
|
||||
// transformclosure already did all preparation work.
|
||||
|
||||
// Prepend captured variables to argument list.
|
||||
n.List = concat(n.Left.Func.Enter, n.List)
|
||||
n.List = concat(n.Left.Func.Enter.NodeList(), n.List)
|
||||
|
||||
n.Left.Func.Enter = nil
|
||||
n.Left.Func.Enter.Set(nil)
|
||||
|
||||
// Replace OCLOSURE with ONAME/PFUNC.
|
||||
n.Left = n.Left.Func.Closure.Func.Nname
|
||||
@ -723,7 +730,7 @@ opswitch:
|
||||
r := convas(Nod(OAS, n.Left, n.Right), init)
|
||||
r.Dodata = n.Dodata
|
||||
n = r
|
||||
n = applywritebarrier(n, init)
|
||||
n = applywritebarrier(n)
|
||||
}
|
||||
|
||||
case OAS2:
|
||||
@ -734,7 +741,7 @@ opswitch:
|
||||
ll := ascompatee(OAS, n.List, n.Rlist, init)
|
||||
ll = reorder3(ll)
|
||||
for lr := ll; lr != nil; lr = lr.Next {
|
||||
lr.N = applywritebarrier(lr.N, init)
|
||||
lr.N = applywritebarrier(lr.N)
|
||||
}
|
||||
n = liststmt(ll)
|
||||
|
||||
@ -749,7 +756,7 @@ opswitch:
|
||||
|
||||
ll := ascompatet(n.Op, n.List, &r.Type, 0, init)
|
||||
for lr := ll; lr != nil; lr = lr.Next {
|
||||
lr.N = applywritebarrier(lr.N, init)
|
||||
lr.N = applywritebarrier(lr.N)
|
||||
}
|
||||
n = liststmt(concat(list1(r), ll))
|
||||
|
||||
@ -2132,7 +2139,7 @@ func needwritebarrier(l *Node, r *Node) bool {
|
||||
|
||||
// TODO(rsc): Perhaps componentgen should run before this.
|
||||
|
||||
func applywritebarrier(n *Node, init **NodeList) *Node {
|
||||
func applywritebarrier(n *Node) *Node {
|
||||
if n.Left != nil && n.Right != nil && needwritebarrier(n.Left, n.Right) {
|
||||
if Debug_wb > 1 {
|
||||
Warnl(int(n.Lineno), "marking %v for barrier", Nconv(n.Left, 0))
|
||||
@ -2541,12 +2548,12 @@ func vmatch1(l *Node, r *Node) bool {
|
||||
// walk through argin parameters.
|
||||
// generate and return code to allocate
|
||||
// copies of escaped parameters to the heap.
|
||||
func paramstoheap(argin **Type, out int) *NodeList {
|
||||
func paramstoheap(argin **Type, out int) []*Node {
|
||||
var savet Iter
|
||||
var v *Node
|
||||
var as *Node
|
||||
|
||||
var nn *NodeList
|
||||
var nn []*Node
|
||||
for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) {
|
||||
v = t.Nname
|
||||
if v != nil && v.Sym != nil && v.Sym.Name[0] == '~' && v.Sym.Name[1] == 'r' { // unnamed result
|
||||
@ -2559,7 +2566,7 @@ func paramstoheap(argin **Type, out int) *NodeList {
|
||||
// Defer might stop a panic and show the
|
||||
// return values as they exist at the time of panic.
|
||||
// Make sure to zero them on entry to the function.
|
||||
nn = list(nn, Nod(OAS, nodarg(t, -1), nil))
|
||||
nn = append(nn, Nod(OAS, nodarg(t, -1), nil))
|
||||
}
|
||||
|
||||
if v == nil || v.Class&PHEAP == 0 {
|
||||
@ -2573,13 +2580,13 @@ func paramstoheap(argin **Type, out int) *NodeList {
|
||||
if prealloc[v] == nil {
|
||||
prealloc[v] = callnew(v.Type)
|
||||
}
|
||||
nn = list(nn, Nod(OAS, v.Name.Heapaddr, prealloc[v]))
|
||||
nn = append(nn, Nod(OAS, v.Name.Heapaddr, prealloc[v]))
|
||||
if v.Class&^PHEAP != PPARAMOUT {
|
||||
as = Nod(OAS, v, v.Name.Param.Stackparam)
|
||||
v.Name.Param.Stackparam.Typecheck = 1
|
||||
typecheck(&as, Etop)
|
||||
as = applywritebarrier(as, &nn)
|
||||
nn = list(nn, as)
|
||||
as = applywritebarrier(as)
|
||||
nn = append(nn, as)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2587,17 +2594,17 @@ func paramstoheap(argin **Type, out int) *NodeList {
|
||||
}
|
||||
|
||||
// walk through argout parameters copying back to stack
|
||||
func returnsfromheap(argin **Type) *NodeList {
|
||||
func returnsfromheap(argin **Type) []*Node {
|
||||
var savet Iter
|
||||
var v *Node
|
||||
|
||||
var nn *NodeList
|
||||
var nn []*Node
|
||||
for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) {
|
||||
v = t.Nname
|
||||
if v == nil || v.Class != PHEAP|PPARAMOUT {
|
||||
continue
|
||||
}
|
||||
nn = list(nn, Nod(OAS, v.Name.Param.Stackparam, v))
|
||||
nn = append(nn, Nod(OAS, v.Name.Param.Stackparam, v))
|
||||
}
|
||||
|
||||
return nn
|
||||
@ -2610,11 +2617,11 @@ func heapmoves() {
|
||||
lno := lineno
|
||||
lineno = Curfn.Lineno
|
||||
nn := paramstoheap(getthis(Curfn.Type), 0)
|
||||
nn = concat(nn, paramstoheap(getinarg(Curfn.Type), 0))
|
||||
nn = concat(nn, paramstoheap(Getoutarg(Curfn.Type), 1))
|
||||
Curfn.Func.Enter = concat(Curfn.Func.Enter, nn)
|
||||
nn = append(nn, paramstoheap(getinarg(Curfn.Type), 0)...)
|
||||
nn = append(nn, paramstoheap(Getoutarg(Curfn.Type), 1)...)
|
||||
Curfn.Func.Enter.Append(nn...)
|
||||
lineno = Curfn.Func.Endlineno
|
||||
Curfn.Func.Exit = returnsfromheap(Getoutarg(Curfn.Type))
|
||||
Curfn.Func.Exit.Append(returnsfromheap(Getoutarg(Curfn.Type))...)
|
||||
lineno = lno
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,6 @@ import (
|
||||
)
|
||||
|
||||
func defframe(ptxt *obj.Prog) {
|
||||
var n *gc.Node
|
||||
|
||||
// fill in argument size, stack size
|
||||
ptxt.To.Type = obj.TYPE_TEXTSIZE
|
||||
|
||||
@ -30,8 +28,7 @@ func defframe(ptxt *obj.Prog) {
|
||||
lo := hi
|
||||
|
||||
// iterate through declarations - they are sorted in decreasing xoffset order.
|
||||
for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
|
||||
n = l.N
|
||||
for _, n := range gc.Curfn.Func.Dcl {
|
||||
if !n.Name.Needzero {
|
||||
continue
|
||||
}
|
||||
|
@ -12,8 +12,6 @@ import (
|
||||
)
|
||||
|
||||
func defframe(ptxt *obj.Prog) {
|
||||
var n *gc.Node
|
||||
|
||||
// fill in argument size, stack size
|
||||
ptxt.To.Type = obj.TYPE_TEXTSIZE
|
||||
|
||||
@ -30,8 +28,7 @@ func defframe(ptxt *obj.Prog) {
|
||||
lo := hi
|
||||
|
||||
// iterate through declarations - they are sorted in decreasing xoffset order.
|
||||
for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
|
||||
n = l.N
|
||||
for _, n := range gc.Curfn.Func.Dcl {
|
||||
if !n.Name.Needzero {
|
||||
continue
|
||||
}
|
||||
@ -133,7 +130,7 @@ func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) {
|
||||
// The hardware will generate undefined result.
|
||||
// Also need to explicitly trap on division on zero,
|
||||
// the hardware will silently generate undefined result.
|
||||
// DIVW will leave unpredicable result in higher 32-bit,
|
||||
// DIVW will leave unpredictable result in higher 32-bit,
|
||||
// so always use DIVD/DIVDU.
|
||||
t := nl.Type
|
||||
|
||||
|
@ -11,8 +11,6 @@ import (
|
||||
)
|
||||
|
||||
func defframe(ptxt *obj.Prog) {
|
||||
var n *gc.Node
|
||||
|
||||
// fill in argument size, stack size
|
||||
ptxt.To.Type = obj.TYPE_TEXTSIZE
|
||||
|
||||
@ -28,8 +26,7 @@ func defframe(ptxt *obj.Prog) {
|
||||
hi := int64(0)
|
||||
lo := hi
|
||||
ax := uint32(0)
|
||||
for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
|
||||
n = l.N
|
||||
for _, n := range gc.Curfn.Func.Dcl {
|
||||
if !n.Name.Needzero {
|
||||
continue
|
||||
}
|
||||
|
@ -284,7 +284,7 @@ func elimshortmov(g *gc.Graph) {
|
||||
}
|
||||
|
||||
if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST {
|
||||
// move or artihmetic into partial register.
|
||||
// move or arithmetic into partial register.
|
||||
// from another register or constant can be movl.
|
||||
// we don't switch to 32-bit arithmetic if it can
|
||||
// change how the carry bit is set (and the carry bit is needed).
|
||||
|
66
src/cmd/dist/build.go
vendored
66
src/cmd/dist/build.go
vendored
@ -6,6 +6,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
@ -463,6 +464,9 @@ var deptab = []struct {
|
||||
{"runtime/internal/sys", []string{
|
||||
"zversion.go",
|
||||
}},
|
||||
{"go/build", []string{
|
||||
"zcgo.go",
|
||||
}},
|
||||
}
|
||||
|
||||
// depsuffix records the allowed suffixes for source files.
|
||||
@ -478,6 +482,7 @@ var gentab = []struct {
|
||||
}{
|
||||
{"zdefaultcc.go", mkzdefaultcc},
|
||||
{"zversion.go", mkzversion},
|
||||
{"zcgo.go", mkzcgo},
|
||||
|
||||
// not generated anymore, but delete the file if we see it
|
||||
{"enam.c", nil},
|
||||
@ -933,6 +938,7 @@ func usage() {
|
||||
"clean deletes all built files\n" +
|
||||
"env [-p] print environment (-p: include $PATH)\n" +
|
||||
"install [dir] install individual directory\n" +
|
||||
"list [-json] list all supported platforms\n" +
|
||||
"test [-h] run Go test(s)\n" +
|
||||
"version print Go version\n" +
|
||||
"\n" +
|
||||
@ -1061,9 +1067,13 @@ func cmdbootstrap() {
|
||||
}
|
||||
}
|
||||
|
||||
// Copied from go/build/build.go.
|
||||
// Cannot use go/build directly because cmd/dist for a new release
|
||||
// builds against an old release's go/build, which may be out of sync.
|
||||
// To reduce duplication, we generate the list for go/build from this.
|
||||
//
|
||||
// We list all supported platforms in this list, so that this is the
|
||||
// single point of truth for supported platforms. This list is used
|
||||
// by 'go tool dist list'.
|
||||
var cgoEnabled = map[string]bool{
|
||||
"darwin/386": true,
|
||||
"darwin/amd64": true,
|
||||
@ -1072,19 +1082,31 @@ var cgoEnabled = map[string]bool{
|
||||
"dragonfly/amd64": true,
|
||||
"freebsd/386": true,
|
||||
"freebsd/amd64": true,
|
||||
"freebsd/arm": false,
|
||||
"linux/386": true,
|
||||
"linux/amd64": true,
|
||||
"linux/arm": true,
|
||||
"linux/arm64": true,
|
||||
"linux/ppc64": false,
|
||||
"linux/ppc64le": true,
|
||||
"linux/mips64": false,
|
||||
"linux/mips64le": false,
|
||||
"android/386": true,
|
||||
"android/amd64": true,
|
||||
"android/arm": true,
|
||||
"android/arm64": true,
|
||||
"nacl/386": false,
|
||||
"nacl/amd64p32": false,
|
||||
"nacl/arm": false,
|
||||
"netbsd/386": true,
|
||||
"netbsd/amd64": true,
|
||||
"netbsd/arm": true,
|
||||
"openbsd/386": true,
|
||||
"openbsd/amd64": true,
|
||||
"openbsd/arm": false,
|
||||
"plan9/386": false,
|
||||
"plan9/amd64": false,
|
||||
"plan9/arm": false,
|
||||
"solaris/amd64": true,
|
||||
"windows/386": true,
|
||||
"windows/amd64": true,
|
||||
@ -1128,7 +1150,7 @@ func defaulttarg() string {
|
||||
fatal("current directory %s is not under %s", pwd, real_src)
|
||||
}
|
||||
pwd = pwd[len(real_src):]
|
||||
// guard againt xrealwd return the directory without the trailing /
|
||||
// guard against xrealwd returning the directory without the trailing /
|
||||
pwd = strings.TrimPrefix(pwd, "/")
|
||||
|
||||
return pwd
|
||||
@ -1195,3 +1217,43 @@ func cmdversion() {
|
||||
xflagparse(0)
|
||||
xprintf("%s\n", findgoversion())
|
||||
}
|
||||
|
||||
// cmdlist lists all supported platforms.
|
||||
func cmdlist() {
|
||||
jsonFlag := flag.Bool("json", false, "produce JSON output")
|
||||
xflagparse(0)
|
||||
|
||||
var plats []string
|
||||
for p := range cgoEnabled {
|
||||
plats = append(plats, p)
|
||||
}
|
||||
sort.Strings(plats)
|
||||
|
||||
if !*jsonFlag {
|
||||
for _, p := range plats {
|
||||
xprintf("%s\n", p)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type jsonResult struct {
|
||||
GOOS string
|
||||
GOARCH string
|
||||
CgoSupported bool
|
||||
}
|
||||
var results []jsonResult
|
||||
for _, p := range plats {
|
||||
fields := strings.Split(p, "/")
|
||||
results = append(results, jsonResult{
|
||||
GOOS: fields[0],
|
||||
GOARCH: fields[1],
|
||||
CgoSupported: cgoEnabled[p]})
|
||||
}
|
||||
out, err := json.MarshalIndent(results, "", "\t")
|
||||
if err != nil {
|
||||
fatal("json marshal error: %v", err)
|
||||
}
|
||||
if _, err := os.Stdout.Write(out); err != nil {
|
||||
fatal("write failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
30
src/cmd/dist/buildgo.go
vendored
30
src/cmd/dist/buildgo.go
vendored
@ -4,7 +4,10 @@
|
||||
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
* Helpers for building cmd/go and cmd/cgo.
|
||||
@ -37,3 +40,28 @@ func mkzdefaultcc(dir, file string) {
|
||||
file = file[:i] + "c" + file[i:]
|
||||
writefile(out, file, writeSkipSame)
|
||||
}
|
||||
|
||||
// mkzcgo writes zcgo.go for go/build package:
|
||||
//
|
||||
// package build
|
||||
// var cgoEnabled = map[string]bool{}
|
||||
//
|
||||
// It is invoked to write go/build/zcgo.go.
|
||||
func mkzcgo(dir, file string) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
fmt.Fprintf(&buf,
|
||||
"// auto generated by go tool dist\n"+
|
||||
"\n"+
|
||||
"package build\n"+
|
||||
"\n"+
|
||||
"var cgoEnabled = map[string]bool{\n")
|
||||
for plat, hasCgo := range cgoEnabled {
|
||||
if hasCgo {
|
||||
fmt.Fprintf(&buf, "\t%q: true,\n", plat)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(&buf, "}")
|
||||
|
||||
writefile(buf.String(), file, writeSkipSame)
|
||||
}
|
||||
|
1
src/cmd/dist/main.go
vendored
1
src/cmd/dist/main.go
vendored
@ -21,6 +21,7 @@ var cmdtab = []struct {
|
||||
{"clean", cmdclean},
|
||||
{"env", cmdenv},
|
||||
{"install", cmdinstall},
|
||||
{"list", cmdlist},
|
||||
{"test", cmdtest},
|
||||
{"version", cmdversion},
|
||||
}
|
||||
|
14
src/cmd/dist/test.go
vendored
14
src/cmd/dist/test.go
vendored
@ -441,6 +441,20 @@ func (t *tester) registerTests() {
|
||||
return nil
|
||||
},
|
||||
})
|
||||
fortran := os.Getenv("FC")
|
||||
if fortran == "" {
|
||||
fortran, _ = exec.LookPath("gfortran")
|
||||
}
|
||||
if fortran != "" {
|
||||
t.tests = append(t.tests, distTest{
|
||||
name: "cgo_fortran",
|
||||
heading: "../misc/cgo/fortran",
|
||||
fn: func(dt *distTest) error {
|
||||
t.addCmd(dt, "misc/cgo/fortran", "./test.bash", fortran)
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
if t.cgoEnabled && t.goos != "android" && !t.iOS() {
|
||||
// TODO(crawshaw): reenable on android and iOS
|
||||
|
@ -1348,6 +1348,11 @@ func (b *builder) build(a *action) (err error) {
|
||||
return fmt.Errorf("can't build package %s because it contains Objective-C files (%s) but it's not using cgo nor SWIG",
|
||||
a.p.ImportPath, strings.Join(a.p.MFiles, ","))
|
||||
}
|
||||
// Same as above for Fortran files
|
||||
if len(a.p.FFiles) > 0 && !a.p.usesCgo() && !a.p.usesSwig() {
|
||||
return fmt.Errorf("can't build package %s because it contains Fortran files (%s) but it's not using cgo nor SWIG",
|
||||
a.p.ImportPath, strings.Join(a.p.FFiles, ","))
|
||||
}
|
||||
defer func() {
|
||||
if err != nil && err != errPrintedOutput {
|
||||
err = fmt.Errorf("go build %s: %v", a.p.ImportPath, err)
|
||||
@ -1437,7 +1442,7 @@ func (b *builder) build(a *action) (err error) {
|
||||
if a.cgo != nil && a.cgo.target != "" {
|
||||
cgoExe = a.cgo.target
|
||||
}
|
||||
outGo, outObj, err := b.cgo(a.p, cgoExe, obj, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, cxxfiles, a.p.MFiles)
|
||||
outGo, outObj, err := b.cgo(a.p, cgoExe, obj, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, cxxfiles, a.p.MFiles, a.p.FFiles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -2272,7 +2277,7 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool,
|
||||
// so that it can give good error messages about forward declarations.
|
||||
// Exceptions: a few standard packages have forward declarations for
|
||||
// pieces supplied behind-the-scenes by package runtime.
|
||||
extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles)
|
||||
extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.FFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles)
|
||||
if p.Standard {
|
||||
switch p.ImportPath {
|
||||
case "bytes", "net", "os", "runtime/pprof", "sync", "time":
|
||||
@ -2623,6 +2628,7 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions
|
||||
usesCgo := false
|
||||
cxx := len(root.p.CXXFiles) > 0 || len(root.p.SwigCXXFiles) > 0
|
||||
objc := len(root.p.MFiles) > 0
|
||||
fortran := len(root.p.FFiles) > 0
|
||||
|
||||
actionsSeen := make(map[*action]bool)
|
||||
// Make a pre-order depth-first traversal of the action graph, taking note of
|
||||
@ -2697,6 +2703,9 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions
|
||||
if len(a.p.MFiles) > 0 {
|
||||
objc = true
|
||||
}
|
||||
if len(a.p.FFiles) > 0 {
|
||||
fortran = true
|
||||
}
|
||||
}
|
||||
|
||||
ldflags = append(ldflags, "-Wl,--whole-archive")
|
||||
@ -2768,6 +2777,17 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions
|
||||
if objc {
|
||||
ldflags = append(ldflags, "-lobjc")
|
||||
}
|
||||
if fortran {
|
||||
fc := os.Getenv("FC")
|
||||
if fc == "" {
|
||||
fc = "gfortran"
|
||||
}
|
||||
// support gfortran out of the box and let others pass the correct link options
|
||||
// via CGO_LDFLAGS
|
||||
if strings.Contains(fc, "gfortran") {
|
||||
ldflags = append(ldflags, "-lgfortran")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := b.run(".", root.p.ImportPath, nil, tools.linker(), "-o", out, ofiles, ldflags, buildGccgoflags); err != nil {
|
||||
@ -2862,6 +2882,11 @@ func (b *builder) gxx(p *Package, out string, flags []string, cxxfile string) er
|
||||
return b.ccompile(p, out, flags, cxxfile, b.gxxCmd(p.Dir))
|
||||
}
|
||||
|
||||
// gfortran runs the gfortran Fortran compiler to create an object from a single Fortran file.
|
||||
func (b *builder) gfortran(p *Package, out string, flags []string, ffile string) error {
|
||||
return b.ccompile(p, out, flags, ffile, b.gfortranCmd(p.Dir))
|
||||
}
|
||||
|
||||
// ccompile runs the given C or C++ compiler and creates an object from a single source file.
|
||||
func (b *builder) ccompile(p *Package, out string, flags []string, file string, compiler []string) error {
|
||||
file = mkAbs(p.Dir, file)
|
||||
@ -2891,6 +2916,11 @@ func (b *builder) gxxCmd(objdir string) []string {
|
||||
return b.ccompilerCmd("CXX", defaultCXX, objdir)
|
||||
}
|
||||
|
||||
// gfortranCmd returns a gfortran command line prefix.
|
||||
func (b *builder) gfortranCmd(objdir string) []string {
|
||||
return b.ccompilerCmd("FC", "gfortran", objdir)
|
||||
}
|
||||
|
||||
// ccompilerCmd returns a command line prefix for the given environment
|
||||
// variable and using the default command when the variable is empty.
|
||||
func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string {
|
||||
@ -3009,8 +3039,8 @@ func envList(key, def string) []string {
|
||||
return strings.Fields(v)
|
||||
}
|
||||
|
||||
// Return the flags to use when invoking the C or C++ compilers, or cgo.
|
||||
func (b *builder) cflags(p *Package, def bool) (cppflags, cflags, cxxflags, ldflags []string) {
|
||||
// Return the flags to use when invoking the C, C++ or Fortran compilers, or cgo.
|
||||
func (b *builder) cflags(p *Package, def bool) (cppflags, cflags, cxxflags, fflags, ldflags []string) {
|
||||
var defaults string
|
||||
if def {
|
||||
defaults = "-g -O2"
|
||||
@ -3019,15 +3049,16 @@ func (b *builder) cflags(p *Package, def bool) (cppflags, cflags, cxxflags, ldfl
|
||||
cppflags = stringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS)
|
||||
cflags = stringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS)
|
||||
cxxflags = stringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS)
|
||||
fflags = stringList(envList("CGO_FFLAGS", defaults), p.CgoFFLAGS)
|
||||
ldflags = stringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS)
|
||||
return
|
||||
}
|
||||
|
||||
var cgoRe = regexp.MustCompile(`[/\\:]`)
|
||||
|
||||
func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) {
|
||||
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoLDFLAGS := b.cflags(p, true)
|
||||
_, cgoexeCFLAGS, _, _ := b.cflags(p, false)
|
||||
func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, gxxfiles, mfiles, ffiles []string) (outGo, outObj []string, err error) {
|
||||
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoFFLAGS, cgoLDFLAGS := b.cflags(p, true)
|
||||
_, cgoexeCFLAGS, _, _, _ := b.cflags(p, false)
|
||||
cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...)
|
||||
cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...)
|
||||
// If we are compiling Objective-C code, then we need to link against libobjc
|
||||
@ -3035,6 +3066,19 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
|
||||
cgoLDFLAGS = append(cgoLDFLAGS, "-lobjc")
|
||||
}
|
||||
|
||||
// Likewise for Fortran, except there are many Fortran compilers.
|
||||
// Support gfortran out of the box and let others pass the correct link options
|
||||
// via CGO_LDFLAGS
|
||||
if len(ffiles) > 0 {
|
||||
fc := os.Getenv("FC")
|
||||
if fc == "" {
|
||||
fc = "gfortran"
|
||||
}
|
||||
if strings.Contains(fc, "gfortran") {
|
||||
cgoLDFLAGS = append(cgoLDFLAGS, "-lgfortran")
|
||||
}
|
||||
}
|
||||
|
||||
if buildMSan && p.ImportPath != "runtime/cgo" {
|
||||
cgoCFLAGS = append([]string{"-fsanitize=memory"}, cgoCFLAGS...)
|
||||
cgoLDFLAGS = append([]string{"-fsanitize=memory"}, cgoLDFLAGS...)
|
||||
@ -3202,6 +3246,17 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
|
||||
outObj = append(outObj, ofile)
|
||||
}
|
||||
|
||||
fflags := stringList(cgoCPPFLAGS, cgoFFLAGS)
|
||||
for _, file := range ffiles {
|
||||
// Append .o to the file, just in case the pkg has file.c and file.f
|
||||
ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o"
|
||||
if err := b.gfortran(p, ofile, fflags, file); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
linkobj = append(linkobj, ofile)
|
||||
outObj = append(outObj, ofile)
|
||||
}
|
||||
|
||||
linkobj = append(linkobj, p.SysoFiles...)
|
||||
dynobj := obj + "_cgo_.o"
|
||||
pie := (goarch == "arm" && goos == "linux") || goos == "android"
|
||||
@ -3395,7 +3450,7 @@ func (b *builder) swigIntSize(obj string) (intsize string, err error) {
|
||||
|
||||
// Run SWIG on one SWIG input file.
|
||||
func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx bool, intgosize string) (outGo, outC string, err error) {
|
||||
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true)
|
||||
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _, _ := b.cflags(p, true)
|
||||
var cflags []string
|
||||
if cxx {
|
||||
cflags = stringList(cgoCPPFLAGS, pcCFLAGS, cgoCXXFLAGS)
|
||||
|
@ -119,6 +119,14 @@ func runGet(cmd *Command, args []string) {
|
||||
delete(packageCache, name)
|
||||
}
|
||||
|
||||
// In order to rebuild packages information completely,
|
||||
// we need to clear commands cache. Command packages are
|
||||
// referring to evicted packages from the package cache.
|
||||
// This leads to duplicated loads of the standard packages.
|
||||
for name := range cmdCache {
|
||||
delete(cmdCache, name)
|
||||
}
|
||||
|
||||
args = importPaths(args)
|
||||
packagesForBuild(args)
|
||||
|
||||
|
@ -2122,7 +2122,7 @@ func TestIssue7108(t *testing.T) {
|
||||
// cmd/go: go test -a foo does not rebuild regexp.
|
||||
func TestIssue6844(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("don't rebuild the standard libary in short mode")
|
||||
t.Skip("don't rebuild the standard library in short mode")
|
||||
}
|
||||
|
||||
tg := testgo(t)
|
||||
@ -2763,6 +2763,10 @@ func TestCgoConsistentResults(t *testing.T) {
|
||||
if !canCgo {
|
||||
t.Skip("skipping because cgo not enabled")
|
||||
}
|
||||
if runtime.GOOS == "solaris" {
|
||||
// See https://golang.org/issue/13247
|
||||
t.Skip("skipping because Solaris builds are known to be inconsistent; see #13247")
|
||||
}
|
||||
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
@ -2785,3 +2789,15 @@ func TestCgoConsistentResults(t *testing.T) {
|
||||
t.Error("building cgotest twice did not produce the same output")
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 14444: go get -u .../ duplicate loads errors
|
||||
func TestGoGetUpdateAllDoesNotTryToLoadDuplicates(t *testing.T) {
|
||||
testenv.MustHaveExternalNetwork(t)
|
||||
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.makeTempdir()
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
tg.run("get", "-u", ".../")
|
||||
tg.grepStderrNot("duplicate loads of", "did not remove old packages from cache")
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ syntax of package template. The default output is equivalent to -f
|
||||
CXXFiles []string // .cc, .cxx and .cpp source files
|
||||
MFiles []string // .m source files
|
||||
HFiles []string // .h, .hh, .hpp and .hxx source files
|
||||
FFiles []string // .f, .F, .for and .f90 Fortran source files
|
||||
SFiles []string // .s source files
|
||||
SwigFiles []string // .swig files
|
||||
SwigCXXFiles []string // .swigcxx files
|
||||
@ -60,6 +61,7 @@ syntax of package template. The default output is equivalent to -f
|
||||
CgoCFLAGS []string // cgo: flags for C compiler
|
||||
CgoCPPFLAGS []string // cgo: flags for C preprocessor
|
||||
CgoCXXFLAGS []string // cgo: flags for C++ compiler
|
||||
CgoFFLAGS []string // cgo: flags for Fortran compiler
|
||||
CgoLDFLAGS []string // cgo: flags for linker
|
||||
CgoPkgConfig []string // cgo: pkg-config names
|
||||
|
||||
|
@ -50,6 +50,7 @@ type Package struct {
|
||||
CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files
|
||||
MFiles []string `json:",omitempty"` // .m source files
|
||||
HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx source files
|
||||
FFiles []string `json:",omitempty"` // .f, .F, .for and .f90 Fortran source files
|
||||
SFiles []string `json:",omitempty"` // .s source files
|
||||
SwigFiles []string `json:",omitempty"` // .swig files
|
||||
SwigCXXFiles []string `json:",omitempty"` // .swigcxx files
|
||||
@ -59,6 +60,7 @@ type Package struct {
|
||||
CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler
|
||||
CgoCPPFLAGS []string `json:",omitempty"` // cgo: flags for C preprocessor
|
||||
CgoCXXFLAGS []string `json:",omitempty"` // cgo: flags for C++ compiler
|
||||
CgoFFLAGS []string `json:",omitempty"` // cgo: flags for Fortran compiler
|
||||
CgoLDFLAGS []string `json:",omitempty"` // cgo: flags for linker
|
||||
CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names
|
||||
|
||||
@ -161,6 +163,7 @@ func (p *Package) copyBuild(pp *build.Package) {
|
||||
p.CXXFiles = pp.CXXFiles
|
||||
p.MFiles = pp.MFiles
|
||||
p.HFiles = pp.HFiles
|
||||
p.FFiles = pp.FFiles
|
||||
p.SFiles = pp.SFiles
|
||||
p.SwigFiles = pp.SwigFiles
|
||||
p.SwigCXXFiles = pp.SwigCXXFiles
|
||||
@ -909,6 +912,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
|
||||
p.CXXFiles,
|
||||
p.MFiles,
|
||||
p.HFiles,
|
||||
p.FFiles,
|
||||
p.SFiles,
|
||||
p.SysoFiles,
|
||||
p.SwigFiles,
|
||||
@ -1216,7 +1220,7 @@ var isGoRelease = strings.HasPrefix(runtime.Version(), "go1")
|
||||
// an explicit data comparison. Specifically, we build a list of the
|
||||
// inputs to the build, compute its SHA1 hash, and record that as the
|
||||
// ``build ID'' in the generated object. At the next build, we can
|
||||
// recompute the buid ID and compare it to the one in the generated
|
||||
// recompute the build ID and compare it to the one in the generated
|
||||
// object. If they differ, the list of inputs has changed, so the object
|
||||
// is out of date and must be rebuilt.
|
||||
//
|
||||
@ -1495,7 +1499,7 @@ func isStale(p *Package) bool {
|
||||
// to test for write access, and then skip GOPATH roots we don't have write
|
||||
// access to. But hopefully we can just use the mtimes always.
|
||||
|
||||
srcs := stringList(p.GoFiles, p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.SFiles, p.CgoFiles, p.SysoFiles, p.SwigFiles, p.SwigCXXFiles)
|
||||
srcs := stringList(p.GoFiles, p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.CgoFiles, p.SysoFiles, p.SwigFiles, p.SwigCXXFiles)
|
||||
for _, src := range srcs {
|
||||
if olderThan(filepath.Join(p.Dir, src)) {
|
||||
return true
|
||||
|
@ -1206,11 +1206,11 @@ func (b *builder) notest(a *action) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// isTestMain tells whether fn is a TestMain(m *testing.M) function.
|
||||
func isTestMain(fn *ast.FuncDecl) bool {
|
||||
if fn.Name.String() != "TestMain" ||
|
||||
fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
|
||||
fn.Type.Params == nil ||
|
||||
// isTestFunc tells whether fn has the type of a testing function. arg
|
||||
// specifies the parameter type we look for: B, M or T.
|
||||
func isTestFunc(fn *ast.FuncDecl, arg string) bool {
|
||||
if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
|
||||
fn.Type.Params.List == nil ||
|
||||
len(fn.Type.Params.List) != 1 ||
|
||||
len(fn.Type.Params.List[0].Names) > 1 {
|
||||
return false
|
||||
@ -1222,10 +1222,11 @@ func isTestMain(fn *ast.FuncDecl) bool {
|
||||
// We can't easily check that the type is *testing.M
|
||||
// because we don't know how testing has been imported,
|
||||
// but at least check that it's *M or *something.M.
|
||||
if name, ok := ptr.X.(*ast.Ident); ok && name.Name == "M" {
|
||||
// Same applies for B and T.
|
||||
if name, ok := ptr.X.(*ast.Ident); ok && name.Name == arg {
|
||||
return true
|
||||
}
|
||||
if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == "M" {
|
||||
if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == arg {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@ -1344,16 +1345,24 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
|
||||
}
|
||||
name := n.Name.String()
|
||||
switch {
|
||||
case isTestMain(n):
|
||||
case name == "TestMain" && isTestFunc(n, "M"):
|
||||
if t.TestMain != nil {
|
||||
return errors.New("multiple definitions of TestMain")
|
||||
}
|
||||
t.TestMain = &testFunc{pkg, name, ""}
|
||||
*doImport, *seen = true, true
|
||||
case isTest(name, "Test"):
|
||||
err := checkTestFunc(n, "T")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Tests = append(t.Tests, testFunc{pkg, name, ""})
|
||||
*doImport, *seen = true, true
|
||||
case isTest(name, "Benchmark"):
|
||||
err := checkTestFunc(n, "B")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, ""})
|
||||
*doImport, *seen = true, true
|
||||
}
|
||||
@ -1372,6 +1381,15 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkTestFunc(fn *ast.FuncDecl, arg string) error {
|
||||
if !isTestFunc(fn, arg) {
|
||||
name := fn.Name.String()
|
||||
pos := testFileSet.Position(fn.Pos())
|
||||
return fmt.Errorf("%s: wrong signature for %s, must be: func %s(%s *testing.%s)", pos, name, name, strings.ToLower(arg), arg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type byOrder []*doc.Example
|
||||
|
||||
func (x byOrder) Len() int { return len(x) }
|
||||
|
@ -530,7 +530,7 @@ func (r *objReader) parseArchive() error {
|
||||
return errCorruptArchive
|
||||
}
|
||||
switch name {
|
||||
case "__.SYMDEF", "__.GOSYMDEF", "__.PKGDEF":
|
||||
case "__.PKGDEF":
|
||||
r.skip(size)
|
||||
default:
|
||||
oldLimit := r.limit
|
||||
|
@ -55,7 +55,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
|
||||
}
|
||||
}
|
||||
|
||||
// Replace TLS register fetches on older ARM procesors.
|
||||
// Replace TLS register fetches on older ARM processors.
|
||||
switch p.As {
|
||||
// Treat MRC 15, 0, <reg>, C13, C0, 3 specially.
|
||||
case AMRC:
|
||||
|
@ -444,6 +444,11 @@ const (
|
||||
R_PLT1
|
||||
R_PLT2
|
||||
R_USEFIELD
|
||||
// R_USETYPE resolves to an *rtype, but no relocation is created. The
|
||||
// linker uses this as a signal that the pointed-to type information
|
||||
// should be linked into the final binary, even if there are no other
|
||||
// direct references. (This is used for types reachable by reflection.)
|
||||
R_USETYPE
|
||||
R_POWER_TOC
|
||||
R_GOTPCREL
|
||||
// R_JMPMIPS (only used on mips64) resolves to non-PC-relative target address
|
||||
@ -572,6 +577,7 @@ type Link struct {
|
||||
Debugpcln int32
|
||||
Flag_shared int32
|
||||
Flag_dynlink bool
|
||||
Flag_optimize bool
|
||||
Bso *Biobuf
|
||||
Pathname string
|
||||
Windows int32
|
||||
@ -617,6 +623,10 @@ type Link struct {
|
||||
Data *LSym
|
||||
Etext *LSym
|
||||
Edata *LSym
|
||||
|
||||
// Cache of Progs
|
||||
allocIdx int
|
||||
progs [10000]Prog
|
||||
}
|
||||
|
||||
func (ctxt *Link) Diag(format string, args ...interface{}) {
|
||||
|
@ -1234,7 +1234,7 @@ func markregused(ctxt *obj.Link, s *Sch) {
|
||||
}
|
||||
|
||||
/*
|
||||
* test to see if 2 instrictions can be
|
||||
* test to see if two instructions can be
|
||||
* interchanged without changing semantics
|
||||
*/
|
||||
func depend(ctxt *obj.Link, sa, sb *Sch) bool {
|
||||
|
@ -116,6 +116,12 @@ func Writeobjdirect(ctxt *Link, b *Biobuf) {
|
||||
}
|
||||
|
||||
func Flushplist(ctxt *Link) {
|
||||
flushplist(ctxt, ctxt.Debugasm == 0)
|
||||
}
|
||||
func FlushplistNoFree(ctxt *Link) {
|
||||
flushplist(ctxt, false)
|
||||
}
|
||||
func flushplist(ctxt *Link, freeProgs bool) {
|
||||
var flag int
|
||||
var s *LSym
|
||||
var p *Prog
|
||||
@ -295,21 +301,34 @@ func Flushplist(ctxt *Link) {
|
||||
for s := text; s != nil; s = s.Next {
|
||||
mkfwd(s)
|
||||
linkpatch(ctxt, s)
|
||||
if ctxt.Flag_optimize {
|
||||
ctxt.Arch.Follow(ctxt, s)
|
||||
}
|
||||
ctxt.Arch.Preprocess(ctxt, s)
|
||||
ctxt.Arch.Assemble(ctxt, s)
|
||||
fieldtrack(ctxt, s)
|
||||
linkpcln(ctxt, s)
|
||||
if freeProgs {
|
||||
s.Text = nil
|
||||
s.Etext = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Add to running list in ctxt.
|
||||
if ctxt.Etext == nil {
|
||||
if text != nil {
|
||||
if ctxt.Text == nil {
|
||||
ctxt.Text = text
|
||||
} else {
|
||||
ctxt.Etext.Next = text
|
||||
}
|
||||
ctxt.Etext = etext
|
||||
}
|
||||
ctxt.Plist = nil
|
||||
ctxt.Plast = nil
|
||||
ctxt.Curp = nil
|
||||
if freeProgs {
|
||||
ctxt.freeProgs()
|
||||
}
|
||||
}
|
||||
|
||||
func Writeobjfile(ctxt *Link, b *Biobuf) {
|
||||
|
@ -202,6 +202,7 @@ func linkpatch(ctxt *Link, sym *LSym) {
|
||||
p.Pcond = q
|
||||
}
|
||||
|
||||
if ctxt.Flag_optimize {
|
||||
for p := sym.Text; p != nil; p = p.Link {
|
||||
if p.Pcond != nil {
|
||||
p.Pcond = brloop(ctxt, p.Pcond)
|
||||
@ -213,3 +214,4 @@ func linkpatch(ctxt *Link, sym *LSym) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -110,6 +110,7 @@ func Linknew(arch *LinkArch) *Link {
|
||||
ctxt.Goarm = Getgoarm()
|
||||
}
|
||||
|
||||
ctxt.Flag_optimize = true
|
||||
return ctxt
|
||||
}
|
||||
|
||||
|
@ -287,6 +287,10 @@ func CConv(s uint8) string {
|
||||
}
|
||||
|
||||
func (p *Prog) String() string {
|
||||
if p == nil {
|
||||
return "<nil Prog>"
|
||||
}
|
||||
|
||||
if p.Ctxt == nil {
|
||||
return "<Prog without ctxt>"
|
||||
}
|
||||
@ -325,10 +329,23 @@ func (p *Prog) String() string {
|
||||
}
|
||||
|
||||
func (ctxt *Link) NewProg() *Prog {
|
||||
p := new(Prog) // should be the only call to this; all others should use ctxt.NewProg
|
||||
var p *Prog
|
||||
if i := ctxt.allocIdx; i < len(ctxt.progs) {
|
||||
p = &ctxt.progs[i]
|
||||
ctxt.allocIdx = i + 1
|
||||
} else {
|
||||
p = new(Prog) // should be the only call to this; all others should use ctxt.NewProg
|
||||
}
|
||||
p.Ctxt = ctxt
|
||||
return p
|
||||
}
|
||||
func (ctxt *Link) freeProgs() {
|
||||
s := ctxt.progs[:ctxt.allocIdx]
|
||||
for i := range s {
|
||||
s[i] = Prog{}
|
||||
}
|
||||
ctxt.allocIdx = 0
|
||||
}
|
||||
|
||||
func (ctxt *Link) Line(n int) string {
|
||||
return ctxt.LineHist.LineString(n)
|
||||
|
@ -189,7 +189,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite 0 to $0 in 3rd argment to CMPPS etc.
|
||||
// Rewrite 0 to $0 in 3rd argument to CMPPS etc.
|
||||
// That's what the tables expect.
|
||||
switch p.As {
|
||||
case ACMPPD, ACMPPS, ACMPSD, ACMPSS:
|
||||
|
@ -109,7 +109,7 @@ func gentext() {
|
||||
}
|
||||
|
||||
// Preserve highest 8 bits of a, and do addition to lower 24-bit
|
||||
// of a and b; used to adjust ARM branch intruction's target
|
||||
// of a and b; used to adjust ARM branch instruction's target
|
||||
func braddoff(a int32, b int32) int32 {
|
||||
return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b))
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ func archinit() {
|
||||
}
|
||||
|
||||
case obj.Hdarwin: /* apple MACH */
|
||||
ld.Debug['w'] = 1 // disable DWARF generataion
|
||||
ld.Debug['w'] = 1 // disable DWARF generation
|
||||
ld.Machoinit()
|
||||
ld.HEADR = ld.INITIAL_MACHO_HEADR
|
||||
if ld.INITTEXT == -1 {
|
||||
|
@ -162,7 +162,7 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
|
||||
rs := r.Xsym
|
||||
|
||||
// ld64 has a bug handling MACHO_ARM64_RELOC_UNSIGNED with !extern relocation.
|
||||
// see cmd/internal/ld/data.go for details. The workarond is that don't use !extern
|
||||
// see cmd/internal/ld/data.go for details. The workaround is that don't use !extern
|
||||
// UNSIGNED relocation at all.
|
||||
if rs.Type == obj.SHOSTOBJ || r.Type == obj.R_CALLARM64 || r.Type == obj.R_ADDRARM64 || r.Type == obj.R_ADDR {
|
||||
if rs.Dynid < 0 {
|
||||
|
@ -116,7 +116,7 @@ const (
|
||||
DW_CHILDREN_yes = 0x01
|
||||
)
|
||||
|
||||
// Not from the spec, but logicaly belongs here
|
||||
// Not from the spec, but logically belongs here
|
||||
const (
|
||||
DW_CLS_ADDRESS = 0x01 + iota
|
||||
DW_CLS_BLOCK
|
||||
|
@ -1066,9 +1066,9 @@ func readelfsym(elfobj *ElfObj, i int, sym *ElfSym, needSym int) (err error) {
|
||||
}
|
||||
|
||||
if needSym != 0 {
|
||||
// local names and hidden visiblity global names are unique
|
||||
// and should only reference by its index, not name, so we
|
||||
// don't bother to add them into hash table
|
||||
// local names and hidden global names are unique
|
||||
// and should only be referenced by their index, not name, so we
|
||||
// don't bother to add them into the hash table
|
||||
s = linknewsym(Ctxt, sym.name, Ctxt.Version)
|
||||
|
||||
s.Type |= obj.SHIDDEN
|
||||
|
@ -255,10 +255,7 @@ var coutbuf struct {
|
||||
f *os.File
|
||||
}
|
||||
|
||||
const (
|
||||
symname = "__.GOSYMDEF"
|
||||
pkgname = "__.PKGDEF"
|
||||
)
|
||||
const pkgname = "__.PKGDEF"
|
||||
|
||||
var (
|
||||
// Set if we see an object compiled by the host compiler that is not
|
||||
@ -781,7 +778,7 @@ func objfile(lib *Library) {
|
||||
return
|
||||
}
|
||||
|
||||
/* skip over optional __.GOSYMDEF and process __.PKGDEF */
|
||||
/* process __.PKGDEF */
|
||||
off := obj.Boffset(f)
|
||||
|
||||
var arhdr ArHdr
|
||||
@ -792,15 +789,6 @@ func objfile(lib *Library) {
|
||||
goto out
|
||||
}
|
||||
|
||||
if strings.HasPrefix(arhdr.name, symname) {
|
||||
off += l
|
||||
l = nextar(f, off, &arhdr)
|
||||
if l <= 0 {
|
||||
Diag("%s: short read on archive file symbol header", lib.File)
|
||||
goto out
|
||||
}
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(arhdr.name, pkgname) {
|
||||
Diag("%s: cannot find package header", lib.File)
|
||||
goto out
|
||||
@ -829,7 +817,7 @@ func objfile(lib *Library) {
|
||||
* the individual symbols that are unused.
|
||||
*
|
||||
* loading every object will also make it possible to
|
||||
* load foreign objects not referenced by __.GOSYMDEF.
|
||||
* load foreign objects not referenced by __.PKGDEF.
|
||||
*/
|
||||
for {
|
||||
l = nextar(f, off, &arhdr)
|
||||
|
@ -569,7 +569,7 @@ func Asmbmacho() {
|
||||
if Linkmode == LinkInternal {
|
||||
// For lldb, must say LC_VERSION_MIN_MACOSX or else
|
||||
// it won't know that this Mach-O binary is from OS X
|
||||
// (could be iOS or WatchOS intead).
|
||||
// (could be iOS or WatchOS instead).
|
||||
// Go on iOS uses linkmode=external, and linkmode=external
|
||||
// adds this itself. So we only need this code for linkmode=internal
|
||||
// and we can assume OS X.
|
||||
|
@ -1340,7 +1340,7 @@ const (
|
||||
addressOrder
|
||||
)
|
||||
|
||||
// sort reoders the entries in a report based on the specified
|
||||
// sort reorders the entries in a report based on the specified
|
||||
// ordering criteria. The result is sorted in decreasing order for
|
||||
// numeric quantities, alphabetically for text, and increasing for
|
||||
// addresses.
|
||||
|
@ -408,7 +408,7 @@ func getMissingFunctionSource(filename string, asm map[int]nodes, start, end int
|
||||
return fnodes, filename
|
||||
}
|
||||
|
||||
// adjustSourcePath adjusts the pathe for a source file by trimmming
|
||||
// adjustSourcePath adjusts the path for a source file by trimming
|
||||
// known prefixes and searching for the file on all parents of the
|
||||
// current working dir.
|
||||
func adjustSourcePath(path string) (*os.File, string, error) {
|
||||
|
@ -85,12 +85,12 @@ Flag: -copylocks
|
||||
|
||||
Locks that are erroneously passed by value.
|
||||
|
||||
Documentation examples
|
||||
Tests, benchmarks and documentation examples
|
||||
|
||||
Flag: -example
|
||||
Flag: -tests
|
||||
|
||||
Mistakes involving example tests, including examples with incorrect names or
|
||||
function signatures, or that document identifiers not in the package.
|
||||
Mistakes involving tests including functions with incorrect names or signatures
|
||||
and example tests that document identifiers not in the package.
|
||||
|
||||
Methods
|
||||
|
||||
|
@ -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
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
// Test of examples.
|
||||
|
||||
package testdata
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Buf is a ...
|
||||
type Buf []byte
|
||||
|
||||
@ -46,3 +48,27 @@ func ExamplePuffer() // ERROR "ExamplePuffer refers to unknown identifier: Puffe
|
||||
func ExamplePuffer_Append() // ERROR "ExamplePuffer_Append refers to unknown identifier: Puffer"
|
||||
|
||||
func ExamplePuffer_suffix() // ERROR "ExamplePuffer_suffix refers to unknown identifier: Puffer"
|
||||
|
||||
func nonTest() {} // OK because it doesn't start with "Test".
|
||||
|
||||
func (Buf) TesthasReceiver() {} // OK because it has a receiver.
|
||||
|
||||
func TestOKSuffix(*testing.T) {} // OK because first char after "Test" is Uppercase.
|
||||
|
||||
func TestÜnicodeWorks(*testing.T) {} // OK because the first char after "Test" is Uppercase.
|
||||
|
||||
func TestbadSuffix(*testing.T) {} // ERROR "first letter after 'Test' must not be lowercase"
|
||||
|
||||
func TestemptyImportBadSuffix(*T) {} // ERROR "first letter after 'Test' must not be lowercase"
|
||||
|
||||
func Test(*testing.T) {} // OK "Test" on its own is considered a test.
|
||||
|
||||
func Testify() {} // OK because it takes no parameters.
|
||||
|
||||
func TesttooManyParams(*testing.T, string) {} // OK because it takes too many parameters.
|
||||
|
||||
func TesttooManyNames(a, b *testing.T) {} // OK because it takes too many names.
|
||||
|
||||
func TestnoTParam(string) {} // OK because it doesn't take a *testing.T
|
||||
|
||||
func BenchmarkbadSuffix(*testing.B) {} // ERROR "first letter after 'Benchmark' must not be lowercase"
|
182
src/cmd/vet/tests.go
Normal file
182
src/cmd/vet/tests.go
Normal 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)
|
||||
}
|
||||
}
|
@ -130,7 +130,7 @@ func testNonceSafety(t *testing.T, c elliptic.Curve, tag string) {
|
||||
}
|
||||
|
||||
if r0.Cmp(r1) == 0 {
|
||||
t.Errorf("%s: the nonce used for two diferent messages was the same", tag)
|
||||
t.Errorf("%s: the nonce used for two different messages was the same", tag)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ loop:
|
||||
BEQ aligned // aligned detected - skip copy
|
||||
|
||||
// Copy the unaligned source data into the aligned temporary buffer
|
||||
// memove(to=4(R13), from=8(R13), n=12(R13)) - Corrupts all registers
|
||||
// memmove(to=4(R13), from=8(R13), n=12(R13)) - Corrupts all registers
|
||||
MOVW $buf, Rtable // to
|
||||
MOVW $64, Rc0 // n
|
||||
MOVM.IB [Rtable,Rdata,Rc0], (R13)
|
||||
|
@ -11,8 +11,9 @@ import "io"
|
||||
// Reader is a global, shared instance of a cryptographically
|
||||
// strong pseudo-random generator.
|
||||
//
|
||||
// On Unix-like systems, Reader reads from /dev/urandom.
|
||||
// On Linux, Reader uses getrandom(2) if available, /dev/urandom otherwise.
|
||||
// On OpenBSD, Reader uses getentropy(2).
|
||||
// On other Unix-like systems, Reader reads from /dev/urandom.
|
||||
// On Windows systems, Reader uses the CryptGenRandom API.
|
||||
var Reader io.Reader
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user