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

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

18
.github/ISSUE_TEMPLATE vendored Normal file
View File

@ -0,0 +1,18 @@
Please answer these questions before submitting your issue. Thanks!
1. What version of Go are you using (`go version`)?
2. What operating system and processor architecture are you using (`go env`)?
3. What did you do?
(Use play.golang.org to provide a runnable example, if possible.)
4. What did you expect to see?
5. What did you see instead?

7
.github/PULL_REQUEST_TEMPLATE vendored Normal file
View File

@ -0,0 +1,7 @@
Please do not send pull requests to the golang/* repositories.
We do, however, take contributions gladly.
See https://golang.org/doc/contribute.html
Thanks!

1
.gitignore vendored
View File

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

View File

@ -1,14 +1,20 @@
Tools:
cmd/dist: add list subcommand to list all supported platforms (CL 19837)
cmd/go: GO15VENDOREXPERIMENT gone, assumed on (CL 19615)
cmd/link: "-X name value" form gone (CL 19614)
cmd/compile: smaller binaries (many CLs)
cmd/go, go/build: add support for Fortran (CL 19670, CL 4114)
cmd/dist: introduce list subcommand to list all supported platforms (CL 19837)
Ports:
SOMETHING WILL HAPPEN
We now require OpenBSD 5.6+ (CL 18219, crypto/rand using getentropy)
plan9/arm support? Start at least.
API additions and behavior changes:
SOMETHING WILL HAPPEN
runtime: add CallerFrames and Frames (CL 19869)
testing/quick: now generates nil values (CL 16470)
net/url: support query string without values (CL 19931)
net/textproto: permit all valid token chars in CanonicalMIMEHeaderKey input (CL 18725)

View File

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

View File

@ -122,6 +122,16 @@ var ptrTests = []ptrTest{
body: `i := 0; p := &S{p:&i}; s := p.a[:]; C.f(unsafe.Pointer(&s[0]))`,
fail: false,
},
{
// Passing the address of a slice of an array that is
// an element in a struct, with a type conversion.
name: "slice-ok-4",
c: `typedef void* PV; void f(PV p) {}`,
imports: []string{"unsafe"},
support: `type S struct { p *int; a [4]byte }`,
body: `i := 0; p := &S{p:&i}; C.f(C.PV(unsafe.Pointer(&p.a[0])))`,
fail: false,
},
{
// Passing the address of a static variable with no
// pointers doesn't matter.

View File

@ -0,0 +1,9 @@
! Copyright 2016 The Go Authors. All rights reserved.
! Use of this source code is governed by a BSD-style
! license that can be found in the LICENSE file.
function the_answer() result(j) bind(C)
use iso_c_binding, only: c_int
integer(c_int) :: j ! output
j = 42
end function the_answer

View File

@ -0,0 +1,12 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fortran
// int the_answer();
import "C"
func TheAnswer() int {
return int(C.the_answer())
}

View File

@ -0,0 +1,13 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fortran
import "testing"
func TestFortran(t *testing.T) {
if a := TheAnswer(); a != 42 {
t.Errorf("Unexpected result for The Answer. Got: %d Want: 42", a)
}
}

View File

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

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

@ -0,0 +1,22 @@
#!/usr/bin/env bash
# Copyright 2016 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
# This directory is intended to test the use of Fortran with cgo.
set -e
FC=$1
if ! $FC helloworld/helloworld.f90 -o main.exe >& /dev/null; then
echo "skipping Fortran test: could not build helloworld.f90 with $FC"
exit 0
fi
if ! go test; then
echo "FAIL: go test"
status=1
fi
exit $status

View File

@ -10,7 +10,7 @@ set -e
if [ ! -f src/libgo/libgo.go ]; then
cwd=$(pwd)
echo 'misc/cgo/testcshared/test.bash is running in $cwd' 1>&2
echo "misc/cgo/testcshared/test.bash is running in $cwd" 1>&2
exit 1
fi

View File

@ -10,7 +10,7 @@
#
# This script does not handle file names that contain spaces.
gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$')
gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '\.go$')
[ -z "$gofiles" ] && exit 0
unformatted=$(gofmt -l $gofiles)

View File

@ -705,7 +705,7 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
}
}
if err == io.EOF {
// If we filled the buffer exactly, flush pre-emptively.
// If we filled the buffer exactly, flush preemptively.
if b.Available() == 0 {
err = b.flush()
} else {

View File

@ -351,7 +351,7 @@ func TestSplitError(t *testing.T) {
// Test that an EOF is overridden by a user-generated scan error.
func TestErrAtEOF(t *testing.T) {
s := NewScanner(strings.NewReader("1 2 33"))
// This spitter will fail on last entry, after s.err==EOF.
// This splitter will fail on last entry, after s.err==EOF.
split := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
advance, token, err = ScanWords(data, atEOF)
if len(token) > 1 {

View File

@ -32,13 +32,13 @@ if [ "$pattern" = "" ]; then
pattern=.
fi
# put linux, nacl first in the target list to get all the architectures up front.
targets="$((ls runtime | sed -n 's/^rt0_\(.*\)_\(.*\)\.s/\1-\2/p'; echo linux-386-387 linux-arm-arm5) | sort | sed -e 's|linux-mips64x|linux-mips64 linux-mips64le|' | egrep -v android-arm | egrep "$pattern" | egrep 'linux|nacl')
$(ls runtime | sed -n 's/^rt0_\(.*\)_\(.*\)\.s/\1-\2/p' | egrep -v 'android-arm|darwin-arm' | egrep "$pattern" | egrep -v 'linux|nacl')"
./make.bash || exit 1
GOROOT="$(cd .. && pwd)"
# put linux, nacl first in the target list to get all the architectures up front.
targets="$((../bin/go tool dist list | sed -n 's/^\(.*\)\/\(.*\)/\1-\2/p'; echo linux-386-387 linux-arm-arm5) | sort | egrep -v android-arm | egrep "$pattern" | egrep 'linux|nacl')
$(../bin/go tool dist list | sed -n 's/^\(.*\)\/\(.*\)/\1-\2/p' | egrep -v 'android-arm|darwin-arm' | egrep "$pattern" | egrep -v 'linux|nacl')"
failed=false
for target in $targets
do

View File

@ -335,6 +335,41 @@ func TestIndexByteBig(t *testing.T) {
}
}
// test a small index across all page offsets
func TestIndexByteSmall(t *testing.T) {
b := make([]byte, 5015) // bigger than a page
// Make sure we find the correct byte even when straddling a page.
for i := 0; i <= len(b)-15; i++ {
for j := 0; j < 15; j++ {
b[i+j] = byte(100 + j)
}
for j := 0; j < 15; j++ {
p := IndexByte(b[i:i+15], byte(100+j))
if p != j {
t.Errorf("IndexByte(%q, %d) = %d", b[i:i+15], 100+j, p)
}
}
for j := 0; j < 15; j++ {
b[i+j] = 0
}
}
// Make sure matches outside the slice never trigger.
for i := 0; i <= len(b)-15; i++ {
for j := 0; j < 15; j++ {
b[i+j] = 1
}
for j := 0; j < 15; j++ {
p := IndexByte(b[i:i+15], byte(0))
if p != -1 {
t.Errorf("IndexByte(%q, %d) = %d", b[i:i+15], 0, p)
}
}
for j := 0; j < 15; j++ {
b[i+j] = 0
}
}
}
func TestIndexRune(t *testing.T) {
for _, tt := range indexRuneTests {
a := []byte(tt.a)
@ -348,10 +383,12 @@ func TestIndexRune(t *testing.T) {
var bmbuf []byte
func BenchmarkIndexByte10(b *testing.B) { bmIndexByte(b, IndexByte, 10) }
func BenchmarkIndexByte32(b *testing.B) { bmIndexByte(b, IndexByte, 32) }
func BenchmarkIndexByte4K(b *testing.B) { bmIndexByte(b, IndexByte, 4<<10) }
func BenchmarkIndexByte4M(b *testing.B) { bmIndexByte(b, IndexByte, 4<<20) }
func BenchmarkIndexByte64M(b *testing.B) { bmIndexByte(b, IndexByte, 64<<20) }
func BenchmarkIndexBytePortable10(b *testing.B) { bmIndexByte(b, IndexBytePortable, 10) }
func BenchmarkIndexBytePortable32(b *testing.B) { bmIndexByte(b, IndexBytePortable, 32) }
func BenchmarkIndexBytePortable4K(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<10) }
func BenchmarkIndexBytePortable4M(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<20) }

View File

@ -20,7 +20,7 @@ The GOOS and GOARCH environment variables set the desired target.
Flags:
-D value
predefined symbol with optional simple value -D=identifer=value;
predefined symbol with optional simple value -D=identifier=value;
can be set multiple times
-I value
include directory; can be set multiple times

View File

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

View File

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

View File

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

View File

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

View File

@ -29,7 +29,7 @@ var (
)
func init() {
flag.Var(&D, "D", "predefined symbol with optional simple value -D=identifer=value; can be set multiple times")
flag.Var(&D, "D", "predefined symbol with optional simple value -D=identifier=value; can be set multiple times")
flag.Var(&I, "I", "include directory; can be set multiple times")
}

View File

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

View File

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

View File

@ -26,7 +26,7 @@ func main() {
architecture := arch.Set(GOARCH)
if architecture == nil {
log.Fatalf("asm: unrecognized architecture %s", GOARCH)
log.Fatalf("unrecognized architecture %s", GOARCH)
}
flags.Parse()
@ -66,7 +66,7 @@ func main() {
obj.Writeobjdirect(ctxt, output)
}
if !ok || diag {
log.Printf("asm: assembly of %s failed", flag.Arg(0))
log.Printf("assembly of %s failed", flag.Arg(0))
os.Remove(*flags.OutputFile)
os.Exit(1)
}

View File

@ -31,9 +31,9 @@ See $GOROOT/misc/cgo/stdio and $GOROOT/misc/cgo/gmp for examples. See
"C? Go? Cgo!" for an introduction to using cgo:
https://golang.org/doc/articles/c_go_cgo.html.
CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS may be defined with pseudo #cgo
directives within these comments to tweak the behavior of the C or C++
compiler. Values defined in multiple directives are concatenated
CFLAGS, CPPFLAGS, CXXFLAGS, FFLAGS and LDFLAGS may be defined with pseudo
#cgo directives within these comments to tweak the behavior of the C, C++
or Fortran compiler. Values defined in multiple directives are concatenated
together. The directive can include a list of build constraints limiting its
effect to systems satisfying one of the constraints
(see https://golang.org/pkg/go/build/#hdr-Build_Constraints for details about the constraint syntax).
@ -53,7 +53,7 @@ For example:
// #include <png.h>
import "C"
When building, the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS and
When building, the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS and
CGO_LDFLAGS environment variables are added to the flags derived from
these directives. Package-specific flags should be set using the
directives, not the environment variables, so that builds work in
@ -62,10 +62,11 @@ unmodified environments.
All the cgo CPPFLAGS and CFLAGS directives in a package are concatenated and
used to compile C files in that package. All the CPPFLAGS and CXXFLAGS
directives in a package are concatenated and used to compile C++ files in that
package. All the LDFLAGS directives in any package in the program are
concatenated and used at link time. All the pkg-config directives are
concatenated and sent to pkg-config simultaneously to add to each appropriate
set of command-line flags.
package. All the CPPFLAGS and FFLAGS directives in a package are concatenated
and used to compile Fortran files in that package. All the LDFLAGS directives
in any package in the program are concatenated and used at link time. All the
pkg-config directives are concatenated and sent to pkg-config simultaneously
to add to each appropriate set of command-line flags.
When the cgo directives are parsed, any occurrence of the string ${SRCDIR}
will be replaced by the absolute path to the directory containing the source
@ -83,7 +84,8 @@ When the Go tool sees that one or more Go files use the special import
"C", it will look for other non-Go files in the directory and compile
them as part of the Go package. Any .c, .s, or .S files will be
compiled with the C compiler. Any .cc, .cpp, or .cxx files will be
compiled with the C++ compiler. Any .h, .hh, .hpp, or .hxx files will
compiled with the C++ compiler. Any .f, .F, .for or .f90 files will be
compiled with the fortran compiler. Any .h, .hh, .hpp, or .hxx files will
not be compiled separately, but, if these header files are changed,
the C and C++ files will be recompiled. The default C and C++
compilers may be changed by the CC and CXX environment variables,

View File

@ -819,14 +819,17 @@ func (p *Package) hasSideEffects(f *File, x ast.Expr) bool {
func (p *Package) isType(t ast.Expr) bool {
switch t := t.(type) {
case *ast.SelectorExpr:
if t.Sel.Name != "Pointer" {
return false
}
id, ok := t.X.(*ast.Ident)
if !ok {
return false
}
return id.Name == "unsafe"
if id.Name == "unsafe" && t.Sel.Name == "Pointer" {
return true
}
if id.Name == "C" && typedef["_Ctype_"+t.Sel.Name] != nil {
return true
}
return false
case *ast.Ident:
// TODO: This ignores shadowing.
switch t.Name {

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,626 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gc
import "fmt"
const (
// These values are known by runtime.
ANOEQ = iota
AMEM0
AMEM8
AMEM16
AMEM32
AMEM64
AMEM128
ASTRING
AINTER
ANILINTER
AFLOAT32
AFLOAT64
ACPLX64
ACPLX128
AMEM = 100
)
func algtype(t *Type) int {
a := algtype1(t, nil)
if a == AMEM {
switch t.Width {
case 0:
return AMEM0
case 1:
return AMEM8
case 2:
return AMEM16
case 4:
return AMEM32
case 8:
return AMEM64
case 16:
return AMEM128
}
}
return a
}
func algtype1(t *Type, bad **Type) int {
if bad != nil {
*bad = nil
}
if t.Broke {
return AMEM
}
if t.Noalg {
return ANOEQ
}
switch t.Etype {
// will be defined later.
case TANY, TFORW:
*bad = t
return -1
case TINT8,
TUINT8,
TINT16,
TUINT16,
TINT32,
TUINT32,
TINT64,
TUINT64,
TINT,
TUINT,
TUINTPTR,
TBOOL,
TPTR32,
TPTR64,
TCHAN,
TUNSAFEPTR:
return AMEM
case TFUNC, TMAP:
if bad != nil {
*bad = t
}
return ANOEQ
case TFLOAT32:
return AFLOAT32
case TFLOAT64:
return AFLOAT64
case TCOMPLEX64:
return ACPLX64
case TCOMPLEX128:
return ACPLX128
case TSTRING:
return ASTRING
case TINTER:
if isnilinter(t) {
return ANILINTER
}
return AINTER
case TARRAY:
if Isslice(t) {
if bad != nil {
*bad = t
}
return ANOEQ
}
a := algtype1(t.Type, bad)
if a == ANOEQ || a == AMEM {
if a == ANOEQ && bad != nil {
*bad = t
}
return a
}
switch t.Bound {
case 0:
// We checked above that the element type is comparable.
return AMEM
case 1:
// Single-element array is same as its lone element.
return a
}
return -1 // needs special compare
case TSTRUCT:
if t.Type != nil && t.Type.Down == nil && !isblanksym(t.Type.Sym) {
// One-field struct is same as that one field alone.
return algtype1(t.Type.Type, bad)
}
ret := AMEM
var a int
for t1 := t.Type; t1 != nil; t1 = t1.Down {
// All fields must be comparable.
a = algtype1(t1.Type, bad)
if a == ANOEQ {
return ANOEQ
}
// Blank fields, padded fields, fields with non-memory
// equality need special compare.
if a != AMEM || isblanksym(t1.Sym) || ispaddedfield(t1, t.Width) {
ret = -1
continue
}
}
return ret
}
Fatalf("algtype1: unexpected type %v", t)
return 0
}
// Generate a helper function to compute the hash of a value of type t.
func genhash(sym *Sym, t *Type) {
if Debug['r'] != 0 {
fmt.Printf("genhash %v %v\n", sym, t)
}
lineno = 1 // less confusing than end of input
dclcontext = PEXTERN
markdcl()
// func sym(p *T, h uintptr) uintptr
fn := Nod(ODCLFUNC, nil, nil)
fn.Func.Nname = newname(sym)
fn.Func.Nname.Class = PFUNC
tfn := Nod(OTFUNC, nil, nil)
fn.Func.Nname.Name.Param.Ntype = tfn
n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
tfn.List = list(tfn.List, n)
np := n.Left
n = Nod(ODCLFIELD, newname(Lookup("h")), typenod(Types[TUINTPTR]))
tfn.List = list(tfn.List, n)
nh := n.Left
n = Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])) // return value
tfn.Rlist = list(tfn.Rlist, n)
funchdr(fn)
typecheck(&fn.Func.Nname.Name.Param.Ntype, Etype)
// genhash is only called for types that have equality but
// cannot be handled by the standard algorithms,
// so t must be either an array or a struct.
switch t.Etype {
default:
Fatalf("genhash %v", t)
case TARRAY:
if Isslice(t) {
Fatalf("genhash %v", t)
}
// An array of pure memory would be handled by the
// standard algorithm, so the element type must not be
// pure memory.
hashel := hashfor(t.Type)
n := Nod(ORANGE, nil, Nod(OIND, np, nil))
ni := newname(Lookup("i"))
ni.Type = Types[TINT]
n.List = list1(ni)
n.Colas = true
colasdefn(n.List, n)
ni = n.List.N
// h = hashel(&p[i], h)
call := Nod(OCALL, hashel, nil)
nx := Nod(OINDEX, np, ni)
nx.Bounded = true
na := Nod(OADDR, nx, nil)
na.Etype = 1 // no escape to heap
call.List = list(call.List, na)
call.List = list(call.List, nh)
n.Nbody = list(n.Nbody, Nod(OAS, nh, call))
fn.Nbody = list(fn.Nbody, n)
// Walk the struct using memhash for runs of AMEM
// and calling specific hash functions for the others.
case TSTRUCT:
var call *Node
var nx *Node
var na *Node
var hashel *Node
t1 := t.Type
for {
first, size, next := memrun(t, t1)
t1 = next
// Run memhash for fields up to this one.
if first != nil {
hashel = hashmem(first.Type)
// h = hashel(&p.first, size, h)
call = Nod(OCALL, hashel, nil)
nx = Nod(OXDOT, np, newname(first.Sym)) // TODO: fields from other packages?
na = Nod(OADDR, nx, nil)
na.Etype = 1 // no escape to heap
call.List = list(call.List, na)
call.List = list(call.List, nh)
call.List = list(call.List, Nodintconst(size))
fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call))
}
if t1 == nil {
break
}
if isblanksym(t1.Sym) {
t1 = t1.Down
continue
}
if algtype1(t1.Type, nil) == AMEM {
// Our memory run might have been stopped by padding or a blank field.
// If the next field is memory-ish, it could be the start of a new run.
continue
}
hashel = hashfor(t1.Type)
call = Nod(OCALL, hashel, nil)
nx = Nod(OXDOT, np, newname(t1.Sym)) // TODO: fields from other packages?
na = Nod(OADDR, nx, nil)
na.Etype = 1 // no escape to heap
call.List = list(call.List, na)
call.List = list(call.List, nh)
fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call))
t1 = t1.Down
}
}
r := Nod(ORETURN, nil, nil)
r.List = list(r.List, nh)
fn.Nbody = list(fn.Nbody, r)
if Debug['r'] != 0 {
dumplist("genhash body", fn.Nbody)
}
funcbody(fn)
Curfn = fn
fn.Func.Dupok = true
typecheck(&fn, Etop)
typechecklist(fn.Nbody, Etop)
Curfn = nil
// Disable safemode while compiling this code: the code we
// generate internally can refer to unsafe.Pointer.
// In this case it can happen if we need to generate an ==
// for a struct containing a reflect.Value, which itself has
// an unexported field of type unsafe.Pointer.
old_safemode := safemode
safemode = 0
funccompile(fn)
safemode = old_safemode
}
func hashfor(t *Type) *Node {
var sym *Sym
a := algtype1(t, nil)
switch a {
case AMEM:
Fatalf("hashfor with AMEM type")
case AINTER:
sym = Pkglookup("interhash", Runtimepkg)
case ANILINTER:
sym = Pkglookup("nilinterhash", Runtimepkg)
case ASTRING:
sym = Pkglookup("strhash", Runtimepkg)
case AFLOAT32:
sym = Pkglookup("f32hash", Runtimepkg)
case AFLOAT64:
sym = Pkglookup("f64hash", Runtimepkg)
case ACPLX64:
sym = Pkglookup("c64hash", Runtimepkg)
case ACPLX128:
sym = Pkglookup("c128hash", Runtimepkg)
default:
sym = typesymprefix(".hash", t)
}
n := newname(sym)
n.Class = PFUNC
tfn := Nod(OTFUNC, nil, nil)
tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
tfn.Rlist = list(tfn.Rlist, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
typecheck(&tfn, Etype)
n.Type = tfn.Type
return n
}
// geneq generates a helper function to
// check equality of two values of type t.
func geneq(sym *Sym, t *Type) {
if Debug['r'] != 0 {
fmt.Printf("geneq %v %v\n", sym, t)
}
lineno = 1 // less confusing than end of input
dclcontext = PEXTERN
markdcl()
// func sym(p, q *T) bool
fn := Nod(ODCLFUNC, nil, nil)
fn.Func.Nname = newname(sym)
fn.Func.Nname.Class = PFUNC
tfn := Nod(OTFUNC, nil, nil)
fn.Func.Nname.Name.Param.Ntype = tfn
n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
tfn.List = list(tfn.List, n)
np := n.Left
n = Nod(ODCLFIELD, newname(Lookup("q")), typenod(Ptrto(t)))
tfn.List = list(tfn.List, n)
nq := n.Left
n = Nod(ODCLFIELD, nil, typenod(Types[TBOOL]))
tfn.Rlist = list(tfn.Rlist, n)
funchdr(fn)
// geneq is only called for types that have equality but
// cannot be handled by the standard algorithms,
// so t must be either an array or a struct.
switch t.Etype {
default:
Fatalf("geneq %v", t)
case TARRAY:
if Isslice(t) {
Fatalf("geneq %v", t)
}
// An array of pure memory would be handled by the
// standard memequal, so the element type must not be
// pure memory. Even if we unrolled the range loop,
// each iteration would be a function call, so don't bother
// unrolling.
nrange := Nod(ORANGE, nil, Nod(OIND, np, nil))
ni := newname(Lookup("i"))
ni.Type = Types[TINT]
nrange.List = list1(ni)
nrange.Colas = true
colasdefn(nrange.List, nrange)
ni = nrange.List.N
// if p[i] != q[i] { return false }
nx := Nod(OINDEX, np, ni)
nx.Bounded = true
ny := Nod(OINDEX, nq, ni)
ny.Bounded = true
nif := Nod(OIF, nil, nil)
nif.Left = Nod(ONE, nx, ny)
r := Nod(ORETURN, nil, nil)
r.List = list(r.List, Nodbool(false))
nif.Nbody = list(nif.Nbody, r)
nrange.Nbody = list(nrange.Nbody, nif)
fn.Nbody = list(fn.Nbody, nrange)
// return true
ret := Nod(ORETURN, nil, nil)
ret.List = list(ret.List, Nodbool(true))
fn.Nbody = list(fn.Nbody, ret)
// Walk the struct using memequal for runs of AMEM
// and calling specific equality tests for the others.
// Skip blank-named fields.
case TSTRUCT:
var conjuncts []*Node
t1 := t.Type
for {
first, size, next := memrun(t, t1)
t1 = next
// Run memequal for fields up to this one.
// TODO(rsc): All the calls to newname are wrong for
// cross-package unexported fields.
if first != nil {
if first.Down == t1 {
conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym)))
} else if first.Down.Down == t1 {
conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym)))
first = first.Down
if !isblanksym(first.Sym) {
conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym)))
}
} else {
// More than two fields: use memequal.
conjuncts = append(conjuncts, eqmem(np, nq, newname(first.Sym), size))
}
}
if t1 == nil {
break
}
if isblanksym(t1.Sym) {
t1 = t1.Down
continue
}
if algtype1(t1.Type, nil) == AMEM {
// Our memory run might have been stopped by padding or a blank field.
// If the next field is memory-ish, it could be the start of a new run.
continue
}
// Check this field, which is not just memory.
conjuncts = append(conjuncts, eqfield(np, nq, newname(t1.Sym)))
t1 = t1.Down
}
var and *Node
switch len(conjuncts) {
case 0:
and = Nodbool(true)
case 1:
and = conjuncts[0]
default:
and = Nod(OANDAND, conjuncts[0], conjuncts[1])
for _, conjunct := range conjuncts[2:] {
and = Nod(OANDAND, and, conjunct)
}
}
ret := Nod(ORETURN, nil, nil)
ret.List = list(ret.List, and)
fn.Nbody = list(fn.Nbody, ret)
}
if Debug['r'] != 0 {
dumplist("geneq body", fn.Nbody)
}
funcbody(fn)
Curfn = fn
fn.Func.Dupok = true
typecheck(&fn, Etop)
typechecklist(fn.Nbody, Etop)
Curfn = nil
// Disable safemode while compiling this code: the code we
// generate internally can refer to unsafe.Pointer.
// In this case it can happen if we need to generate an ==
// for a struct containing a reflect.Value, which itself has
// an unexported field of type unsafe.Pointer.
old_safemode := safemode
safemode = 0
// Disable checknils while compiling this code.
// We are comparing a struct or an array,
// neither of which can be nil, and our comparisons
// are shallow.
Disable_checknil++
funccompile(fn)
safemode = old_safemode
Disable_checknil--
}
// eqfield returns the node
// p.field == q.field
func eqfield(p *Node, q *Node, field *Node) *Node {
nx := Nod(OXDOT, p, field)
ny := Nod(OXDOT, q, field)
ne := Nod(OEQ, nx, ny)
return ne
}
// eqmem returns the node
// memequal(&p.field, &q.field [, size])
func eqmem(p *Node, q *Node, field *Node, size int64) *Node {
var needsize int
nx := Nod(OADDR, Nod(OXDOT, p, field), nil)
nx.Etype = 1 // does not escape
ny := Nod(OADDR, Nod(OXDOT, q, field), nil)
ny.Etype = 1 // does not escape
typecheck(&nx, Erv)
typecheck(&ny, Erv)
call := Nod(OCALL, eqmemfunc(size, nx.Type.Type, &needsize), nil)
call.List = list(call.List, nx)
call.List = list(call.List, ny)
if needsize != 0 {
call.List = list(call.List, Nodintconst(size))
}
return call
}
func eqmemfunc(size int64, type_ *Type, needsize *int) *Node {
var fn *Node
switch size {
default:
fn = syslook("memequal", 1)
*needsize = 1
case 1, 2, 4, 8, 16:
buf := fmt.Sprintf("memequal%d", int(size)*8)
fn = syslook(buf, 1)
*needsize = 0
}
substArgTypes(fn, type_, type_)
return fn
}
// memrun finds runs of struct fields for which memory-only algs are appropriate.
// t is the parent struct type, and field is the field at which to start.
// first is the first field in the memory run.
// size is the length in bytes of the memory included in the run.
// next is the next field after the memory run.
func memrun(t *Type, field *Type) (first *Type, size int64, next *Type) {
var offend int64
for {
if field == nil || algtype1(field.Type, nil) != AMEM || isblanksym(field.Sym) {
break
}
offend = field.Width + field.Type.Width
if first == nil {
first = field
}
// If it's a memory field but it's padded, stop here.
if ispaddedfield(field, t.Width) {
field = field.Down
break
}
field = field.Down
}
if first != nil {
size = offend - first.Width // first.Width is offset
}
return first, size, field
}
// ispaddedfield reports whether the given field
// is followed by padding. For the case where t is
// the last field, total gives the size of the enclosing struct.
func ispaddedfield(t *Type, total int64) bool {
if t.Etype != TFIELD {
Fatalf("ispaddedfield called non-field %v", t)
}
if t.Down == nil {
return t.Width+t.Type.Width != total
}
return t.Width+t.Type.Width != t.Down.Width
}

View File

@ -72,7 +72,7 @@ amount of space to hold the list without the need to grow it later.
All integer values use a variable-length encoding for compact representation.
If debugFormat is set, each integer and string value is preceeded by a marker
If debugFormat is set, each integer and string value is preceded by a marker
and position information in the encoding. This mechanism permits an importer
to recognize immediately when it is out of sync. The importer recognizes this
mode automatically (i.e., it can import export data produced with debugging

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1730,10 +1730,42 @@ func Hconv(l *NodeList, flag int) string {
return buf.String()
}
func Hconvslice(l []*Node, flag int) string {
if len(l) == 0 && fmtmode == FDbg {
return "<nil>"
}
sf := flag
sm, sb := setfmode(&flag)
sep := "; "
if fmtmode == FDbg {
sep = "\n"
} else if flag&obj.FmtComma != 0 {
sep = ", "
}
var buf bytes.Buffer
for i, n := range l {
buf.WriteString(Nconv(n, 0))
if i+1 < len(l) {
buf.WriteString(sep)
}
}
flag = sf
fmtbody = sb
fmtmode = sm
return buf.String()
}
func dumplist(s string, l *NodeList) {
fmt.Printf("%s%v\n", s, Hconv(l, obj.FmtSign))
}
func dumpslice(s string, l []*Node) {
fmt.Printf("%s%v\n", s, Hconvslice(l, obj.FmtSign))
}
func Dump(s string, n *Node) {
fmt.Printf("%s [%p]%v\n", s, n, Nconv(n, obj.FmtSign))
}

View File

@ -76,6 +76,9 @@ func addrescapes(n *Node) {
oldfn := Curfn
Curfn = n.Name.Curfn
if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE {
Curfn = Curfn.Func.Closure
}
n.Name.Heapaddr = temp(Ptrto(n.Type))
buf := fmt.Sprintf("&%v", n.Sym)
n.Name.Heapaddr.Sym = Lookup(buf)
@ -218,6 +221,12 @@ func Genlist(l *NodeList) {
}
}
func Genslice(l []*Node) {
for _, n := range l {
gen(n)
}
}
// generate code to start new proc running call n.
func cgen_proc(n *Node, proc int) {
switch n.Left.Op {
@ -587,6 +596,10 @@ func Tempname(nn *Node, t *Type) {
if Curfn == nil {
Fatalf("no curfn for tempname")
}
if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE {
Dump("Tempname", Curfn)
Fatalf("adding tempname to wrong closure function")
}
if t == nil {
Yyerror("tempname called with nil type")
@ -606,7 +619,7 @@ func Tempname(nn *Node, t *Type) {
n.Ullman = 1
n.Esc = EscNever
n.Name.Curfn = Curfn
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
dowidth(t)
n.Xoffset = 0

View File

@ -11,7 +11,8 @@ import (
"log"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
"testing"
)
@ -28,7 +29,7 @@ func TestScanfRemoval(t *testing.T) {
defer os.RemoveAll(dir)
// Create source.
src := path.Join(dir, "test.go")
src := filepath.Join(dir, "test.go")
f, err := os.Create(src)
if err != nil {
log.Fatalf("could not create source file: %v", err)
@ -43,7 +44,7 @@ func main() {
f.Close()
// Name of destination.
dst := path.Join(dir, "test")
dst := filepath.Join(dir, "test")
// Compile source.
cmd := exec.Command("go", "build", "-o", dst, src)
@ -62,3 +63,53 @@ func main() {
log.Fatalf("scanf code not removed from helloworld")
}
}
// Make sure -S prints assembly code. See issue 14515.
func TestDashS(t *testing.T) {
testenv.MustHaveGoBuild(t)
// Make a directory to work in.
dir, err := ioutil.TempDir("", "issue14515-")
if err != nil {
log.Fatalf("could not create directory: %v", err)
}
defer os.RemoveAll(dir)
// Create source.
src := filepath.Join(dir, "test.go")
f, err := os.Create(src)
if err != nil {
log.Fatalf("could not create source file: %v", err)
}
f.Write([]byte(`
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
`))
f.Close()
// Compile source.
cmd := exec.Command("go", "build", "-gcflags", "-S", "-o", filepath.Join(dir, "test"), src)
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("could not build target: %v", err)
}
patterns := []string{
// It is hard to look for actual instructions in an
// arch-independent way. So we'll just look for
// pseudo-ops that are arch-independent.
"\tTEXT\t",
"\tFUNCDATA\t",
"\tPCDATA\t",
}
outstr := string(out)
for _, p := range patterns {
if !strings.Contains(outstr, p) {
println(outstr)
panic("can't find pattern " + p)
}
}
}

View File

@ -26,25 +26,6 @@ const (
MaxStackVarSize = 10 * 1024 * 1024
)
const (
// These values are known by runtime.
ANOEQ = iota
AMEM0
AMEM8
AMEM16
AMEM32
AMEM64
AMEM128
ASTRING
AINTER
ANILINTER
AFLOAT32
AFLOAT64
ACPLX64
ACPLX128
AMEM = 100
)
const (
// Maximum size in bits for Mpints before signalling
// overflow and also mantissa precision for Mpflts.
@ -122,10 +103,9 @@ type Pkg struct {
}
type Sym struct {
Lexical uint16
Flags uint8
Link *Sym
Uniqgen uint32
Link *Sym
Importdef *Pkg // where imported definition was found
Linkname string // link name
@ -425,12 +405,13 @@ var sizeof_String int // runtime sizeof(String)
var dotlist [10]Dlist // size is max depth of embeddeds
// lexlineno is the line number _after_ the most recently read rune.
// In particular, it's advanced (or rewound) as newlines are read (or unread).
var lexlineno int32
// lineno is the line number at the start of the most recently lexed token.
var lineno int32
var prevlineno int32
var pragcgobuf string
var infile string
@ -616,23 +597,10 @@ var flag_largemodel int
// when the race detector is enabled.
var instrumenting bool
// Pending annotations for next func declaration.
var (
noescape bool
noinline bool
norace bool
nosplit bool
nowritebarrier bool
nowritebarrierrec bool
systemstack bool
)
var debuglive int
var Ctxt *obj.Link
var nointerface bool
var writearchive int
var bstdout obj.Biobuf

View File

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

View File

@ -107,7 +107,7 @@ func caninl(fn *Node) {
}
// If marked "go:noinline", don't inline
if fn.Func.Noinline {
if fn.Func.Pragma&Noinline != 0 {
return
}
@ -150,7 +150,10 @@ func caninl(fn *Node) {
fn.Func.Nname.Func.Inl = fn.Nbody
fn.Nbody = inlcopylist(fn.Func.Nname.Func.Inl)
fn.Func.Nname.Func.Inldcl = inlcopylist(fn.Func.Nname.Name.Defn.Func.Dcl)
inldcl := inlcopyslice(fn.Func.Nname.Name.Defn.Func.Dcl)
if len(inldcl) > 0 {
fn.Func.Nname.Func.Inldcl = &inldcl
}
fn.Func.Nname.Func.InlCost = int32(maxBudget - budget)
// hack, TODO, check for better way to link method nodes back to the thing with the ->inl
@ -275,6 +278,18 @@ func inlcopy(n *Node) *Node {
return m
}
// Inlcopyslice is like inlcopylist, but for a slice.
func inlcopyslice(ll []*Node) []*Node {
r := make([]*Node, 0, len(ll))
for _, ln := range ll {
c := inlcopy(ln)
if c != nil {
r = append(r, c)
}
}
return r
}
// Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
// calls made to inlineable functions. This is the external entry point.
func inlcalls(fn *Node) {
@ -556,10 +571,14 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) {
//dumplist("ninit pre", ninit);
var dcl *NodeList
if fn.Name.Defn != nil { // local function
dcl = fn.Func.Inldcl // imported function
var dcl []*Node
if fn.Name.Defn != nil {
// local function
if fn.Func.Inldcl != nil {
dcl = *fn.Func.Inldcl
}
} else {
// imported function
dcl = fn.Func.Dcl
}
@ -567,18 +586,18 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) {
i := 0
// Make temp names to use instead of the originals
for ll := dcl; ll != nil; ll = ll.Next {
if ll.N.Class == PPARAMOUT { // return values handled below.
for _, ln := range dcl {
if ln.Class == PPARAMOUT { // return values handled below.
continue
}
if ll.N.Op == ONAME {
ll.N.Name.Inlvar = inlvar(ll.N)
if ln.Op == ONAME {
ln.Name.Inlvar = inlvar(ln)
// Typecheck because inlvar is not necessarily a function parameter.
typecheck(&ll.N.Name.Inlvar, Erv)
typecheck(&ln.Name.Inlvar, Erv)
if ll.N.Class&^PHEAP != PAUTO {
ninit = list(ninit, Nod(ODCL, ll.N.Name.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs
if ln.Class&^PHEAP != PAUTO {
ninit = list(ninit, Nod(ODCL, ln.Name.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs
}
}
}
@ -852,7 +871,7 @@ func inlvar(var_ *Node) *Node {
addrescapes(n)
}
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
return n
}
@ -863,7 +882,7 @@ func retvar(t *Type, i int) *Node {
n.Class = PAUTO
n.Used = true
n.Name.Curfn = Curfn // the calling function, not the called one
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
return n
}
@ -875,7 +894,7 @@ func argvar(t *Type, i int) *Node {
n.Class = PAUTO
n.Used = true
n.Name.Curfn = Curfn // the calling function, not the called one
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
return n
}

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -387,7 +387,7 @@ func ordercall(n *Node, order *Order) {
// Ordermapassign appends n to order->out, introducing temporaries
// to make sure that all map assignments have the form m[k] = x,
// where x is adressable.
// where x is addressable.
// (Orderexpr has already been called on n, so we know k is addressable.)
//
// If n is m[k] = x where x is not addressable, the rewrite is:
@ -1138,7 +1138,7 @@ func orderexpr(np **Node, order *Order, lhs *Node) {
}
case OCLOSURE:
if n.Noescape && n.Func.Cvars != nil {
if n.Noescape && len(n.Func.Cvars.Slice()) > 0 {
prealloc[n] = ordertemp(Types[TUINT8], order, false) // walk will fill in correct type
}

View File

@ -102,6 +102,8 @@ func (p *parser) syntax_error(msg string) {
}
case LASOP:
tok = goopnames[p.op] + "="
case LINCOP:
tok = goopnames[p.op] + goopnames[p.op]
default:
tok = tokstring(p.tok)
}
@ -110,11 +112,11 @@ func (p *parser) syntax_error(msg string) {
}
// Like syntax_error, but reports error at given line rather than current lexer line.
func (p *parser) syntax_error_at(lineno int32, msg string) {
defer func(lineno int32) {
lexlineno = lineno
}(lexlineno)
lexlineno = lineno
func (p *parser) syntax_error_at(lno int32, msg string) {
defer func(lno int32) {
lineno = lno
}(lineno)
lineno = lno
p.syntax_error(msg)
}
@ -219,12 +221,11 @@ var tokstrings = map[int32]string{
LANDAND: "&&",
LANDNOT: "&^",
LCOMM: "<-",
LDEC: "--",
LEQ: "==",
LGE: ">=",
LGT: ">",
LIGNORE: "LIGNORE", // we should never see this one
LINC: "++",
LINCOP: "opop",
LLE: "<=",
LLSH: "<<",
LLT: "<",
@ -280,13 +281,11 @@ func (p *parser) package_() {
defer p.trace("package_")()
}
if p.got(LPACKAGE) {
mkpackage(p.sym().Name)
} else {
prevlineno = lineno // see issue #13267
if !p.got(LPACKAGE) {
p.syntax_error("package statement must be first")
errorexit()
}
mkpackage(p.sym().Name)
}
// ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
@ -335,20 +334,22 @@ func (p *parser) importdcl() {
}
line := int32(parserline())
path := p.val
p.next()
importfile(&path, p.indent)
if importpkg == nil {
// We need to clear importpkg before calling p.next(),
// otherwise it will affect lexlineno.
// TODO(mdempsky): Fix this clumsy API.
importfile(&p.val, p.indent)
ipkg := importpkg
importpkg = nil
p.next()
if ipkg == nil {
if nerrors == 0 {
Fatalf("phase error in import")
}
return
}
ipkg := importpkg
importpkg = nil
ipkg.Direct = true
if my == nil {
@ -562,22 +563,13 @@ func (p *parser) simple_stmt(labelOk, rangeOk bool) *Node {
stmt.Etype = EType(op) // rathole to pass opcode
return stmt
case LINC:
// expr LINC
case LINCOP:
// expr LINCOP
p.next()
stmt := Nod(OASOP, lhs, Nodintconst(1))
stmt.Implicit = true
stmt.Etype = EType(OADD)
return stmt
case LDEC:
// expr LDEC
p.next()
stmt := Nod(OASOP, lhs, Nodintconst(1))
stmt.Implicit = true
stmt.Etype = EType(OSUB)
stmt.Etype = EType(p.op)
return stmt
case ':':
@ -689,7 +681,7 @@ func (p *parser) labeled_stmt(label *Node) *Node {
ls = p.stmt()
if ls == missing_stmt {
// report error at line of ':' token
p.syntax_error_at(prevlineno, "missing statement after label")
p.syntax_error_at(label.Lineno, "missing statement after label")
// we are already at the end of the labeled statement - no need to advance
return missing_stmt
}
@ -1104,55 +1096,18 @@ func (p *parser) select_stmt() *Node {
return hdr
}
// TODO(gri) should have lexer return this info - no need for separate lookup
// (issue 13244)
var prectab = map[int32]struct {
prec int // > 0 (0 indicates not found)
op Op
}{
// not an expression anymore, but left in so we can give a good error
// message when used in expression context
LCOMM: {1, OSEND},
LOROR: {2, OOROR},
LANDAND: {3, OANDAND},
LEQ: {4, OEQ},
LNE: {4, ONE},
LLE: {4, OLE},
LGE: {4, OGE},
LLT: {4, OLT},
LGT: {4, OGT},
'+': {5, OADD},
'-': {5, OSUB},
'|': {5, OOR},
'^': {5, OXOR},
'*': {6, OMUL},
'/': {6, ODIV},
'%': {6, OMOD},
'&': {6, OAND},
LLSH: {6, OLSH},
LRSH: {6, ORSH},
LANDNOT: {6, OANDNOT},
}
// Expression = UnaryExpr | Expression binary_op Expression .
func (p *parser) bexpr(prec int) *Node {
func (p *parser) bexpr(prec OpPrec) *Node {
// don't trace bexpr - only leads to overly nested trace output
// prec is precedence of the prior/enclosing binary operator (if any),
// so we only want to parse tokens of greater precedence.
x := p.uexpr()
t := prectab[p.tok]
for tprec := t.prec; tprec >= prec; tprec-- {
for tprec == prec {
for p.prec > prec {
op, prec1 := p.op, p.prec
p.next()
y := p.bexpr(t.prec + 1)
x = Nod(t.op, x, y)
t = prectab[p.tok]
tprec = t.prec
}
x = Nod(op, x, p.bexpr(prec1))
}
return x
}
@ -1162,7 +1117,7 @@ func (p *parser) expr() *Node {
defer p.trace("expr")()
}
return p.bexpr(1)
return p.bexpr(0)
}
func unparen(x *Node) *Node {
@ -1611,13 +1566,15 @@ func (p *parser) new_name(sym *Sym) *Node {
return nil
}
func (p *parser) dcl_name(sym *Sym) *Node {
func (p *parser) dcl_name() *Node {
if trace && Debug['x'] != 0 {
defer p.trace("dcl_name")()
}
symlineno := lineno
sym := p.sym()
if sym == nil {
yyerrorl(int(prevlineno), "invalid declaration")
yyerrorl(int(symlineno), "invalid declaration")
return nil
}
return dclname(sym)
@ -1894,25 +1851,21 @@ func (p *parser) xfndcl() *Node {
}
p.want(LFUNC)
f := p.fndcl()
f := p.fndcl(p.pragma&Nointerface != 0)
body := p.fnbody()
if f == nil {
return nil
}
if noescape && body != nil {
Yyerror("can only use //go:noescape with external func implementations")
}
f.Nbody = body
f.Noescape = p.pragma&Noescape != 0
if f.Noescape && body != nil {
Yyerror("can only use //go:noescape with external func implementations")
}
f.Func.Pragma = p.pragma
f.Func.Endlineno = lineno
f.Noescape = noescape
f.Func.Norace = norace
f.Func.Nosplit = nosplit
f.Func.Noinline = noinline
f.Func.Nowritebarrier = nowritebarrier
f.Func.Nowritebarrierrec = nowritebarrierrec
f.Func.Systemstack = systemstack
funcbody(f)
return f
@ -1923,7 +1876,7 @@ func (p *parser) xfndcl() *Node {
// Function = Signature FunctionBody .
// MethodDecl = "func" Receiver MethodName ( Function | Signature ) .
// Receiver = Parameters .
func (p *parser) fndcl() *Node {
func (p *parser) fndcl(nointerface bool) *Node {
if trace && Debug['x'] != 0 {
defer p.trace("fndcl")()
}
@ -2059,8 +2012,7 @@ func (p *parser) hidden_fndcl() *Node {
ss.Type = functype(s2.N, s6, s8)
checkwidth(ss.Type)
addmethod(s4, ss.Type, false, nointerface)
nointerface = false
addmethod(s4, ss.Type, false, false)
funchdr(ss)
// inl.C's inlnode in on a dotmeth node expects to find the inlineable body as
@ -2141,18 +2093,10 @@ loop:
testdclstack()
}
noescape = false
noinline = false
nointerface = false
norace = false
nosplit = false
nowritebarrier = false
nowritebarrierrec = false
systemstack = false
// Reset p.pragma BEFORE advancing to the next token (consuming ';')
// since comments before may set pragmas for the next function decl.
p.pragma = 0
// Consume ';' AFTER resetting the above flags since
// it may read the subsequent comment line which may
// set the flags for the next function declaration.
if p.tok != EOF && !p.got(';') {
p.syntax_error("after top level declaration")
p.advance(LVAR, LCONST, LTYPE, LFUNC)
@ -2567,15 +2511,15 @@ func (p *parser) stmt() *Node {
stmt := Nod(ORETURN, nil, nil)
stmt.List = results
if stmt.List == nil && Curfn != nil {
for l := Curfn.Func.Dcl; l != nil; l = l.Next {
if l.N.Class == PPARAM {
for _, ln := range Curfn.Func.Dcl {
if ln.Class == PPARAM {
continue
}
if l.N.Class != PPARAMOUT {
if ln.Class != PPARAMOUT {
break
}
if l.N.Sym.Def != l.N {
Yyerror("%s is shadowed during return", l.N.Sym.Name)
if ln.Sym.Def != ln {
Yyerror("%s is shadowed during return", ln.Sym.Name)
}
}
}
@ -2639,9 +2583,9 @@ func (p *parser) dcl_name_list() *NodeList {
defer p.trace("dcl_name_list")()
}
l := list1(p.dcl_name(p.sym()))
l := list1(p.dcl_name())
for p.got(',') {
l = list(l, p.dcl_name(p.sym()))
l = list(l, p.dcl_name())
}
return l
}

View File

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

View File

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

View File

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

View File

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

View File

@ -491,17 +491,11 @@ func dextratype(sym *Sym, off int, t *Type, ptroff int) int {
ot := off
s := sym
if t.Sym != nil {
ot = dgostringptr(s, ot, t.Sym.Name)
if t != Types[t.Etype] && t != errortype {
if t.Sym != nil && t != Types[t.Etype] && t != errortype {
ot = dgopkgpath(s, ot, t.Sym.Pkg)
} else {
ot = dgostringptr(s, ot, "")
}
} else {
ot = dgostringptr(s, ot, "")
ot = dgostringptr(s, ot, "")
}
// slice header
ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint)
@ -700,12 +694,14 @@ func dcommontype(s *Sym, ot int, t *Type) int {
algsym = dalgsym(t)
}
var sptr *Sym
tptr := Ptrto(t)
if !Isptr[t.Etype] && (t.Sym != nil || methods(tptr) != nil) {
sptr = dtypesym(tptr)
} else {
sptr = weaktypesym(tptr)
sptr := dtypesym(tptr)
r := obj.Addrel(Linksym(s))
r.Off = 0
r.Siz = 0
r.Sym = sptr.Lsym
r.Type = obj.R_USETYPE
}
gcsym, useGCProg, ptrdata := dgcsym(t)
@ -724,7 +720,6 @@ func dcommontype(s *Sym, ot int, t *Type) int {
// gcdata *byte
// string *string
// *uncommonType
// ptrToThis *rtype
// }
ot = duintptr(s, ot, uint64(t.Width))
ot = duintptr(s, ot, uint64(ptrdata))
@ -763,12 +758,14 @@ func dcommontype(s *Sym, ot int, t *Type) int {
} else {
ot = dsymptr(s, ot, algsym, 0)
}
ot = dsymptr(s, ot, gcsym, 0)
ot = dsymptr(s, ot, gcsym, 0) // gcdata
p := Tconv(t, obj.FmtLeft|obj.FmtUnsigned)
//print("dcommontype: %s\n", p);
ot = dgostringptr(s, ot, p) // string
_, symdata := stringsym(p) // string
ot = dsymptr(s, ot, symdata, 0)
ot = duintxx(s, ot, uint64(len(p)), Widthint)
//fmt.Printf("dcommontype: %s\n", p)
// skip pointer to extraType,
// which follows the rest of this type structure.
@ -776,7 +773,6 @@ func dcommontype(s *Sym, ot int, t *Type) int {
// otherwise linker will assume 0.
ot += Widthptr
ot = dsymptr(s, ot, sptr, 0) // ptrto type
return ot
}
@ -1006,7 +1002,7 @@ ok:
switch t.Etype {
default:
ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr
xt = ot - 1*Widthptr
case TARRAY:
if t.Bound >= 0 {
@ -1018,7 +1014,7 @@ ok:
t2.Bound = -1 // slice
s2 := dtypesym(t2)
ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr
xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s1, 0)
ot = dsymptr(s, ot, s2, 0)
ot = duintptr(s, ot, uint64(t.Bound))
@ -1027,7 +1023,7 @@ ok:
s1 := dtypesym(t.Type)
ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr
xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s1, 0)
}
@ -1036,7 +1032,7 @@ ok:
s1 := dtypesym(t.Type)
ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr
xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s1, 0)
ot = duintptr(s, ot, uint64(t.Chan))
@ -1055,7 +1051,7 @@ ok:
}
ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr
xt = ot - 1*Widthptr
ot = duint8(s, ot, uint8(obj.Bool2int(isddd)))
// two slice headers: in and out.
@ -1093,7 +1089,7 @@ ok:
// ../../../../runtime/type.go:/interfaceType
ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr
xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint)
ot = duintxx(s, ot, uint64(n), Widthint)
ot = duintxx(s, ot, uint64(n), Widthint)
@ -1113,7 +1109,7 @@ ok:
s3 := dtypesym(mapbucket(t))
s4 := dtypesym(hmap(t))
ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr
xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s1, 0)
ot = dsymptr(s, ot, s2, 0)
ot = dsymptr(s, ot, s3, 0)
@ -1150,7 +1146,7 @@ ok:
s1 := dtypesym(t.Type)
ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr
xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s1, 0)
// ../../../../runtime/type.go:/structType
@ -1164,7 +1160,7 @@ ok:
}
ot = dcommontype(s, ot, t)
xt = ot - 2*Widthptr
xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint)
ot = duintxx(s, ot, uint64(n), Widthint)
ot = duintxx(s, ot, uint64(n), Widthint)
@ -1203,21 +1199,7 @@ ok:
// we want be able to find.
if t.Sym == nil {
switch t.Etype {
case TPTR32, TPTR64:
// The ptrto field of the type data cannot be relied on when
// dynamic linking: a type T may be defined in a module that makes
// no use of pointers to that type, but another module can contain
// a package that imports the first one and does use *T pointers.
// The second module will end up defining type data for *T and a
// type.*T symbol pointing at it. It's important that calling
// .PtrTo() on the reflect.Type for T returns this type data and
// not some synthesized object, so we need reflect to be able to
// find it!
if !Ctxt.Flag_dynlink {
break
}
fallthrough
case TARRAY, TCHAN, TFUNC, TMAP:
case TPTR32, TPTR64, TARRAY, TCHAN, TFUNC, TMAP:
slink := typelinksym(t)
dsymptr(slink, 0, s, 0)
ggloblsym(slink, int32(Widthptr), int16(dupok|obj.RODATA))
@ -1377,7 +1359,7 @@ func dalgsym(t *Type) *Sym {
// be multiples of four words. On 32-bit systems that's 16 bytes, and
// all size classes >= 16 bytes are 16-byte aligned, so no real constraint.
// On 64-bit systems, that's 32 bytes, and 32-byte alignment is guaranteed
// for size classes >= 256 bytes. On a 64-bit sytem, 256 bytes allocated
// for size classes >= 256 bytes. On a 64-bit system, 256 bytes allocated
// is 32 pointers, the bits for which fit in 4 bytes. So maxPtrmaskBytes
// must be >= 4.
//

View File

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

View File

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

View File

@ -19,7 +19,6 @@ import (
type Error struct {
lineno int
seq int
msg string
}
@ -49,35 +48,24 @@ func adderrorname(n *Node) {
func adderr(line int, format string, args ...interface{}) {
errors = append(errors, Error{
seq: len(errors),
lineno: line,
msg: fmt.Sprintf("%v: %s\n", Ctxt.Line(line), fmt.Sprintf(format, args...)),
})
}
// errcmp sorts errors by line, then seq, then message.
type errcmp []Error
// byLineno sorts errors by lineno.
type byLineno []Error
func (x errcmp) Len() int { return len(x) }
func (x errcmp) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x errcmp) Less(i, j int) bool {
a := &x[i]
b := &x[j]
if a.lineno != b.lineno {
return a.lineno < b.lineno
}
if a.seq != b.seq {
return a.seq < b.seq
}
return a.msg < b.msg
}
func (x byLineno) Len() int { return len(x) }
func (x byLineno) Less(i, j int) bool { return x[i].lineno < x[j].lineno }
func (x byLineno) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func Flusherrors() {
bstdout.Flush()
if len(errors) == 0 {
return
}
sort.Sort(errcmp(errors))
sort.Stable(byLineno(errors))
for i := 0; i < len(errors); i++ {
if i == 0 || errors[i].msg != errors[i-1].msg {
fmt.Printf("%s", errors[i].msg)
@ -109,7 +97,7 @@ func yyerrorl(line int, format string, args ...interface{}) {
}
}
var yyerror_lastsyntax int
var yyerror_lastsyntax int32
func Yyerror(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
@ -117,18 +105,12 @@ func Yyerror(format string, args ...interface{}) {
nsyntaxerrors++
// only one syntax error per line
if int32(yyerror_lastsyntax) == lexlineno {
if yyerror_lastsyntax == lineno {
return
}
yyerror_lastsyntax = int(lexlineno)
yyerror_lastsyntax = lineno
// plain "syntax error" gets "near foo" added
if msg == "syntax error" {
yyerrorl(int(lexlineno), "syntax error near %s", lexbuf.String())
return
}
yyerrorl(int(lexlineno), "%s", msg)
yyerrorl(int(lineno), "%s", msg)
return
}
@ -258,7 +240,6 @@ func (pkg *Pkg) Lookup(name string) *Sym {
s := &Sym{
Name: name,
Pkg: pkg,
Lexical: LNAME,
}
if name == "init" {
initSyms = append(initSyms, s)
@ -367,162 +348,6 @@ func saveorignode(n *Node) {
n.Orig = norig
}
// ispaddedfield reports whether the given field
// is followed by padding. For the case where t is
// the last field, total gives the size of the enclosing struct.
func ispaddedfield(t *Type, total int64) bool {
if t.Etype != TFIELD {
Fatalf("ispaddedfield called non-field %v", t)
}
if t.Down == nil {
return t.Width+t.Type.Width != total
}
return t.Width+t.Type.Width != t.Down.Width
}
func algtype1(t *Type, bad **Type) int {
if bad != nil {
*bad = nil
}
if t.Broke {
return AMEM
}
if t.Noalg {
return ANOEQ
}
switch t.Etype {
// will be defined later.
case TANY, TFORW:
*bad = t
return -1
case TINT8,
TUINT8,
TINT16,
TUINT16,
TINT32,
TUINT32,
TINT64,
TUINT64,
TINT,
TUINT,
TUINTPTR,
TBOOL,
TPTR32,
TPTR64,
TCHAN,
TUNSAFEPTR:
return AMEM
case TFUNC, TMAP:
if bad != nil {
*bad = t
}
return ANOEQ
case TFLOAT32:
return AFLOAT32
case TFLOAT64:
return AFLOAT64
case TCOMPLEX64:
return ACPLX64
case TCOMPLEX128:
return ACPLX128
case TSTRING:
return ASTRING
case TINTER:
if isnilinter(t) {
return ANILINTER
}
return AINTER
case TARRAY:
if Isslice(t) {
if bad != nil {
*bad = t
}
return ANOEQ
}
a := algtype1(t.Type, bad)
if a == ANOEQ || a == AMEM {
if a == ANOEQ && bad != nil {
*bad = t
}
return a
}
switch t.Bound {
case 0:
// We checked above that the element type is comparable.
return AMEM
case 1:
// Single-element array is same as its lone element.
return a
}
return -1 // needs special compare
case TSTRUCT:
if t.Type != nil && t.Type.Down == nil && !isblanksym(t.Type.Sym) {
// One-field struct is same as that one field alone.
return algtype1(t.Type.Type, bad)
}
ret := AMEM
var a int
for t1 := t.Type; t1 != nil; t1 = t1.Down {
// All fields must be comparable.
a = algtype1(t1.Type, bad)
if a == ANOEQ {
return ANOEQ
}
// Blank fields, padded fields, fields with non-memory
// equality need special compare.
if a != AMEM || isblanksym(t1.Sym) || ispaddedfield(t1, t.Width) {
ret = -1
continue
}
}
return ret
}
Fatalf("algtype1: unexpected type %v", t)
return 0
}
func algtype(t *Type) int {
a := algtype1(t, nil)
if a == AMEM {
switch t.Width {
case 0:
return AMEM0
case 1:
return AMEM8
case 2:
return AMEM16
case 4:
return AMEM32
case 8:
return AMEM64
case 16:
return AMEM128
}
}
return a
}
func maptype(key *Type, val *Type) *Type {
if key != nil {
var bad *Type
@ -1561,8 +1386,8 @@ func frame(context int) {
if Curfn != nil {
fmt.Printf("--- %v frame ---\n", Curfn.Func.Nname.Sym)
for l := Curfn.Func.Dcl; l != nil; l = l.Next {
printframenode(l.N)
for _, ln := range Curfn.Func.Dcl {
printframenode(ln)
}
}
}
@ -2414,457 +2239,6 @@ func hashmem(t *Type) *Node {
return n
}
func hashfor(t *Type) *Node {
var sym *Sym
a := algtype1(t, nil)
switch a {
case AMEM:
Fatalf("hashfor with AMEM type")
case AINTER:
sym = Pkglookup("interhash", Runtimepkg)
case ANILINTER:
sym = Pkglookup("nilinterhash", Runtimepkg)
case ASTRING:
sym = Pkglookup("strhash", Runtimepkg)
case AFLOAT32:
sym = Pkglookup("f32hash", Runtimepkg)
case AFLOAT64:
sym = Pkglookup("f64hash", Runtimepkg)
case ACPLX64:
sym = Pkglookup("c64hash", Runtimepkg)
case ACPLX128:
sym = Pkglookup("c128hash", Runtimepkg)
default:
sym = typesymprefix(".hash", t)
}
n := newname(sym)
n.Class = PFUNC
tfn := Nod(OTFUNC, nil, nil)
tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
tfn.Rlist = list(tfn.Rlist, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
typecheck(&tfn, Etype)
n.Type = tfn.Type
return n
}
// Generate a helper function to compute the hash of a value of type t.
func genhash(sym *Sym, t *Type) {
if Debug['r'] != 0 {
fmt.Printf("genhash %v %v\n", sym, t)
}
lineno = 1 // less confusing than end of input
dclcontext = PEXTERN
markdcl()
// func sym(p *T, h uintptr) uintptr
fn := Nod(ODCLFUNC, nil, nil)
fn.Func.Nname = newname(sym)
fn.Func.Nname.Class = PFUNC
tfn := Nod(OTFUNC, nil, nil)
fn.Func.Nname.Name.Param.Ntype = tfn
n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
tfn.List = list(tfn.List, n)
np := n.Left
n = Nod(ODCLFIELD, newname(Lookup("h")), typenod(Types[TUINTPTR]))
tfn.List = list(tfn.List, n)
nh := n.Left
n = Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])) // return value
tfn.Rlist = list(tfn.Rlist, n)
funchdr(fn)
typecheck(&fn.Func.Nname.Name.Param.Ntype, Etype)
// genhash is only called for types that have equality but
// cannot be handled by the standard algorithms,
// so t must be either an array or a struct.
switch t.Etype {
default:
Fatalf("genhash %v", t)
case TARRAY:
if Isslice(t) {
Fatalf("genhash %v", t)
}
// An array of pure memory would be handled by the
// standard algorithm, so the element type must not be
// pure memory.
hashel := hashfor(t.Type)
n := Nod(ORANGE, nil, Nod(OIND, np, nil))
ni := newname(Lookup("i"))
ni.Type = Types[TINT]
n.List = list1(ni)
n.Colas = true
colasdefn(n.List, n)
ni = n.List.N
// h = hashel(&p[i], h)
call := Nod(OCALL, hashel, nil)
nx := Nod(OINDEX, np, ni)
nx.Bounded = true
na := Nod(OADDR, nx, nil)
na.Etype = 1 // no escape to heap
call.List = list(call.List, na)
call.List = list(call.List, nh)
n.Nbody = list(n.Nbody, Nod(OAS, nh, call))
fn.Nbody = list(fn.Nbody, n)
// Walk the struct using memhash for runs of AMEM
// and calling specific hash functions for the others.
case TSTRUCT:
var first *Type
offend := int64(0)
var size int64
var call *Node
var nx *Node
var na *Node
var hashel *Node
for t1 := t.Type; ; t1 = t1.Down {
if t1 != nil && algtype1(t1.Type, nil) == AMEM && !isblanksym(t1.Sym) {
offend = t1.Width + t1.Type.Width
if first == nil {
first = t1
}
// If it's a memory field but it's padded, stop here.
if ispaddedfield(t1, t.Width) {
t1 = t1.Down
} else {
continue
}
}
// Run memhash for fields up to this one.
if first != nil {
size = offend - first.Width // first->width is offset
hashel = hashmem(first.Type)
// h = hashel(&p.first, size, h)
call = Nod(OCALL, hashel, nil)
nx = Nod(OXDOT, np, newname(first.Sym)) // TODO: fields from other packages?
na = Nod(OADDR, nx, nil)
na.Etype = 1 // no escape to heap
call.List = list(call.List, na)
call.List = list(call.List, nh)
call.List = list(call.List, Nodintconst(size))
fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call))
first = nil
}
if t1 == nil {
break
}
if isblanksym(t1.Sym) {
continue
}
// Run hash for this field.
if algtype1(t1.Type, nil) == AMEM {
hashel = hashmem(t1.Type)
// h = memhash(&p.t1, h, size)
call = Nod(OCALL, hashel, nil)
nx = Nod(OXDOT, np, newname(t1.Sym)) // TODO: fields from other packages?
na = Nod(OADDR, nx, nil)
na.Etype = 1 // no escape to heap
call.List = list(call.List, na)
call.List = list(call.List, nh)
call.List = list(call.List, Nodintconst(t1.Type.Width))
fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call))
} else {
hashel = hashfor(t1.Type)
// h = hashel(&p.t1, h)
call = Nod(OCALL, hashel, nil)
nx = Nod(OXDOT, np, newname(t1.Sym)) // TODO: fields from other packages?
na = Nod(OADDR, nx, nil)
na.Etype = 1 // no escape to heap
call.List = list(call.List, na)
call.List = list(call.List, nh)
fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call))
}
}
}
r := Nod(ORETURN, nil, nil)
r.List = list(r.List, nh)
fn.Nbody = list(fn.Nbody, r)
if Debug['r'] != 0 {
dumplist("genhash body", fn.Nbody)
}
funcbody(fn)
Curfn = fn
fn.Func.Dupok = true
typecheck(&fn, Etop)
typechecklist(fn.Nbody, Etop)
Curfn = nil
// Disable safemode while compiling this code: the code we
// generate internally can refer to unsafe.Pointer.
// In this case it can happen if we need to generate an ==
// for a struct containing a reflect.Value, which itself has
// an unexported field of type unsafe.Pointer.
old_safemode := safemode
safemode = 0
funccompile(fn)
safemode = old_safemode
}
// eqfield returns the node
// p.field == q.field
func eqfield(p *Node, q *Node, field *Node) *Node {
nx := Nod(OXDOT, p, field)
ny := Nod(OXDOT, q, field)
ne := Nod(OEQ, nx, ny)
return ne
}
func eqmemfunc(size int64, type_ *Type, needsize *int) *Node {
var fn *Node
switch size {
default:
fn = syslook("memequal", 1)
*needsize = 1
case 1, 2, 4, 8, 16:
buf := fmt.Sprintf("memequal%d", int(size)*8)
fn = syslook(buf, 1)
*needsize = 0
}
substArgTypes(fn, type_, type_)
return fn
}
// eqmem returns the node
// memequal(&p.field, &q.field [, size])
func eqmem(p *Node, q *Node, field *Node, size int64) *Node {
var needsize int
nx := Nod(OADDR, Nod(OXDOT, p, field), nil)
nx.Etype = 1 // does not escape
ny := Nod(OADDR, Nod(OXDOT, q, field), nil)
ny.Etype = 1 // does not escape
typecheck(&nx, Erv)
typecheck(&ny, Erv)
call := Nod(OCALL, eqmemfunc(size, nx.Type.Type, &needsize), nil)
call.List = list(call.List, nx)
call.List = list(call.List, ny)
if needsize != 0 {
call.List = list(call.List, Nodintconst(size))
}
return call
}
// geneq generates a helper function to
// check equality of two values of type t.
func geneq(sym *Sym, t *Type) {
if Debug['r'] != 0 {
fmt.Printf("geneq %v %v\n", sym, t)
}
lineno = 1 // less confusing than end of input
dclcontext = PEXTERN
markdcl()
// func sym(p, q *T) bool
fn := Nod(ODCLFUNC, nil, nil)
fn.Func.Nname = newname(sym)
fn.Func.Nname.Class = PFUNC
tfn := Nod(OTFUNC, nil, nil)
fn.Func.Nname.Name.Param.Ntype = tfn
n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
tfn.List = list(tfn.List, n)
np := n.Left
n = Nod(ODCLFIELD, newname(Lookup("q")), typenod(Ptrto(t)))
tfn.List = list(tfn.List, n)
nq := n.Left
n = Nod(ODCLFIELD, nil, typenod(Types[TBOOL]))
tfn.Rlist = list(tfn.Rlist, n)
funchdr(fn)
// geneq is only called for types that have equality but
// cannot be handled by the standard algorithms,
// so t must be either an array or a struct.
switch t.Etype {
default:
Fatalf("geneq %v", t)
case TARRAY:
if Isslice(t) {
Fatalf("geneq %v", t)
}
// An array of pure memory would be handled by the
// standard memequal, so the element type must not be
// pure memory. Even if we unrolled the range loop,
// each iteration would be a function call, so don't bother
// unrolling.
nrange := Nod(ORANGE, nil, Nod(OIND, np, nil))
ni := newname(Lookup("i"))
ni.Type = Types[TINT]
nrange.List = list1(ni)
nrange.Colas = true
colasdefn(nrange.List, nrange)
ni = nrange.List.N
// if p[i] != q[i] { return false }
nx := Nod(OINDEX, np, ni)
nx.Bounded = true
ny := Nod(OINDEX, nq, ni)
ny.Bounded = true
nif := Nod(OIF, nil, nil)
nif.Left = Nod(ONE, nx, ny)
r := Nod(ORETURN, nil, nil)
r.List = list(r.List, Nodbool(false))
nif.Nbody = list(nif.Nbody, r)
nrange.Nbody = list(nrange.Nbody, nif)
fn.Nbody = list(fn.Nbody, nrange)
// return true
ret := Nod(ORETURN, nil, nil)
ret.List = list(ret.List, Nodbool(true))
fn.Nbody = list(fn.Nbody, ret)
// Walk the struct using memequal for runs of AMEM
// and calling specific equality tests for the others.
// Skip blank-named fields.
case TSTRUCT:
var first *Type
var conjuncts []*Node
offend := int64(0)
var size int64
for t1 := t.Type; ; t1 = t1.Down {
if t1 != nil && algtype1(t1.Type, nil) == AMEM && !isblanksym(t1.Sym) {
offend = t1.Width + t1.Type.Width
if first == nil {
first = t1
}
// If it's a memory field but it's padded, stop here.
if ispaddedfield(t1, t.Width) {
t1 = t1.Down
} else {
continue
}
}
// Run memequal for fields up to this one.
// TODO(rsc): All the calls to newname are wrong for
// cross-package unexported fields.
if first != nil {
if first.Down == t1 {
conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym)))
} else if first.Down.Down == t1 {
conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym)))
first = first.Down
if !isblanksym(first.Sym) {
conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym)))
}
} else {
// More than two fields: use memequal.
size = offend - first.Width // first->width is offset
conjuncts = append(conjuncts, eqmem(np, nq, newname(first.Sym), size))
}
first = nil
}
if t1 == nil {
break
}
if isblanksym(t1.Sym) {
continue
}
// Check this field, which is not just memory.
conjuncts = append(conjuncts, eqfield(np, nq, newname(t1.Sym)))
}
var and *Node
switch len(conjuncts) {
case 0:
and = Nodbool(true)
case 1:
and = conjuncts[0]
default:
and = Nod(OANDAND, conjuncts[0], conjuncts[1])
for _, conjunct := range conjuncts[2:] {
and = Nod(OANDAND, and, conjunct)
}
}
ret := Nod(ORETURN, nil, nil)
ret.List = list(ret.List, and)
fn.Nbody = list(fn.Nbody, ret)
}
if Debug['r'] != 0 {
dumplist("geneq body", fn.Nbody)
}
funcbody(fn)
Curfn = fn
fn.Func.Dupok = true
typecheck(&fn, Etop)
typechecklist(fn.Nbody, Etop)
Curfn = nil
// Disable safemode while compiling this code: the code we
// generate internally can refer to unsafe.Pointer.
// In this case it can happen if we need to generate an ==
// for a struct containing a reflect.Value, which itself has
// an unexported field of type unsafe.Pointer.
old_safemode := safemode
safemode = 0
// Disable checknils while compiling this code.
// We are comparing a struct or an array,
// neither of which can be nil, and our comparisons
// are shallow.
Disable_checknil++
funccompile(fn)
safemode = old_safemode
Disable_checknil--
}
func ifacelookdot(s *Sym, t *Type, followptr *bool, ignorecase int) *Type {
*followptr = false

View File

@ -149,11 +149,11 @@ type Param struct {
// Func holds Node fields used only with function-like nodes.
type Func struct {
Shortname *Node
Enter *NodeList // for example, allocate and initialize memory for escaping parameters
Exit *NodeList
Cvars *NodeList // closure params
Dcl *NodeList // autodcl for this func/closure
Inldcl *NodeList // copy of dcl for use in inlining
Enter Nodes // for example, allocate and initialize memory for escaping parameters
Exit Nodes
Cvars Nodes // closure params
Dcl []*Node // autodcl for this func/closure
Inldcl *[]*Node // copy of dcl for use in inlining
Closgen int
Outerfunc *Node
Fieldtrack []*Type
@ -169,18 +169,12 @@ type Func struct {
Depth int32
Endlineno int32
WBLineno int32 // line number of first write barrier
Norace bool // func must not have race detector annotations
Nosplit bool // func should not execute on separate stack
Noinline bool // func should not be inlined
Nowritebarrier bool // emit compiler error instead of write barrier
Nowritebarrierrec bool // error on write barrier in this or recursive callees
Pragma Pragma // go:xxx function annotations
Dupok bool // duplicate definitions ok
Wrapper bool // is method wrapper
Needctxt bool // function uses context register (has closure variables)
Systemstack bool // must run on system stack
WBLineno int32 // line number of first write barrier
}
type Op uint8
@ -491,3 +485,55 @@ func count(l *NodeList) int {
}
return int(n)
}
// Nodes is a pointer to a slice of *Node.
// For fields that are not used in most nodes, this is used instead of
// a slice to save space.
type Nodes struct{ slice *[]*Node }
// Slice returns the entries in Nodes as a slice.
// Changes to the slice entries (as in s[i] = n) will be reflected in
// the Nodes.
func (n *Nodes) Slice() []*Node {
if n.slice == nil {
return nil
}
return *n.slice
}
// NodeList returns the entries in Nodes as a NodeList.
// Changes to the NodeList entries (as in l.N = n) will *not* be
// reflect in the Nodes.
// This wastes memory and should be used as little as possible.
func (n *Nodes) NodeList() *NodeList {
if n.slice == nil {
return nil
}
var ret *NodeList
for _, n := range *n.slice {
ret = list(ret, n)
}
return ret
}
// Set sets Nodes to a slice.
// This takes ownership of the slice.
func (n *Nodes) Set(s []*Node) {
if len(s) == 0 {
n.slice = nil
} else {
n.slice = &s
}
}
// Append appends entries to Nodes.
// If a slice is passed in, this will take ownership of it.
func (n *Nodes) Append(a ...*Node) {
if n.slice == nil {
if len(a) > 0 {
n.slice = &a
}
} else {
*n.slice = append(*n.slice, a...)
}
}

View File

@ -40,6 +40,12 @@ func typechecklist(l *NodeList, top int) {
}
}
func typecheckslice(l []*Node, top int) {
for i := range l {
typecheck(&l[i], top)
}
}
var _typekind = []string{
TINT: "int",
TUINT: "uint",
@ -3433,9 +3439,9 @@ func typecheckfunc(n *Node) {
addmethod(n.Func.Shortname.Sym, t, true, n.Func.Nname.Nointerface)
}
for l := n.Func.Dcl; l != nil; l = l.Next {
if l.N.Op == ONAME && (l.N.Class == PPARAM || l.N.Class == PPARAMOUT) {
l.N.Name.Decldepth = 1
for _, ln := range n.Func.Dcl {
if ln.Op == ONAME && (ln.Class == PPARAM || ln.Class == PPARAMOUT) {
ln.Name.Decldepth = 1
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

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

@ -6,6 +6,7 @@ package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"os"
@ -463,6 +464,9 @@ var deptab = []struct {
{"runtime/internal/sys", []string{
"zversion.go",
}},
{"go/build", []string{
"zcgo.go",
}},
}
// depsuffix records the allowed suffixes for source files.
@ -478,6 +482,7 @@ var gentab = []struct {
}{
{"zdefaultcc.go", mkzdefaultcc},
{"zversion.go", mkzversion},
{"zcgo.go", mkzcgo},
// not generated anymore, but delete the file if we see it
{"enam.c", nil},
@ -933,6 +938,7 @@ func usage() {
"clean deletes all built files\n" +
"env [-p] print environment (-p: include $PATH)\n" +
"install [dir] install individual directory\n" +
"list [-json] list all supported platforms\n" +
"test [-h] run Go test(s)\n" +
"version print Go version\n" +
"\n" +
@ -1061,9 +1067,13 @@ func cmdbootstrap() {
}
}
// Copied from go/build/build.go.
// Cannot use go/build directly because cmd/dist for a new release
// builds against an old release's go/build, which may be out of sync.
// To reduce duplication, we generate the list for go/build from this.
//
// We list all supported platforms in this list, so that this is the
// single point of truth for supported platforms. This list is used
// by 'go tool dist list'.
var cgoEnabled = map[string]bool{
"darwin/386": true,
"darwin/amd64": true,
@ -1072,19 +1082,31 @@ var cgoEnabled = map[string]bool{
"dragonfly/amd64": true,
"freebsd/386": true,
"freebsd/amd64": true,
"freebsd/arm": false,
"linux/386": true,
"linux/amd64": true,
"linux/arm": true,
"linux/arm64": true,
"linux/ppc64": false,
"linux/ppc64le": true,
"linux/mips64": false,
"linux/mips64le": false,
"android/386": true,
"android/amd64": true,
"android/arm": true,
"android/arm64": true,
"nacl/386": false,
"nacl/amd64p32": false,
"nacl/arm": false,
"netbsd/386": true,
"netbsd/amd64": true,
"netbsd/arm": true,
"openbsd/386": true,
"openbsd/amd64": true,
"openbsd/arm": false,
"plan9/386": false,
"plan9/amd64": false,
"plan9/arm": false,
"solaris/amd64": true,
"windows/386": true,
"windows/amd64": true,
@ -1128,7 +1150,7 @@ func defaulttarg() string {
fatal("current directory %s is not under %s", pwd, real_src)
}
pwd = pwd[len(real_src):]
// guard againt xrealwd return the directory without the trailing /
// guard against xrealwd returning the directory without the trailing /
pwd = strings.TrimPrefix(pwd, "/")
return pwd
@ -1195,3 +1217,43 @@ func cmdversion() {
xflagparse(0)
xprintf("%s\n", findgoversion())
}
// cmdlist lists all supported platforms.
func cmdlist() {
jsonFlag := flag.Bool("json", false, "produce JSON output")
xflagparse(0)
var plats []string
for p := range cgoEnabled {
plats = append(plats, p)
}
sort.Strings(plats)
if !*jsonFlag {
for _, p := range plats {
xprintf("%s\n", p)
}
return
}
type jsonResult struct {
GOOS string
GOARCH string
CgoSupported bool
}
var results []jsonResult
for _, p := range plats {
fields := strings.Split(p, "/")
results = append(results, jsonResult{
GOOS: fields[0],
GOARCH: fields[1],
CgoSupported: cgoEnabled[p]})
}
out, err := json.MarshalIndent(results, "", "\t")
if err != nil {
fatal("json marshal error: %v", err)
}
if _, err := os.Stdout.Write(out); err != nil {
fatal("write failed: %v", err)
}
}

View File

@ -4,7 +4,10 @@
package main
import "fmt"
import (
"bytes"
"fmt"
)
/*
* Helpers for building cmd/go and cmd/cgo.
@ -37,3 +40,28 @@ func mkzdefaultcc(dir, file string) {
file = file[:i] + "c" + file[i:]
writefile(out, file, writeSkipSame)
}
// mkzcgo writes zcgo.go for go/build package:
//
// package build
// var cgoEnabled = map[string]bool{}
//
// It is invoked to write go/build/zcgo.go.
func mkzcgo(dir, file string) {
var buf bytes.Buffer
fmt.Fprintf(&buf,
"// auto generated by go tool dist\n"+
"\n"+
"package build\n"+
"\n"+
"var cgoEnabled = map[string]bool{\n")
for plat, hasCgo := range cgoEnabled {
if hasCgo {
fmt.Fprintf(&buf, "\t%q: true,\n", plat)
}
}
fmt.Fprintf(&buf, "}")
writefile(buf.String(), file, writeSkipSame)
}

View File

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

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

@ -441,6 +441,20 @@ func (t *tester) registerTests() {
return nil
},
})
fortran := os.Getenv("FC")
if fortran == "" {
fortran, _ = exec.LookPath("gfortran")
}
if fortran != "" {
t.tests = append(t.tests, distTest{
name: "cgo_fortran",
heading: "../misc/cgo/fortran",
fn: func(dt *distTest) error {
t.addCmd(dt, "misc/cgo/fortran", "./test.bash", fortran)
return nil
},
})
}
}
if t.cgoEnabled && t.goos != "android" && !t.iOS() {
// TODO(crawshaw): reenable on android and iOS

View File

@ -1348,6 +1348,11 @@ func (b *builder) build(a *action) (err error) {
return fmt.Errorf("can't build package %s because it contains Objective-C files (%s) but it's not using cgo nor SWIG",
a.p.ImportPath, strings.Join(a.p.MFiles, ","))
}
// Same as above for Fortran files
if len(a.p.FFiles) > 0 && !a.p.usesCgo() && !a.p.usesSwig() {
return fmt.Errorf("can't build package %s because it contains Fortran files (%s) but it's not using cgo nor SWIG",
a.p.ImportPath, strings.Join(a.p.FFiles, ","))
}
defer func() {
if err != nil && err != errPrintedOutput {
err = fmt.Errorf("go build %s: %v", a.p.ImportPath, err)
@ -1437,7 +1442,7 @@ func (b *builder) build(a *action) (err error) {
if a.cgo != nil && a.cgo.target != "" {
cgoExe = a.cgo.target
}
outGo, outObj, err := b.cgo(a.p, cgoExe, obj, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, cxxfiles, a.p.MFiles)
outGo, outObj, err := b.cgo(a.p, cgoExe, obj, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, cxxfiles, a.p.MFiles, a.p.FFiles)
if err != nil {
return err
}
@ -2272,7 +2277,7 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool,
// so that it can give good error messages about forward declarations.
// Exceptions: a few standard packages have forward declarations for
// pieces supplied behind-the-scenes by package runtime.
extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles)
extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.FFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles)
if p.Standard {
switch p.ImportPath {
case "bytes", "net", "os", "runtime/pprof", "sync", "time":
@ -2623,6 +2628,7 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions
usesCgo := false
cxx := len(root.p.CXXFiles) > 0 || len(root.p.SwigCXXFiles) > 0
objc := len(root.p.MFiles) > 0
fortran := len(root.p.FFiles) > 0
actionsSeen := make(map[*action]bool)
// Make a pre-order depth-first traversal of the action graph, taking note of
@ -2697,6 +2703,9 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions
if len(a.p.MFiles) > 0 {
objc = true
}
if len(a.p.FFiles) > 0 {
fortran = true
}
}
ldflags = append(ldflags, "-Wl,--whole-archive")
@ -2768,6 +2777,17 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions
if objc {
ldflags = append(ldflags, "-lobjc")
}
if fortran {
fc := os.Getenv("FC")
if fc == "" {
fc = "gfortran"
}
// support gfortran out of the box and let others pass the correct link options
// via CGO_LDFLAGS
if strings.Contains(fc, "gfortran") {
ldflags = append(ldflags, "-lgfortran")
}
}
}
if err := b.run(".", root.p.ImportPath, nil, tools.linker(), "-o", out, ofiles, ldflags, buildGccgoflags); err != nil {
@ -2862,6 +2882,11 @@ func (b *builder) gxx(p *Package, out string, flags []string, cxxfile string) er
return b.ccompile(p, out, flags, cxxfile, b.gxxCmd(p.Dir))
}
// gfortran runs the gfortran Fortran compiler to create an object from a single Fortran file.
func (b *builder) gfortran(p *Package, out string, flags []string, ffile string) error {
return b.ccompile(p, out, flags, ffile, b.gfortranCmd(p.Dir))
}
// ccompile runs the given C or C++ compiler and creates an object from a single source file.
func (b *builder) ccompile(p *Package, out string, flags []string, file string, compiler []string) error {
file = mkAbs(p.Dir, file)
@ -2891,6 +2916,11 @@ func (b *builder) gxxCmd(objdir string) []string {
return b.ccompilerCmd("CXX", defaultCXX, objdir)
}
// gfortranCmd returns a gfortran command line prefix.
func (b *builder) gfortranCmd(objdir string) []string {
return b.ccompilerCmd("FC", "gfortran", objdir)
}
// ccompilerCmd returns a command line prefix for the given environment
// variable and using the default command when the variable is empty.
func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string {
@ -3009,8 +3039,8 @@ func envList(key, def string) []string {
return strings.Fields(v)
}
// Return the flags to use when invoking the C or C++ compilers, or cgo.
func (b *builder) cflags(p *Package, def bool) (cppflags, cflags, cxxflags, ldflags []string) {
// Return the flags to use when invoking the C, C++ or Fortran compilers, or cgo.
func (b *builder) cflags(p *Package, def bool) (cppflags, cflags, cxxflags, fflags, ldflags []string) {
var defaults string
if def {
defaults = "-g -O2"
@ -3019,15 +3049,16 @@ func (b *builder) cflags(p *Package, def bool) (cppflags, cflags, cxxflags, ldfl
cppflags = stringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS)
cflags = stringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS)
cxxflags = stringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS)
fflags = stringList(envList("CGO_FFLAGS", defaults), p.CgoFFLAGS)
ldflags = stringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS)
return
}
var cgoRe = regexp.MustCompile(`[/\\:]`)
func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) {
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoLDFLAGS := b.cflags(p, true)
_, cgoexeCFLAGS, _, _ := b.cflags(p, false)
func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, gxxfiles, mfiles, ffiles []string) (outGo, outObj []string, err error) {
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoFFLAGS, cgoLDFLAGS := b.cflags(p, true)
_, cgoexeCFLAGS, _, _, _ := b.cflags(p, false)
cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...)
cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...)
// If we are compiling Objective-C code, then we need to link against libobjc
@ -3035,6 +3066,19 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
cgoLDFLAGS = append(cgoLDFLAGS, "-lobjc")
}
// Likewise for Fortran, except there are many Fortran compilers.
// Support gfortran out of the box and let others pass the correct link options
// via CGO_LDFLAGS
if len(ffiles) > 0 {
fc := os.Getenv("FC")
if fc == "" {
fc = "gfortran"
}
if strings.Contains(fc, "gfortran") {
cgoLDFLAGS = append(cgoLDFLAGS, "-lgfortran")
}
}
if buildMSan && p.ImportPath != "runtime/cgo" {
cgoCFLAGS = append([]string{"-fsanitize=memory"}, cgoCFLAGS...)
cgoLDFLAGS = append([]string{"-fsanitize=memory"}, cgoLDFLAGS...)
@ -3202,6 +3246,17 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
outObj = append(outObj, ofile)
}
fflags := stringList(cgoCPPFLAGS, cgoFFLAGS)
for _, file := range ffiles {
// Append .o to the file, just in case the pkg has file.c and file.f
ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o"
if err := b.gfortran(p, ofile, fflags, file); err != nil {
return nil, nil, err
}
linkobj = append(linkobj, ofile)
outObj = append(outObj, ofile)
}
linkobj = append(linkobj, p.SysoFiles...)
dynobj := obj + "_cgo_.o"
pie := (goarch == "arm" && goos == "linux") || goos == "android"
@ -3395,7 +3450,7 @@ func (b *builder) swigIntSize(obj string) (intsize string, err error) {
// Run SWIG on one SWIG input file.
func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx bool, intgosize string) (outGo, outC string, err error) {
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true)
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _, _ := b.cflags(p, true)
var cflags []string
if cxx {
cflags = stringList(cgoCPPFLAGS, pcCFLAGS, cgoCXXFLAGS)

View File

@ -119,6 +119,14 @@ func runGet(cmd *Command, args []string) {
delete(packageCache, name)
}
// In order to rebuild packages information completely,
// we need to clear commands cache. Command packages are
// referring to evicted packages from the package cache.
// This leads to duplicated loads of the standard packages.
for name := range cmdCache {
delete(cmdCache, name)
}
args = importPaths(args)
packagesForBuild(args)

View File

@ -2122,7 +2122,7 @@ func TestIssue7108(t *testing.T) {
// cmd/go: go test -a foo does not rebuild regexp.
func TestIssue6844(t *testing.T) {
if testing.Short() {
t.Skip("don't rebuild the standard libary in short mode")
t.Skip("don't rebuild the standard library in short mode")
}
tg := testgo(t)
@ -2763,6 +2763,10 @@ func TestCgoConsistentResults(t *testing.T) {
if !canCgo {
t.Skip("skipping because cgo not enabled")
}
if runtime.GOOS == "solaris" {
// See https://golang.org/issue/13247
t.Skip("skipping because Solaris builds are known to be inconsistent; see #13247")
}
tg := testgo(t)
defer tg.cleanup()
@ -2785,3 +2789,15 @@ func TestCgoConsistentResults(t *testing.T) {
t.Error("building cgotest twice did not produce the same output")
}
}
// Issue 14444: go get -u .../ duplicate loads errors
func TestGoGetUpdateAllDoesNotTryToLoadDuplicates(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
tg := testgo(t)
defer tg.cleanup()
tg.makeTempdir()
tg.setenv("GOPATH", tg.path("."))
tg.run("get", "-u", ".../")
tg.grepStderrNot("duplicate loads of", "did not remove old packages from cache")
}

View File

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

View File

@ -50,6 +50,7 @@ type Package struct {
CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files
MFiles []string `json:",omitempty"` // .m source files
HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx source files
FFiles []string `json:",omitempty"` // .f, .F, .for and .f90 Fortran source files
SFiles []string `json:",omitempty"` // .s source files
SwigFiles []string `json:",omitempty"` // .swig files
SwigCXXFiles []string `json:",omitempty"` // .swigcxx files
@ -59,6 +60,7 @@ type Package struct {
CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler
CgoCPPFLAGS []string `json:",omitempty"` // cgo: flags for C preprocessor
CgoCXXFLAGS []string `json:",omitempty"` // cgo: flags for C++ compiler
CgoFFLAGS []string `json:",omitempty"` // cgo: flags for Fortran compiler
CgoLDFLAGS []string `json:",omitempty"` // cgo: flags for linker
CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names
@ -161,6 +163,7 @@ func (p *Package) copyBuild(pp *build.Package) {
p.CXXFiles = pp.CXXFiles
p.MFiles = pp.MFiles
p.HFiles = pp.HFiles
p.FFiles = pp.FFiles
p.SFiles = pp.SFiles
p.SwigFiles = pp.SwigFiles
p.SwigCXXFiles = pp.SwigCXXFiles
@ -909,6 +912,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
p.CXXFiles,
p.MFiles,
p.HFiles,
p.FFiles,
p.SFiles,
p.SysoFiles,
p.SwigFiles,
@ -1216,7 +1220,7 @@ var isGoRelease = strings.HasPrefix(runtime.Version(), "go1")
// an explicit data comparison. Specifically, we build a list of the
// inputs to the build, compute its SHA1 hash, and record that as the
// ``build ID'' in the generated object. At the next build, we can
// recompute the buid ID and compare it to the one in the generated
// recompute the build ID and compare it to the one in the generated
// object. If they differ, the list of inputs has changed, so the object
// is out of date and must be rebuilt.
//
@ -1495,7 +1499,7 @@ func isStale(p *Package) bool {
// to test for write access, and then skip GOPATH roots we don't have write
// access to. But hopefully we can just use the mtimes always.
srcs := stringList(p.GoFiles, p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.SFiles, p.CgoFiles, p.SysoFiles, p.SwigFiles, p.SwigCXXFiles)
srcs := stringList(p.GoFiles, p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.CgoFiles, p.SysoFiles, p.SwigFiles, p.SwigCXXFiles)
for _, src := range srcs {
if olderThan(filepath.Join(p.Dir, src)) {
return true

View File

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

View File

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

View File

@ -55,7 +55,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
}
}
// Replace TLS register fetches on older ARM procesors.
// Replace TLS register fetches on older ARM processors.
switch p.As {
// Treat MRC 15, 0, <reg>, C13, C0, 3 specially.
case AMRC:

View File

@ -444,6 +444,11 @@ const (
R_PLT1
R_PLT2
R_USEFIELD
// R_USETYPE resolves to an *rtype, but no relocation is created. The
// linker uses this as a signal that the pointed-to type information
// should be linked into the final binary, even if there are no other
// direct references. (This is used for types reachable by reflection.)
R_USETYPE
R_POWER_TOC
R_GOTPCREL
// R_JMPMIPS (only used on mips64) resolves to non-PC-relative target address
@ -572,6 +577,7 @@ type Link struct {
Debugpcln int32
Flag_shared int32
Flag_dynlink bool
Flag_optimize bool
Bso *Biobuf
Pathname string
Windows int32
@ -617,6 +623,10 @@ type Link struct {
Data *LSym
Etext *LSym
Edata *LSym
// Cache of Progs
allocIdx int
progs [10000]Prog
}
func (ctxt *Link) Diag(format string, args ...interface{}) {

View File

@ -1234,7 +1234,7 @@ func markregused(ctxt *obj.Link, s *Sch) {
}
/*
* test to see if 2 instrictions can be
* test to see if two instructions can be
* interchanged without changing semantics
*/
func depend(ctxt *obj.Link, sa, sb *Sch) bool {

View File

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

View File

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

View File

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

View File

@ -287,6 +287,10 @@ func CConv(s uint8) string {
}
func (p *Prog) String() string {
if p == nil {
return "<nil Prog>"
}
if p.Ctxt == nil {
return "<Prog without ctxt>"
}
@ -325,10 +329,23 @@ func (p *Prog) String() string {
}
func (ctxt *Link) NewProg() *Prog {
p := new(Prog) // should be the only call to this; all others should use ctxt.NewProg
var p *Prog
if i := ctxt.allocIdx; i < len(ctxt.progs) {
p = &ctxt.progs[i]
ctxt.allocIdx = i + 1
} else {
p = new(Prog) // should be the only call to this; all others should use ctxt.NewProg
}
p.Ctxt = ctxt
return p
}
func (ctxt *Link) freeProgs() {
s := ctxt.progs[:ctxt.allocIdx]
for i := range s {
s[i] = Prog{}
}
ctxt.allocIdx = 0
}
func (ctxt *Link) Line(n int) string {
return ctxt.LineHist.LineString(n)

View File

@ -189,7 +189,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
}
}
// Rewrite 0 to $0 in 3rd argment to CMPPS etc.
// Rewrite 0 to $0 in 3rd argument to CMPPS etc.
// That's what the tables expect.
switch p.As {
case ACMPPD, ACMPPS, ACMPSD, ACMPSS:

View File

@ -109,7 +109,7 @@ func gentext() {
}
// Preserve highest 8 bits of a, and do addition to lower 24-bit
// of a and b; used to adjust ARM branch intruction's target
// of a and b; used to adjust ARM branch instruction's target
func braddoff(a int32, b int32) int32 {
return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b))
}

