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