View File

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

View File

@ -162,7 +162,7 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
rs := r.Xsym
// ld64 has a bug handling MACHO_ARM64_RELOC_UNSIGNED with !extern relocation.
// see cmd/internal/ld/data.go for details. The workarond is that don't use !extern
// see cmd/internal/ld/data.go for details. The workaround is that don't use !extern
// UNSIGNED relocation at all.
if rs.Type == obj.SHOSTOBJ || r.Type == obj.R_CALLARM64 || r.Type == obj.R_ADDRARM64 || r.Type == obj.R_ADDR {
if rs.Dynid < 0 {

View File

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

View File

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

View File

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

View File

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

View File

@ -1340,7 +1340,7 @@ const (
addressOrder
)
// sort reoders the entries in a report based on the specified
// sort reorders the entries in a report based on the specified
// ordering criteria. The result is sorted in decreasing order for
// numeric quantities, alphabetically for text, and increasing for
// addresses.

View File

@ -408,7 +408,7 @@ func getMissingFunctionSource(filename string, asm map[int]nodes, start, end int
return fnodes, filename
}
// adjustSourcePath adjusts the pathe for a source file by trimmming
// adjustSourcePath adjusts the path for a source file by trimming
// known prefixes and searching for the file on all parents of the
// current working dir.
func adjustSourcePath(path string) (*os.File, string, error) {

View File

@ -85,12 +85,12 @@ Flag: -copylocks
Locks that are erroneously passed by value.
Documentation examples
Tests, benchmarks and documentation examples
Flag: -example
Flag: -tests
Mistakes involving example tests, including examples with incorrect names or
function signatures, or that document identifiers not in the package.
Mistakes involving tests including functions with incorrect names or signatures
and example tests that document identifiers not in the package.
Methods

View File

@ -1,124 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"go/ast"
"go/types"
"strings"
"unicode"
"unicode/utf8"
)
func init() {
register("example",
"check for common mistaken usages of documentation examples",
checkExample,
funcDecl)
}
func isExampleSuffix(s string) bool {
r, size := utf8.DecodeRuneInString(s)
return size > 0 && unicode.IsLower(r)
}
// checkExample walks the documentation example functions checking for common
// mistakes of misnamed functions, failure to map functions to existing
// identifiers, etc.
func checkExample(f *File, node ast.Node) {
if !strings.HasSuffix(f.name, "_test.go") {
return
}
var (
pkg = f.pkg
pkgName = pkg.typesPkg.Name()
scopes = []*types.Scope{pkg.typesPkg.Scope()}
lookup = func(name string) types.Object {
for _, scope := range scopes {
if o := scope.Lookup(name); o != nil {
return o
}
}
return nil
}
)
if strings.HasSuffix(pkgName, "_test") {
// Treat 'package foo_test' as an alias for 'package foo'.
var (
basePkg = strings.TrimSuffix(pkgName, "_test")
pkg = f.pkg
)
for _, p := range pkg.typesPkg.Imports() {
if p.Name() == basePkg {
scopes = append(scopes, p.Scope())
break
}
}
}
fn, ok := node.(*ast.FuncDecl)
if !ok {
// Ignore non-functions.
return
}
var (
fnName = fn.Name.Name
report = func(format string, args ...interface{}) { f.Badf(node.Pos(), format, args...) }
)
if fn.Recv != nil || !strings.HasPrefix(fnName, "Example") {
// Ignore methods and types not named "Example".
return
}
if params := fn.Type.Params; len(params.List) != 0 {
report("%s should be niladic", fnName)
}
if results := fn.Type.Results; results != nil && len(results.List) != 0 {
report("%s should return nothing", fnName)
}
if fnName == "Example" {
// Nothing more to do.
return
}
if filesRun && !includesNonTest {
// The coherence checks between a test and the package it tests
// will report false positives if no non-test files have
// been provided.
return
}
var (
exName = strings.TrimPrefix(fnName, "Example")
elems = strings.SplitN(exName, "_", 3)
ident = elems[0]
obj = lookup(ident)
)
if ident != "" && obj == nil {
// Check ExampleFoo and ExampleBadFoo.
report("%s refers to unknown identifier: %s", fnName, ident)
// Abort since obj is absent and no subsequent checks can be performed.
return
}
if elemCnt := strings.Count(exName, "_"); elemCnt == 0 {
// Nothing more to do.
return
}
mmbr := elems[1]
if ident == "" {
// Check Example_suffix and Example_BadSuffix.
if residual := strings.TrimPrefix(exName, "_"); !isExampleSuffix(residual) {
report("%s has malformed example suffix: %s", fnName, residual)
}
return
}
if !isExampleSuffix(mmbr) {
// Check ExampleFoo_Method and ExampleFoo_BadMethod.
if obj, _, _ := types.LookupFieldOrMethod(obj.Type(), true, obj.Pkg(), mmbr); obj == nil {
report("%s refers to unknown field or method: %s.%s", fnName, ident, mmbr)
}
}
if len(elems) == 3 && !isExampleSuffix(elems[2]) {
// Check ExampleFoo_Method_suffix and ExampleFoo_Method_Badsuffix.
report("%s has malformed example suffix: %s", fnName, elems[2])
}
return
}

View File

@ -1,7 +1,9 @@
// Test of examples.
package testdata
import (
"testing"
)
// Buf is a ...
type Buf []byte
@ -46,3 +48,27 @@ func ExamplePuffer() // ERROR "ExamplePuffer refers to unknown identifier: Puffe
func ExamplePuffer_Append() // ERROR "ExamplePuffer_Append refers to unknown identifier: Puffer"
func ExamplePuffer_suffix() // ERROR "ExamplePuffer_suffix refers to unknown identifier: Puffer"
func nonTest() {} // OK because it doesn't start with "Test".
func (Buf) TesthasReceiver() {} // OK because it has a receiver.
func TestOKSuffix(*testing.T) {} // OK because first char after "Test" is Uppercase.
func TestÜnicodeWorks(*testing.T) {} // OK because the first char after "Test" is Uppercase.
func TestbadSuffix(*testing.T) {} // ERROR "first letter after 'Test' must not be lowercase"
func TestemptyImportBadSuffix(*T) {} // ERROR "first letter after 'Test' must not be lowercase"
func Test(*testing.T) {} // OK "Test" on its own is considered a test.
func Testify() {} // OK because it takes no parameters.
func TesttooManyParams(*testing.T, string) {} // OK because it takes too many parameters.
func TesttooManyNames(a, b *testing.T) {} // OK because it takes too many names.
func TestnoTParam(string) {} // OK because it doesn't take a *testing.T
func BenchmarkbadSuffix(*testing.B) {} // ERROR "first letter after 'Benchmark' must not be lowercase"

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

@ -0,0 +1,182 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"go/ast"
"go/types"
"strings"
"unicode"
"unicode/utf8"
)
func init() {
register("tests",
"check for common mistaken usages of tests/documentation examples",
checkTestFunctions,
funcDecl)
}
func isExampleSuffix(s string) bool {
r, size := utf8.DecodeRuneInString(s)
return size > 0 && unicode.IsLower(r)
}
func isTestSuffix(name string) bool {
if len(name) == 0 {
// "Test" is ok.
return true
}
r, _ := utf8.DecodeRuneInString(name)
return !unicode.IsLower(r)
}
func isTestParam(typ ast.Expr, wantType string) bool {
ptr, ok := typ.(*ast.StarExpr)
if !ok {
// Not a pointer.
return false
}
// No easy way of making sure it's a *testing.T or *testing.B:
// ensure the name of the type matches.
if name, ok := ptr.X.(*ast.Ident); ok {
return name.Name == wantType
}
if sel, ok := ptr.X.(*ast.SelectorExpr); ok {
return sel.Sel.Name == wantType
}
return false
}
func lookup(name string, scopes []*types.Scope) types.Object {
for _, scope := range scopes {
if o := scope.Lookup(name); o != nil {
return o
}
}
return nil
}
func extendedScope(pkg *Package) []*types.Scope {
scopes := []*types.Scope{pkg.typesPkg.Scope()}
pkgName := pkg.typesPkg.Name()
if strings.HasPrefix(pkgName, "_test") {
basePkg := strings.TrimSuffix(pkgName, "_test")
for _, p := range pkg.typesPkg.Imports() {
if p.Name() == basePkg {
scopes = append(scopes, p.Scope())
break
}
}
}
return scopes
}
func checkExample(fn *ast.FuncDecl, pkg *Package, report reporter) {
fnName := fn.Name.Name
if params := fn.Type.Params; len(params.List) != 0 {
report("%s should be niladic", fnName)
}
if results := fn.Type.Results; results != nil && len(results.List) != 0 {
report("%s should return nothing", fnName)
}
if filesRun && !includesNonTest {
// The coherence checks between a test and the package it tests
// will report false positives if no non-test files have
// been provided.
return
}
if fnName == "Example" {
// Nothing more to do.
return
}
var (
exName = strings.TrimPrefix(fnName, "Example")
elems = strings.SplitN(exName, "_", 3)
ident = elems[0]
obj = lookup(ident, extendedScope(pkg))
)
if ident != "" && obj == nil {
// Check ExampleFoo and ExampleBadFoo.
report("%s refers to unknown identifier: %s", fnName, ident)
// Abort since obj is absent and no subsequent checks can be performed.
return
}
if len(elems) < 2 {
// Nothing more to do.
return
}
if ident == "" {
// Check Example_suffix and Example_BadSuffix.
if residual := strings.TrimPrefix(exName, "_"); !isExampleSuffix(residual) {
report("%s has malformed example suffix: %s", fnName, residual)
}
return
}
mmbr := elems[1]
if !isExampleSuffix(mmbr) {
// Check ExampleFoo_Method and ExampleFoo_BadMethod.
if obj, _, _ := types.LookupFieldOrMethod(obj.Type(), true, obj.Pkg(), mmbr); obj == nil {
report("%s refers to unknown field or method: %s.%s", fnName, ident, mmbr)
}
}
if len(elems) == 3 && !isExampleSuffix(elems[2]) {
// Check ExampleFoo_Method_suffix and ExampleFoo_Method_Badsuffix.
report("%s has malformed example suffix: %s", fnName, elems[2])
}
}
func checkTest(fn *ast.FuncDecl, prefix string, report reporter) {
// Want functions with 0 results and 1 parameter.
if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
fn.Type.Params == nil ||
len(fn.Type.Params.List) != 1 ||
len(fn.Type.Params.List[0].Names) > 1 {
return
}
// The param must look like a *testing.T or *testing.B.
if !isTestParam(fn.Type.Params.List[0].Type, prefix[:1]) {
return
}
if !isTestSuffix(fn.Name.Name[len(prefix):]) {
report("%s has malformed name: first letter after '%s' must not be lowercase", fn.Name.Name, prefix)
}
}
type reporter func(format string, args ...interface{})
// checkTestFunctions walks Test, Benchmark and Example functions checking
// malformed names, wrong signatures and examples documenting inexistent
// identifiers.
func checkTestFunctions(f *File, node ast.Node) {
if !strings.HasSuffix(f.name, "_test.go") {
return
}
fn, ok := node.(*ast.FuncDecl)
if !ok || fn.Recv != nil {
// Ignore non-functions or functions with receivers.
return
}
report := func(format string, args ...interface{}) { f.Badf(node.Pos(), format, args...) }
switch {
case strings.HasPrefix(fn.Name.Name, "Example"):
checkExample(fn, f.pkg, report)
case strings.HasPrefix(fn.Name.Name, "Test"):
checkTest(fn, "Test", report)
case strings.HasPrefix(fn.Name.Name, "Benchmark"):
checkTest(fn, "Benchmark", report)
}
}

View File

@ -130,7 +130,7 @@ func testNonceSafety(t *testing.T, c elliptic.Curve, tag string) {
}
if r0.Cmp(r1) == 0 {
t.Errorf("%s: the nonce used for two diferent messages was the same", tag)
t.Errorf("%s: the nonce used for two different messages was the same", tag)
}
}

View File

@ -46,7 +46,7 @@ loop:
BEQ aligned // aligned detected - skip copy
// Copy the unaligned source data into the aligned temporary buffer
// memove(to=4(R13), from=8(R13), n=12(R13)) - Corrupts all registers
// memmove(to=4(R13), from=8(R13), n=12(R13)) - Corrupts all registers
MOVW $buf, Rtable // to
MOVW $64, Rc0 // n
MOVM.IB [Rtable,Rdata,Rc0], (R13)

View File

@ -11,8 +11,9 @@ import "io"
// Reader is a global, shared instance of a cryptographically
// strong pseudo-random generator.
//
// On Unix-like systems, Reader reads from /dev/urandom.
// On Linux, Reader uses getrandom(2) if available, /dev/urandom otherwise.
// On OpenBSD, Reader uses getentropy(2).
// On other Unix-like systems, Reader reads from /dev/urandom.
// On Windows systems, Reader uses the CryptGenRandom API.
var Reader io.Reader

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