diff --git a/api/next.txt b/api/next.txt
index cebbe877b6..b8e09df5c2 100644
--- a/api/next.txt
+++ b/api/next.txt
@@ -1,6 +1,7 @@
pkg archive/zip, method (*Writer) SetOffset(int64)
pkg bufio, method (*Reader) Discard(int) (int, error)
pkg bufio, method (ReadWriter) Discard(int) (int, error)
+pkg bytes, func LastIndexByte([]uint8, uint8) int
pkg bytes, method (*Buffer) Cap() int
pkg bytes, method (*Reader) Size() int64
pkg crypto, type Decrypter interface { Decrypt, Public }
@@ -18,16 +19,56 @@ pkg crypto/tls, const TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 49196
pkg crypto/tls, const TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16
pkg crypto/tls, const TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 49200
pkg crypto/tls, const TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16
+pkg crypto/tls, method (*Config) SetSessionTicketKeys([][32]uint8)
+pkg crypto/tls, type Certificate struct, SignedCertificateTimestamps [][]uint8
+pkg crypto/tls, type ConnectionState struct, OCSPResponse []uint8
+pkg crypto/tls, type ConnectionState struct, SignedCertificateTimestamps [][]uint8
+pkg crypto/x509, method (*CertificateRequest) CheckSignature() error
+pkg crypto/x509, type Certificate struct, UnhandledCriticalExtensions []asn1.ObjectIdentifier
pkg crypto/x509/pkix, type Name struct, ExtraNames []AttributeTypeAndValue
pkg database/sql, method (*DB) Stats() DBStats
pkg database/sql, type DBStats struct
pkg database/sql, type DBStats struct, OpenConnections int
+pkg debug/dwarf, const ClassAddress = 1
+pkg debug/dwarf, const ClassAddress Class
+pkg debug/dwarf, const ClassBlock = 2
+pkg debug/dwarf, const ClassBlock Class
+pkg debug/dwarf, const ClassConstant = 3
+pkg debug/dwarf, const ClassConstant Class
+pkg debug/dwarf, const ClassExprLoc = 4
+pkg debug/dwarf, const ClassExprLoc Class
+pkg debug/dwarf, const ClassFlag = 5
+pkg debug/dwarf, const ClassFlag Class
+pkg debug/dwarf, const ClassLinePtr = 6
+pkg debug/dwarf, const ClassLinePtr Class
+pkg debug/dwarf, const ClassLocListPtr = 7
+pkg debug/dwarf, const ClassLocListPtr Class
+pkg debug/dwarf, const ClassMacPtr = 8
+pkg debug/dwarf, const ClassMacPtr Class
+pkg debug/dwarf, const ClassRangeListPtr = 9
+pkg debug/dwarf, const ClassRangeListPtr Class
+pkg debug/dwarf, const ClassReference = 10
+pkg debug/dwarf, const ClassReference Class
+pkg debug/dwarf, const ClassReferenceAlt = 13
+pkg debug/dwarf, const ClassReferenceAlt Class
+pkg debug/dwarf, const ClassReferenceSig = 11
+pkg debug/dwarf, const ClassReferenceSig Class
+pkg debug/dwarf, const ClassString = 12
+pkg debug/dwarf, const ClassString Class
+pkg debug/dwarf, const ClassStringAlt = 14
+pkg debug/dwarf, const ClassStringAlt Class
pkg debug/dwarf, method (*Data) LineReader(*Entry) (*LineReader, error)
+pkg debug/dwarf, method (*Entry) AttrField(Attr) *Field
pkg debug/dwarf, method (*LineReader) Next(*LineEntry) error
pkg debug/dwarf, method (*LineReader) Reset()
pkg debug/dwarf, method (*LineReader) Seek(LineReaderPos)
pkg debug/dwarf, method (*LineReader) SeekPC(uint64, *LineEntry) error
pkg debug/dwarf, method (*LineReader) Tell() LineReaderPos
+pkg debug/dwarf, method (*Reader) AddressSize() int
+pkg debug/dwarf, method (Class) GoString() string
+pkg debug/dwarf, method (Class) String() string
+pkg debug/dwarf, type Class int
+pkg debug/dwarf, type Field struct, Class Class
pkg debug/dwarf, type LineEntry struct
pkg debug/dwarf, type LineEntry struct, Address uint64
pkg debug/dwarf, type LineEntry struct, BasicBlock bool
@@ -227,48 +268,52 @@ pkg encoding/base64, var RawURLEncoding *Encoding
pkg encoding/json, type UnmarshalTypeError struct, Offset int64
pkg flag, func UnquoteUsage(*Flag) (string, string)
pkg go/ast, type EmptyStmt struct, Implicit bool
-pkg go/exact, const Bool = 1
-pkg go/exact, const Bool Kind
-pkg go/exact, const Complex = 5
-pkg go/exact, const Complex Kind
-pkg go/exact, const Float = 4
-pkg go/exact, const Float Kind
-pkg go/exact, const Int = 3
-pkg go/exact, const Int Kind
-pkg go/exact, const String = 2
-pkg go/exact, const String Kind
-pkg go/exact, const Unknown = 0
-pkg go/exact, const Unknown Kind
-pkg go/exact, func BinaryOp(Value, token.Token, Value) Value
-pkg go/exact, func BitLen(Value) int
-pkg go/exact, func BoolVal(Value) bool
-pkg go/exact, func Bytes(Value) []uint8
-pkg go/exact, func Compare(Value, token.Token, Value) bool
-pkg go/exact, func Denom(Value) Value
-pkg go/exact, func Float32Val(Value) (float32, bool)
-pkg go/exact, func Float64Val(Value) (float64, bool)
-pkg go/exact, func Imag(Value) Value
-pkg go/exact, func Int64Val(Value) (int64, bool)
-pkg go/exact, func MakeBool(bool) Value
-pkg go/exact, func MakeFloat64(float64) Value
-pkg go/exact, func MakeFromBytes([]uint8) Value
-pkg go/exact, func MakeFromLiteral(string, token.Token) Value
-pkg go/exact, func MakeImag(Value) Value
-pkg go/exact, func MakeInt64(int64) Value
-pkg go/exact, func MakeString(string) Value
-pkg go/exact, func MakeUint64(uint64) Value
-pkg go/exact, func MakeUnknown() Value
-pkg go/exact, func Num(Value) Value
-pkg go/exact, func Real(Value) Value
-pkg go/exact, func Shift(Value, token.Token, uint) Value
-pkg go/exact, func Sign(Value) int
-pkg go/exact, func StringVal(Value) string
-pkg go/exact, func Uint64Val(Value) (uint64, bool)
-pkg go/exact, func UnaryOp(token.Token, Value, int) Value
-pkg go/exact, type Kind int
-pkg go/exact, type Value interface, Kind() Kind
-pkg go/exact, type Value interface, String() string
-pkg go/exact, type Value interface, unexported methods
+pkg go/build, type Package struct, PkgTargetRoot string
+pkg go/constant, const Bool = 1
+pkg go/constant, const Bool Kind
+pkg go/constant, const Complex = 5
+pkg go/constant, const Complex Kind
+pkg go/constant, const Float = 4
+pkg go/constant, const Float Kind
+pkg go/constant, const Int = 3
+pkg go/constant, const Int Kind
+pkg go/constant, const String = 2
+pkg go/constant, const String Kind
+pkg go/constant, const Unknown = 0
+pkg go/constant, const Unknown Kind
+pkg go/constant, func BinaryOp(Value, token.Token, Value) Value
+pkg go/constant, func BitLen(Value) int
+pkg go/constant, func BoolVal(Value) bool
+pkg go/constant, func Bytes(Value) []uint8
+pkg go/constant, func Compare(Value, token.Token, Value) bool
+pkg go/constant, func Denom(Value) Value
+pkg go/constant, func Float32Val(Value) (float32, bool)
+pkg go/constant, func Float64Val(Value) (float64, bool)
+pkg go/constant, func Imag(Value) Value
+pkg go/constant, func Int64Val(Value) (int64, bool)
+pkg go/constant, func MakeBool(bool) Value
+pkg go/constant, func MakeFloat64(float64) Value
+pkg go/constant, func MakeFromBytes([]uint8) Value
+pkg go/constant, func MakeFromLiteral(string, token.Token, uint) Value
+pkg go/constant, func MakeImag(Value) Value
+pkg go/constant, func MakeInt64(int64) Value
+pkg go/constant, func MakeString(string) Value
+pkg go/constant, func MakeUint64(uint64) Value
+pkg go/constant, func MakeUnknown() Value
+pkg go/constant, func Num(Value) Value
+pkg go/constant, func Real(Value) Value
+pkg go/constant, func Shift(Value, token.Token, uint) Value
+pkg go/constant, func Sign(Value) int
+pkg go/constant, func StringVal(Value) string
+pkg go/constant, func Uint64Val(Value) (uint64, bool)
+pkg go/constant, func UnaryOp(token.Token, Value, uint) Value
+pkg go/constant, type Kind int
+pkg go/constant, type Value interface, Kind() Kind
+pkg go/constant, type Value interface, String() string
+pkg go/constant, type Value interface, unexported methods
+pkg go/importer, func Default() types.Importer
+pkg go/importer, func For(string, Lookup) types.Importer
+pkg go/importer, type Lookup func(string) (io.ReadCloser, error)
pkg go/types, const Bool = 1
pkg go/types, const Bool BasicKind
pkg go/types, const Byte = 8
@@ -376,6 +421,7 @@ pkg go/types, func New(string) Type
pkg go/types, func NewArray(Type, int64) *Array
pkg go/types, func NewChan(ChanDir, Type) *Chan
pkg go/types, func NewChecker(*Config, *token.FileSet, *Package, *Info) *Checker
+pkg go/types, func NewConst(token.Pos, *Package, string, Type, constant.Value) *Const
pkg go/types, func NewConst(token.Pos, *Package, string, Type, exact.Value) *Const
pkg go/types, func NewField(token.Pos, *Package, string, Type, bool) *Var
pkg go/types, func NewFunc(token.Pos, *Package, string, *Signature) *Func
@@ -432,6 +478,7 @@ pkg go/types, method (*Const) Pkg() *Package
pkg go/types, method (*Const) Pos() token.Pos
pkg go/types, method (*Const) String() string
pkg go/types, method (*Const) Type() Type
+pkg go/types, method (*Const) Val() constant.Value
pkg go/types, method (*Const) Val() exact.Value
pkg go/types, method (*Func) Exported() bool
pkg go/types, method (*Func) FullName() string
@@ -590,6 +637,7 @@ pkg go/types, type Config struct, Error func(error)
pkg go/types, type Config struct, FakeImportC bool
pkg go/types, type Config struct, IgnoreFuncBodies bool
pkg go/types, type Config struct, Import Importer
+pkg go/types, type Config struct, Importer Importer
pkg go/types, type Config struct, Packages map[string]*Package
pkg go/types, type Config struct, Sizes Sizes
pkg go/types, type Const struct
@@ -600,6 +648,8 @@ pkg go/types, type Error struct, Pos token.Pos
pkg go/types, type Error struct, Soft bool
pkg go/types, type Func struct
pkg go/types, type Importer func(map[string]*Package, string) (*Package, error)
+pkg go/types, type Importer interface { Import }
+pkg go/types, type Importer interface, Import(string) (*Package, error)
pkg go/types, type Info struct
pkg go/types, type Info struct, Defs map[*ast.Ident]Object
pkg go/types, type Info struct, Implicits map[ast.Node]Object
@@ -649,6 +699,7 @@ pkg go/types, type Type interface, String() string
pkg go/types, type Type interface, Underlying() Type
pkg go/types, type TypeAndValue struct
pkg go/types, type TypeAndValue struct, Type Type
+pkg go/types, type TypeAndValue struct, Value constant.Value
pkg go/types, type TypeAndValue struct, Value exact.Value
pkg go/types, type TypeName struct
pkg go/types, type Var struct
@@ -690,6 +741,18 @@ pkg image/color, type CMYK struct, K uint8
pkg image/color, type CMYK struct, M uint8
pkg image/color, type CMYK struct, Y uint8
pkg image/color, var CMYKModel Model
+pkg image/gif, const DisposalBackground = 2
+pkg image/gif, const DisposalBackground ideal-int
+pkg image/gif, const DisposalNone = 1
+pkg image/gif, const DisposalNone ideal-int
+pkg image/gif, const DisposalPrevious = 3
+pkg image/gif, const DisposalPrevious ideal-int
+pkg image/gif, type GIF struct, BackgroundIndex uint8
+pkg image/gif, type GIF struct, Config image.Config
+pkg image/gif, type GIF struct, Disposal []uint8
+pkg io, func CopyBuffer(Writer, Reader, []uint8) (int64, error)
+pkg log, const LUTC = 32
+pkg log, const LUTC ideal-int
pkg log, func Output(int, string) error
pkg log, method (*Logger) SetOutput(io.Writer)
pkg math/big, const Above = 1
@@ -716,6 +779,7 @@ pkg math/big, const ToPositiveInf = 5
pkg math/big, const ToPositiveInf RoundingMode
pkg math/big, const ToZero = 2
pkg math/big, const ToZero RoundingMode
+pkg math/big, func Jacobi(*Int, *Int) int
pkg math/big, func NewFloat(float64) *Float
pkg math/big, func ParseFloat(string, int, uint, RoundingMode) (*Float, int, error)
pkg math/big, func ScanFloat(io.ByteScanner, int, uint, RoundingMode) (*Float, int, error)
@@ -758,6 +822,7 @@ pkg math/big, method (*Float) Signbit() bool
pkg math/big, method (*Float) String() string
pkg math/big, method (*Float) Sub(*Float, *Float) *Float
pkg math/big, method (*Float) Uint64() (uint64, Accuracy)
+pkg math/big, method (*Int) ModSqrt(*Int, *Int) *Int
pkg math/big, method (Accuracy) String() string
pkg math/big, method (ErrNaN) Error() string
pkg math/big, method (RoundingMode) String() string
@@ -765,25 +830,48 @@ pkg math/big, type Accuracy int8
pkg math/big, type ErrNaN struct
pkg math/big, type Float struct
pkg math/big, type RoundingMode uint8
+pkg mime, const BEncoding = 98
+pkg mime, const BEncoding WordEncoder
+pkg mime, const QEncoding = 113
+pkg mime, const QEncoding WordEncoder
pkg mime, func ExtensionsByType(string) ([]string, error)
+pkg mime, method (*WordDecoder) Decode(string) (string, error)
+pkg mime, method (*WordDecoder) DecodeHeader(string) (string, error)
+pkg mime, method (WordEncoder) Encode(string, string) string
+pkg mime, type WordDecoder struct
+pkg mime, type WordDecoder struct, CharsetReader func(string, io.Reader) (io.Reader, error)
+pkg mime, type WordEncoder uint8
+pkg mime/quotedprintable, func NewReader(io.Reader) *Reader
pkg mime/quotedprintable, func NewReader(io.Reader) io.Reader
pkg mime/quotedprintable, func NewWriter(io.Writer) *Writer
+pkg mime/quotedprintable, method (*Reader) Read([]uint8) (int, error)
pkg mime/quotedprintable, method (*Writer) Close() error
pkg mime/quotedprintable, method (*Writer) Write([]uint8) (int, error)
+pkg mime/quotedprintable, type Reader struct
pkg mime/quotedprintable, type Writer struct
pkg mime/quotedprintable, type Writer struct, Binary bool
+pkg net, func SocketConn(*os.File, SocketAddr) (Conn, error)
+pkg net, func SocketPacketConn(*os.File, SocketAddr) (PacketConn, error)
+pkg net, type OpError struct, Source Addr
+pkg net, type SocketAddr interface { Addr, Raw }
+pkg net, type SocketAddr interface, Addr([]uint8) Addr
+pkg net, type SocketAddr interface, Raw(Addr) []uint8
pkg net/http/fcgi, var ErrConnClosed error
pkg net/http/fcgi, var ErrRequestAborted error
pkg net/http/pprof, func Trace(http.ResponseWriter, *http.Request)
pkg net/smtp, method (*Client) TLSConnectionState() (tls.ConnectionState, bool)
+pkg os, func LookupEnv(string) (string, bool)
pkg os/signal, func Ignore(...os.Signal)
pkg os/signal, func Reset(...os.Signal)
+pkg reflect, func ArrayOf(int, Type) Type
+pkg reflect, func FuncOf([]Type, []Type, bool) Type
pkg runtime, func ReadTrace() []uint8
pkg runtime, func StartTrace() error
pkg runtime, func StopTrace()
pkg runtime/pprof, func StartTrace(io.Writer) error
pkg runtime/pprof, func StopTrace()
pkg strings, func Compare(string, string) int
+pkg strings, func LastIndexByte(string, uint8) int
pkg strings, method (*Reader) Size() int64
pkg syscall (darwin-386), type SysProcAttr struct, Ctty int
pkg syscall (darwin-386), type SysProcAttr struct, Foreground bool
diff --git a/doc/effective_go.html b/doc/effective_go.html
index d6be37994b..8a827d0433 100644
--- a/doc/effective_go.html
+++ b/doc/effective_go.html
@@ -1382,7 +1382,7 @@ limit of how much data to read. Here is the signature of the
os:
-func (file *File) Read(buf []byte) (n int, err error) +func (f *File) Read(buf []byte) (n int, err error)
The method returns the number of bytes read and an error value, if diff --git a/doc/go1.5.txt b/doc/go1.5.txt index b0602f9b77..f2ceb1d56b 100644 --- a/doc/go1.5.txt +++ b/doc/go1.5.txt @@ -1,25 +1,29 @@ Overall: -toolchain in Go -new GC +- toolchain in Go +- new GC +- go tool asm, go tool compile, go tool link +- default output files changed: now file.o and a.out Language: -permit omission of key type in map composite literals where key is a composite literal (https://golang.org/cl/2591) +- permit omission of key type in map composite literals where key is a composite literal (https://golang.org/cl/2591) Build: -Go 1.4 required to build (https://golang.org/cl/2470, https://golang.org/cl/2993) +- Go 1.4 required to build (https://golang.org/cl/2470, https://golang.org/cl/2993) New Ports: -darwin/arm, a.k.a iOS. (https://golang.org/cl/2118, 2119, 3273, 2121, 2122, ..., 2127) -darwin/arm64 -linux/arm64 (cgo is supported, but only with external linking) -openbsd/arm (no cgo or external linking) -The port to Snow Leopard (OS X 10.6) is no longer actively maintained. - -Runtime: -goroutine scheduling order changed; never guaranteed by language, but can break tests that implicitly assume a specific execution order +- darwin/arm, a.k.a iOS. (https://golang.org/cl/2118, 2119, 3273, 2121, 2122, ..., 2127) +- darwin/arm64 +- linux/arm64 (cgo is supported, but only with external linking) +- openbsd/arm (no cgo or external linking) Removed Ports: -dragonfly/386 (https://golang.org/cl/7543) +- dragonfly/386 (https://golang.org/cl/7543) +- The port to Snow Leopard (OS X 10.6) is no longer actively maintained. + +Runtime: +- goroutine scheduling order changed; never guaranteed by language, + but can break tests that implicitly assume a specific execution + order API additions and behavior changes: @@ -53,9 +57,12 @@ mime: add ExtensionByType (https://golang.org/cl/7444) mime/quotedprintable: new package (https://golang.org/cl/5940 + others) net: add Source field to OpError (https://go-review.googlesource.com/9231) net: fix inconsistent errors (https://golang.org/cl/9236) +net: add SocketConn, SocketPacketConn (https://golang.org/cl/9275) +net: use Go's DNS resolver when system configuration permits (https://golang.org/cl/8945) net/http: support for setting trailers from a server Handler (https://golang.org/cl/2157) net/http: ignore the Unix epoch time in ServeContent (https://golang.org/cl/7915) net/http/cgi: fix REMOTE_ADDR, REMOTE_HOST, add REMOTE_PORT (https://golang.org/cl/4933) +net/mail: adds AddressParser type (https://golang.org/cl/10392) net/smtp: add TLSConnectionState accessor (https://golang.org/cl/2151) os: add LookupEnv (https://golang.org/cl/9791) os/signal: add Ignore and Reset (https://golang.org/cl/3580) @@ -63,6 +70,7 @@ reflect: add ArrayOf (https://golang.org/cl/4111) reflect: add FuncOf (https://golang.org/cl/1996) runtime, syscall: use SYSCALL instruction on FreeBSD (Go 1.5 now requires FreeBSD 8-STABLE+) (https://golang.org/cl/3020) runtime, syscall: use get_random_bytes syscall for NaCl (Go 1.5 now requires NaCl SDK pepper-39 or above) (https://golang.org/cl/1755) +runtime/pprof: memory profiles include overall memory statistics by default (https://golang.org/cl/9491) strings: add Compare(x, y string) int, for symmetry with bytes.Compare (https://golang.org/cl/2828) syscall: Add Foreground and Pgid to SysProcAttr (https://golang.org/cl/5130) syscall: add missing Syscall9 for darwin/amd64 (https://golang.org/cl/6555) @@ -106,6 +114,8 @@ cmd/gc: allocate backing storage for non-escaping interfaces on stack (https://g encoding/xml: avoid an allocation for tags without attributes (https://golang.org/cl/4160) image: many optimizations runtime: add ARM runtime.cmpstring and bytes.Compare (https://golang.org/cl/8010) +runtime: do not scan maps when k/v do not contain pointers (https://golang.org/cl/3288) +runtime: reduce thrashing of gs between ps (https://golang.org/cl/9872) sort: number of Sort performance optimizations (https://golang.org/cl/2100, https://golang.org/cl/2614, ...) strconv: optimize decimal to string conversion (https://golang.org/cl/2105) strconv: optimize float to string conversion (https://golang.org/cl/5600) diff --git a/doc/go_spec.html b/doc/go_spec.html index d02697bd0a..b5f18f3a02 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,24 +1,9 @@ - - -
@@ -2605,7 +2590,7 @@ one may write:
t.z // t.z
t.y // t.T1.y
-t.x // (*t.TO).x
+t.x // (*t.T0).x
p.z // (*p).z
p.y // (*p).T1.y
@@ -3305,7 +3290,7 @@ Operators combine operands into expressions.
-Expression = UnaryExpr | Expression binary_op UnaryExpr .
+Expression = UnaryExpr | Expression binary_op Expression .
UnaryExpr = PrimaryExpr | unary_op UnaryExpr .
binary_op = "||" | "&&" | rel_op | add_op | mul_op .
diff --git a/misc/android/cleaner.go b/misc/android/cleaner.go
new file mode 100644
index 0000000000..dafb162697
--- /dev/null
+++ b/misc/android/cleaner.go
@@ -0,0 +1,39 @@
+// 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.
+
+// Cleaner removes anything from /data/local/tmp/goroot not on a builtin list.
+// Used by androidtest.bash.
+package main
+
+import (
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+func main() {
+ const goroot = "/data/local/tmp/goroot"
+ expect := make(map[string]bool)
+ for _, f := range strings.Split(files, "\n") {
+ expect[filepath.Join(goroot, f)] = true
+ }
+
+ err := filepath.Walk(goroot, func(path string, info os.FileInfo, err error) error {
+ if expect[path] {
+ return nil
+ }
+ log.Printf("removing %s", path)
+ if err := os.RemoveAll(path); err != nil {
+ return err
+ }
+ if info.IsDir() {
+ return filepath.SkipDir
+ }
+ return nil
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/misc/cgo/test/cgo_linux_test.go b/misc/cgo/test/cgo_linux_test.go
index 6e1d1065f6..3cc2af5919 100644
--- a/misc/cgo/test/cgo_linux_test.go
+++ b/misc/cgo/test/cgo_linux_test.go
@@ -6,7 +6,8 @@ package cgotest
import "testing"
-func TestSetgid(t *testing.T) { testSetgid(t) }
-func Test6997(t *testing.T) { test6997(t) }
-func TestBuildID(t *testing.T) { testBuildID(t) }
-func Test9400(t *testing.T) { test9400(t) }
+func TestSetgid(t *testing.T) { testSetgid(t) }
+func Test6997(t *testing.T) { test6997(t) }
+func TestBuildID(t *testing.T) { testBuildID(t) }
+func Test9400(t *testing.T) { test9400(t) }
+func TestSigProcMask(t *testing.T) { testSigProcMask(t) }
diff --git a/misc/cgo/test/sigprocmask_linux.c b/misc/cgo/test/sigprocmask_linux.c
new file mode 100644
index 0000000000..518c533fa4
--- /dev/null
+++ b/misc/cgo/test/sigprocmask_linux.c
@@ -0,0 +1,36 @@
+// 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.
+
+#include
+#include
+#include
+#include
+#include
+
+extern void IntoGoAndBack();
+
+int CheckBlocked() {
+ sigset_t mask;
+ sigprocmask(SIG_BLOCK, NULL, &mask);
+ return sigismember(&mask, SIGIO);
+}
+
+static void* sigthreadfunc(void* unused) {
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGIO);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+ IntoGoAndBack();
+ return NULL;
+}
+
+int RunSigThread() {
+ pthread_t thread;
+ int r;
+
+ r = pthread_create(&thread, NULL, &sigthreadfunc, NULL);
+ if (r != 0)
+ return r;
+ return pthread_join(thread, NULL);
+}
diff --git a/misc/cgo/test/sigprocmask_linux.go b/misc/cgo/test/sigprocmask_linux.go
new file mode 100644
index 0000000000..7d343e92c4
--- /dev/null
+++ b/misc/cgo/test/sigprocmask_linux.go
@@ -0,0 +1,38 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+/*
+#cgo CFLAGS: -pthread
+#cgo LDFLAGS: -pthread
+extern int RunSigThread();
+extern int CheckBlocked();
+*/
+import "C"
+import (
+ "os"
+ "os/signal"
+ "syscall"
+ "testing"
+)
+
+var blocked bool
+
+//export IntoGoAndBack
+func IntoGoAndBack() {
+ // Verify that SIGIO stays blocked on the C thread
+ // even when unblocked for signal.Notify().
+ signal.Notify(make(chan os.Signal), syscall.SIGIO)
+ blocked = C.CheckBlocked() != 0
+}
+
+func testSigProcMask(t *testing.T) {
+ if r := C.RunSigThread(); r != 0 {
+ t.Error("pthread_create/pthread_join failed")
+ }
+ if !blocked {
+ t.Error("Go runtime unblocked SIGIO")
+ }
+}
diff --git a/misc/cgo/testcshared/test.bash b/misc/cgo/testcshared/test.bash
index 9862a37993..492d25e134 100755
--- a/misc/cgo/testcshared/test.bash
+++ b/misc/cgo/testcshared/test.bash
@@ -15,6 +15,14 @@ if [ ! -f src/libgo/libgo.go ]; then
fi
goos=$(go env GOOS)
+goarch=$(go env GOARCH)
+
+# Directory where cgo headers and outputs will be installed.
+# The installation directory format varies depending on the platform.
+installdir=pkg/${goos}_${goarch}_testcshared_shared
+if [ "${goos}/${goarch}" == "android/arm" ]; then
+ installdir=pkg/${goos}_${goarch}_testcshared
+fi
# Temporary directory on the android device.
androidpath=/data/local/tmp/testcshared-$$
@@ -22,9 +30,9 @@ androidpath=/data/local/tmp/testcshared-$$
function cleanup() {
rm -rf libgo.so libgo2.so libgo.h testp testp2 testp3 pkg
- rm -rf $(go env GOROOT)/pkg/$(go env GOOS)_$(go env GOARCH)_testcshared_shared
+ rm -rf $(go env GOROOT)/${installdir}
- if [ "$(go env GOOS)" == "android" ]; then
+ if [ "$goos" == "android" ]; then
adb shell rm -rf $androidpath
fi
}
@@ -38,11 +46,8 @@ function run() {
case "$goos" in
"android")
local args=$@
- for ((i=0; i < ${#args}; i++)); do
- args[$i]=${args[$i]//.\//${androidpath}\/}
- args[$i]=${args[$i]//=./=${androidpath}}
- done
- output=$(adb shell ${args} | tr -d '\r')
+ output=$(adb shell "cd ${androidpath}; $@")
+ output=$(echo $output|tr -d '\r')
case $output in
*PASS) echo "PASS";;
*) echo "$output";;
@@ -73,8 +78,9 @@ binpush libgo.so
# test0: exported symbols in shared lib are accessible.
# TODO(iant): using _shared here shouldn't really be necessary.
-$(go env CC) $(go env GOGCCFLAGS) -I pkg/$(go env GOOS)_$(go env GOARCH)_testcshared_shared -o testp main0.c libgo.so
+$(go env CC) $(go env GOGCCFLAGS) -I ${installdir} -o testp main0.c libgo.so
binpush testp
+
output=$(run LD_LIBRARY_PATH=. ./testp)
if [ "$output" != "PASS" ]; then
echo "FAIL test0 got ${output}"
diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go
new file mode 100644
index 0000000000..f7a99afce4
--- /dev/null
+++ b/misc/cgo/testshared/shared_test.go
@@ -0,0 +1,584 @@
+// 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 shared_test
+
+import (
+ "bufio"
+ "bytes"
+ "debug/elf"
+ "encoding/binary"
+ "errors"
+ "flag"
+ "fmt"
+ "go/build"
+ "io"
+ "io/ioutil"
+ "log"
+ "math/rand"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "testing"
+ "time"
+)
+
+var gopathInstallDir, gorootInstallDir, suffix string
+
+// This is the smallest set of packages we can link into a shared
+// library (runtime/cgo is built implicitly).
+var minpkgs = []string{"runtime", "sync/atomic"}
+var soname = "libruntime,sync-atomic.so"
+
+// run runs a command and calls t.Errorf if it fails.
+func run(t *testing.T, msg string, args ...string) {
+ c := exec.Command(args[0], args[1:]...)
+ if output, err := c.CombinedOutput(); err != nil {
+ t.Errorf("executing %s (%s) failed %s:\n%s", strings.Join(args, " "), msg, err, output)
+ }
+}
+
+// goCmd invokes the go tool with the installsuffix set up by TestMain. It calls
+// t.Errorf if the command fails.
+func goCmd(t *testing.T, args ...string) {
+ newargs := []string{args[0], "-installsuffix=" + suffix}
+ if testing.Verbose() {
+ newargs = append(newargs, "-v")
+ }
+ newargs = append(newargs, args[1:]...)
+ c := exec.Command("go", newargs...)
+ var output []byte
+ var err error
+ if testing.Verbose() {
+ fmt.Printf("+ go %s\n", strings.Join(newargs, " "))
+ c.Stdout = os.Stdout
+ c.Stderr = os.Stderr
+ err = c.Run()
+ } else {
+ output, err = c.CombinedOutput()
+ }
+ if err != nil {
+ if t != nil {
+ t.Errorf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, output)
+ } else {
+ log.Fatalf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, output)
+ }
+ }
+}
+
+// TestMain calls testMain so that the latter can use defer (TestMain exits with os.Exit).
+func testMain(m *testing.M) (int, error) {
+ // Because go install -buildmode=shared $standard_library_package always
+ // installs into $GOROOT, here are some gymnastics to come up with a unique
+ // installsuffix to use in this test that we can clean up afterwards.
+ myContext := build.Default
+ runtimeP, err := myContext.Import("runtime", ".", build.ImportComment)
+ if err != nil {
+ return 0, fmt.Errorf("import failed: %v", err)
+ }
+ for i := 0; i < 10000; i++ {
+ try := fmt.Sprintf("%s_%d_dynlink", runtimeP.PkgTargetRoot, rand.Int63())
+ err = os.Mkdir(try, 0700)
+ if os.IsExist(err) {
+ continue
+ }
+ if err == nil {
+ gorootInstallDir = try
+ }
+ break
+ }
+ if err != nil {
+ return 0, fmt.Errorf("can't create temporary directory: %v", err)
+ }
+ if gorootInstallDir == "" {
+ return 0, errors.New("could not create temporary directory after 10000 tries")
+ }
+ defer os.RemoveAll(gorootInstallDir)
+
+ // Some tests need to edit the source in GOPATH, so copy this directory to a
+ // temporary directory and chdir to that.
+ scratchDir, err := ioutil.TempDir("", "testshared")
+ if err != nil {
+ return 0, fmt.Errorf("TempDir failed: %v", err)
+ }
+ defer os.RemoveAll(scratchDir)
+ err = filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
+ scratchPath := filepath.Join(scratchDir, path)
+ if info.IsDir() {
+ if path == "." {
+ return nil
+ }
+ return os.Mkdir(scratchPath, info.Mode())
+ } else {
+ fromBytes, err := ioutil.ReadFile(path)
+ if err != nil {
+ return err
+ }
+ return ioutil.WriteFile(scratchPath, fromBytes, info.Mode())
+ }
+ })
+ if err != nil {
+ return 0, fmt.Errorf("walk failed: %v", err)
+ }
+ os.Setenv("GOPATH", scratchDir)
+ myContext.GOPATH = scratchDir
+ os.Chdir(scratchDir)
+
+ // All tests depend on runtime being built into a shared library. Because
+ // that takes a few seconds, do it here and have all tests use the version
+ // built here.
+ suffix = strings.Split(filepath.Base(gorootInstallDir), "_")[2]
+ goCmd(nil, append([]string{"install", "-buildmode=shared"}, minpkgs...)...)
+
+ myContext.InstallSuffix = suffix + "_dynlink"
+ depP, err := myContext.Import("dep", ".", build.ImportComment)
+ if err != nil {
+ return 0, fmt.Errorf("import failed: %v", err)
+ }
+ gopathInstallDir = depP.PkgTargetRoot
+ return m.Run(), nil
+}
+
+func TestMain(m *testing.M) {
+ flag.Parse()
+ exitCode, err := testMain(m)
+ if err != nil {
+ log.Fatal(err)
+ }
+ os.Exit(exitCode)
+}
+
+// The shared library was built at the expected location.
+func TestSOBuilt(t *testing.T) {
+ _, err := os.Stat(filepath.Join(gorootInstallDir, soname))
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// The install command should have created a "shlibname" file for the
+// listed packages (and runtime/cgo) indicating the name of the shared
+// library containing it.
+func TestShlibnameFiles(t *testing.T) {
+ pkgs := append([]string{}, minpkgs...)
+ pkgs = append(pkgs, "runtime/cgo")
+ for _, pkg := range pkgs {
+ shlibnamefile := filepath.Join(gorootInstallDir, pkg+".shlibname")
+ contentsb, err := ioutil.ReadFile(shlibnamefile)
+ if err != nil {
+ t.Errorf("error reading shlibnamefile for %s: %v", pkg, err)
+ continue
+ }
+ contents := strings.TrimSpace(string(contentsb))
+ if contents != soname {
+ t.Errorf("shlibnamefile for %s has wrong contents: %q", pkg, contents)
+ }
+ }
+}
+
+// Is a given offset into the file contained in a loaded segment?
+func isOffsetLoaded(f *elf.File, offset uint64) bool {
+ for _, prog := range f.Progs {
+ if prog.Type == elf.PT_LOAD {
+ if prog.Off <= offset && offset < prog.Off+prog.Filesz {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+func rnd(v int32, r int32) int32 {
+ if r <= 0 {
+ return v
+ }
+ v += r - 1
+ c := v % r
+ if c < 0 {
+ c += r
+ }
+ v -= c
+ return v
+}
+
+func readwithpad(r io.Reader, sz int32) ([]byte, error) {
+ data := make([]byte, rnd(sz, 4))
+ _, err := io.ReadFull(r, data)
+ if err != nil {
+ return nil, err
+ }
+ data = data[:sz]
+ return data, nil
+}
+
+type note struct {
+ name string
+ tag int32
+ desc string
+ section *elf.Section
+}
+
+// Read all notes from f. As ELF section names are not supposed to be special, one
+// looks for a particular note by scanning all SHT_NOTE sections looking for a note
+// with a particular "name" and "tag".
+func readNotes(f *elf.File) ([]*note, error) {
+ var notes []*note
+ for _, sect := range f.Sections {
+ if sect.Type != elf.SHT_NOTE {
+ continue
+ }
+ r := sect.Open()
+ for {
+ var namesize, descsize, tag int32
+ err := binary.Read(r, f.ByteOrder, &namesize)
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ return nil, fmt.Errorf("read namesize failed:", err)
+ }
+ err = binary.Read(r, f.ByteOrder, &descsize)
+ if err != nil {
+ return nil, fmt.Errorf("read descsize failed:", err)
+ }
+ err = binary.Read(r, f.ByteOrder, &tag)
+ if err != nil {
+ return nil, fmt.Errorf("read type failed:", err)
+ }
+ name, err := readwithpad(r, namesize)
+ if err != nil {
+ return nil, fmt.Errorf("read name failed:", err)
+ }
+ desc, err := readwithpad(r, descsize)
+ if err != nil {
+ return nil, fmt.Errorf("read desc failed:", err)
+ }
+ notes = append(notes, ¬e{name: string(name), tag: tag, desc: string(desc), section: sect})
+ }
+ }
+ return notes, nil
+}
+
+func dynStrings(path string, flag elf.DynTag) []string {
+ f, err := elf.Open(path)
+ defer f.Close()
+ if err != nil {
+ log.Fatal("elf.Open failed: ", err)
+ }
+ dynstrings, err := f.DynString(flag)
+ if err != nil {
+ log.Fatal("dynstring failed: ", err)
+ }
+ return dynstrings
+}
+
+func AssertIsLinkedTo(t *testing.T, path, lib string) {
+ for _, dynstring := range dynStrings(path, elf.DT_NEEDED) {
+ if dynstring == lib {
+ return
+ }
+ }
+ t.Errorf("%s is not linked to %s", path, lib)
+}
+
+func AssertHasRPath(t *testing.T, path, dir string) {
+ for _, tag := range []elf.DynTag{elf.DT_RPATH, elf.DT_RUNPATH} {
+ for _, dynstring := range dynStrings(path, tag) {
+ for _, rpath := range strings.Split(dynstring, ":") {
+ if filepath.Clean(rpath) == filepath.Clean(dir) {
+ return
+ }
+ }
+ }
+ }
+ t.Errorf("%s does not have rpath %s", path, dir)
+}
+
+// Build a trivial program that links against the shared runtime and check it runs.
+func TestTrivialExecutable(t *testing.T) {
+ goCmd(t, "install", "-linkshared", "trivial")
+ run(t, "trivial executable", "./bin/trivial")
+ AssertIsLinkedTo(t, "./bin/trivial", soname)
+ AssertHasRPath(t, "./bin/trivial", gorootInstallDir)
+}
+
+// Build a GOPATH package into a shared library that links against the goroot runtime
+// and an executable that links against both.
+func TestGOPathShlib(t *testing.T) {
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep")
+ AssertIsLinkedTo(t, filepath.Join(gopathInstallDir, "libdep.so"), soname)
+ goCmd(t, "install", "-linkshared", "exe")
+ AssertIsLinkedTo(t, "./bin/exe", soname)
+ AssertIsLinkedTo(t, "./bin/exe", "libdep.so")
+ AssertHasRPath(t, "./bin/exe", gorootInstallDir)
+ AssertHasRPath(t, "./bin/exe", gopathInstallDir)
+ // And check it runs.
+ run(t, "executable linked to GOPATH library", "./bin/exe")
+}
+
+// The shared library contains a note listing the packages it contains in a section
+// that is not mapped into memory.
+func testPkgListNote(t *testing.T, f *elf.File, note *note) {
+ if note.section.Flags != 0 {
+ t.Errorf("package list section has flags %v", note.section.Flags)
+ }
+ if isOffsetLoaded(f, note.section.Offset) {
+ t.Errorf("package list section contained in PT_LOAD segment")
+ }
+ if note.desc != "dep\n" {
+ t.Errorf("incorrect package list %q", note.desc)
+ }
+}
+
+// The shared library contains a note containing the ABI hash that is mapped into
+// memory and there is a local symbol called go.link.abihashbytes that points 16
+// bytes into it.
+func testABIHashNote(t *testing.T, f *elf.File, note *note) {
+ if note.section.Flags != elf.SHF_ALLOC {
+ t.Errorf("abi hash section has flags %v", note.section.Flags)
+ }
+ if !isOffsetLoaded(f, note.section.Offset) {
+ t.Errorf("abihash section not contained in PT_LOAD segment")
+ }
+ var hashbytes elf.Symbol
+ symbols, err := f.Symbols()
+ if err != nil {
+ t.Errorf("error reading symbols %v", err)
+ return
+ }
+ for _, sym := range symbols {
+ if sym.Name == "go.link.abihashbytes" {
+ hashbytes = sym
+ }
+ }
+ if hashbytes.Name == "" {
+ t.Errorf("no symbol called go.link.abihashbytes")
+ return
+ }
+ if elf.ST_BIND(hashbytes.Info) != elf.STB_LOCAL {
+ t.Errorf("%s has incorrect binding %v", hashbytes.Name, elf.ST_BIND(hashbytes.Info))
+ }
+ if f.Sections[hashbytes.Section] != note.section {
+ t.Errorf("%s has incorrect section %v", hashbytes.Name, f.Sections[hashbytes.Section].Name)
+ }
+ if hashbytes.Value-note.section.Addr != 16 {
+ t.Errorf("%s has incorrect offset into section %d", hashbytes.Name, hashbytes.Value-note.section.Addr)
+ }
+}
+
+// A Go shared library contains a note indicating which other Go shared libraries it
+// was linked against in an unmapped section.
+func testDepsNote(t *testing.T, f *elf.File, note *note) {
+ if note.section.Flags != 0 {
+ t.Errorf("package list section has flags %v", note.section.Flags)
+ }
+ if isOffsetLoaded(f, note.section.Offset) {
+ t.Errorf("package list section contained in PT_LOAD segment")
+ }
+ // libdep.so just links against the lib containing the runtime.
+ if note.desc != soname {
+ t.Errorf("incorrect dependency list %q", note.desc)
+ }
+}
+
+// The shared library contains notes with defined contents; see above.
+func TestNotes(t *testing.T) {
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep")
+ f, err := elf.Open(filepath.Join(gopathInstallDir, "libdep.so"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+ notes, err := readNotes(f)
+ if err != nil {
+ t.Fatal(err)
+ }
+ pkgListNoteFound := false
+ abiHashNoteFound := false
+ depsNoteFound := false
+ for _, note := range notes {
+ if note.name != "GO\x00\x00" {
+ continue
+ }
+ switch note.tag {
+ case 1: // ELF_NOTE_GOPKGLIST_TAG
+ if pkgListNoteFound {
+ t.Error("multiple package list notes")
+ }
+ testPkgListNote(t, f, note)
+ pkgListNoteFound = true
+ case 2: // ELF_NOTE_GOABIHASH_TAG
+ if abiHashNoteFound {
+ t.Error("multiple abi hash notes")
+ }
+ testABIHashNote(t, f, note)
+ abiHashNoteFound = true
+ case 3: // ELF_NOTE_GODEPS_TAG
+ if depsNoteFound {
+ t.Error("multiple abi hash notes")
+ }
+ testDepsNote(t, f, note)
+ depsNoteFound = true
+ }
+ }
+ if !pkgListNoteFound {
+ t.Error("package list note not found")
+ }
+ if !abiHashNoteFound {
+ t.Error("abi hash note not found")
+ }
+ if !depsNoteFound {
+ t.Error("deps note not found")
+ }
+}
+
+// Build a GOPATH package (dep) into a shared library that links against the goroot
+// runtime, another package (dep2) that links against the first, and and an
+// executable that links against dep2.
+func TestTwoGOPathShlibs(t *testing.T) {
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep")
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep2")
+ goCmd(t, "install", "-linkshared", "exe2")
+ run(t, "executable linked to GOPATH library", "./bin/exe2")
+}
+
+// Testing rebuilding of shared libraries when they are stale is a bit more
+// complicated that it seems like it should be. First, we make everything "old": but
+// only a few seconds old, or it might be older than 6g (or the runtime source) and
+// everything will get rebuilt. Then define a timestamp slightly newer than this
+// time, which is what we set the mtime to of a file to cause it to be seen as new,
+// and finally another slightly even newer one that we can compare files against to
+// see if they have been rebuilt.
+var oldTime = time.Now().Add(-9 * time.Second)
+var nearlyNew = time.Now().Add(-6 * time.Second)
+var stampTime = time.Now().Add(-3 * time.Second)
+
+// resetFileStamps makes "everything" (bin, src, pkg from GOPATH and the
+// test-specific parts of GOROOT) appear old.
+func resetFileStamps() {
+ chtime := func(path string, info os.FileInfo, err error) error {
+ return os.Chtimes(path, oldTime, oldTime)
+ }
+ reset := func(path string) {
+ if err := filepath.Walk(path, chtime); err != nil {
+ log.Fatalf("resetFileStamps failed: %v", err)
+ }
+
+ }
+ reset("bin")
+ reset("pkg")
+ reset("src")
+ reset(gorootInstallDir)
+}
+
+// touch makes path newer than the "old" time stamp used by resetFileStamps.
+func touch(path string) {
+ if err := os.Chtimes(path, nearlyNew, nearlyNew); err != nil {
+ log.Fatalf("os.Chtimes failed: %v", err)
+ }
+}
+
+// isNew returns if the path is newer than the time stamp used by touch.
+func isNew(path string) bool {
+ fi, err := os.Stat(path)
+ if err != nil {
+ log.Fatalf("os.Stat failed: %v", err)
+ }
+ return fi.ModTime().After(stampTime)
+}
+
+// Fail unless path has been rebuilt (i.e. is newer than the time stamp used by
+// isNew)
+func AssertRebuilt(t *testing.T, msg, path string) {
+ if !isNew(path) {
+ t.Errorf("%s was not rebuilt (%s)", msg, path)
+ }
+}
+
+// Fail if path has been rebuilt (i.e. is newer than the time stamp used by isNew)
+func AssertNotRebuilt(t *testing.T, msg, path string) {
+ if isNew(path) {
+ t.Errorf("%s was rebuilt (%s)", msg, path)
+ }
+}
+
+func TestRebuilding(t *testing.T) {
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep")
+ goCmd(t, "install", "-linkshared", "exe")
+
+ // If the source is newer than both the .a file and the .so, both are rebuilt.
+ resetFileStamps()
+ touch("src/dep/dep.go")
+ goCmd(t, "install", "-linkshared", "exe")
+ AssertRebuilt(t, "new source", filepath.Join(gopathInstallDir, "dep.a"))
+ AssertRebuilt(t, "new source", filepath.Join(gopathInstallDir, "libdep.so"))
+
+ // If the .a file is newer than the .so, the .so is rebuilt (but not the .a)
+ resetFileStamps()
+ touch(filepath.Join(gopathInstallDir, "dep.a"))
+ goCmd(t, "install", "-linkshared", "exe")
+ AssertNotRebuilt(t, "new .a file", filepath.Join(gopathInstallDir, "dep.a"))
+ AssertRebuilt(t, "new .a file", filepath.Join(gopathInstallDir, "libdep.so"))
+}
+
+func appendFile(path, content string) {
+ f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND, 0660)
+ if err != nil {
+ log.Fatalf("os.OpenFile failed: %v", err)
+ }
+ defer func() {
+ err := f.Close()
+ if err != nil {
+ log.Fatalf("f.Close failed: %v", err)
+ }
+ }()
+ _, err = f.WriteString(content)
+ if err != nil {
+ log.Fatalf("f.WriteString failed: %v", err)
+ }
+}
+
+func TestABIChecking(t *testing.T) {
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep")
+ goCmd(t, "install", "-linkshared", "exe")
+
+ // If we make an ABI-breaking change to dep and rebuild libp.so but not exe,
+ // exe will abort with a complaint on startup.
+ // This assumes adding an exported function breaks ABI, which is not true in
+ // some senses but suffices for the narrow definition of ABI compatiblity the
+ // toolchain uses today.
+ appendFile("src/dep/dep.go", "func ABIBreak() {}\n")
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep")
+ c := exec.Command("./bin/exe")
+ output, err := c.CombinedOutput()
+ if err == nil {
+ t.Fatal("executing exe did not fail after ABI break")
+ }
+ scanner := bufio.NewScanner(bytes.NewReader(output))
+ foundMsg := false
+ const wantLine = "abi mismatch detected between the executable and libdep.so"
+ for scanner.Scan() {
+ if scanner.Text() == wantLine {
+ foundMsg = true
+ break
+ }
+ }
+ if err = scanner.Err(); err != nil {
+ t.Errorf("scanner encountered error: %v", err)
+ }
+ if !foundMsg {
+ t.Fatalf("exe failed, but without line %q; got output:\n%s", wantLine, output)
+ }
+
+ // Rebuilding exe makes it work again.
+ goCmd(t, "install", "-linkshared", "exe")
+ run(t, "rebuilt exe", "./bin/exe")
+
+ // If we make a change which does not break ABI (such as adding an unexported
+ // function) and rebuild libdep.so, exe still works.
+ appendFile("src/dep/dep.go", "func noABIBreak() {}\n")
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep")
+ run(t, "after non-ABI breaking change", "./bin/exe")
+}
diff --git a/misc/cgo/testshared/src/dep/dep.go b/misc/cgo/testshared/src/dep/dep.go
index fb112cdb82..d3bed3f8ff 100644
--- a/misc/cgo/testshared/src/dep/dep.go
+++ b/misc/cgo/testshared/src/dep/dep.go
@@ -2,6 +2,12 @@ package dep
var V int = 1
+var HasMask []string = []string{"hi"}
+
+type HasProg struct {
+ array [1024]*byte
+}
+
func F() int {
return V
}
diff --git a/misc/cgo/testshared/src/dep2/dep2.go b/misc/cgo/testshared/src/dep2/dep2.go
new file mode 100644
index 0000000000..bac1086a4a
--- /dev/null
+++ b/misc/cgo/testshared/src/dep2/dep2.go
@@ -0,0 +1,11 @@
+package dep2
+
+import "dep"
+
+var W int = 1
+
+var hasProg dep.HasProg
+
+func G() int {
+ return dep.F() + 1
+}
diff --git a/misc/cgo/testshared/src/exe2/exe2.go b/misc/cgo/testshared/src/exe2/exe2.go
new file mode 100644
index 0000000000..acdb4ddcc5
--- /dev/null
+++ b/misc/cgo/testshared/src/exe2/exe2.go
@@ -0,0 +1,7 @@
+package main
+
+import "dep2"
+
+func main() {
+ dep2.W = dep2.G() + 1
+}
diff --git a/misc/cgo/testshared/test.bash b/misc/cgo/testshared/test.bash
deleted file mode 100755
index 21004adaf8..0000000000
--- a/misc/cgo/testshared/test.bash
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/usr/bin/env bash
-# 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.
-
-# Test that -buildmode=shared can produce a shared library and that
-# -linkshared can link against it to produce a working executable.
-
-set -eu
-
-export GOPATH="$(pwd)"
-
-die () {
- echo $@
- exit 1
-}
-
-# Because go install -buildmode=shared $standard_library_package always
-# installs into $GOROOT, here are some gymnastics to come up with a
-# unique installsuffix to use in this test that we can clean up
-# afterwards.
-rootdir="$(dirname $(go list -f '{{.Target}}' runtime))"
-template="${rootdir}_XXXXXXXX_dynlink"
-std_install_dir=$(mktemp -d "$template")
-
-cleanup () {
- rm -rf $std_install_dir ./bin/ ./pkg/
-}
-trap cleanup EXIT
-
-mysuffix=$(echo $std_install_dir | sed -e 's/.*_\([^_]*\)_dynlink/\1/')
-
-# This is the smallest set of packages we can link into a shared
-# library (runtime/cgo is built implicitly). Check they are built into
-# a library with the expected name.
-minpkgs="runtime sync/atomic"
-soname=libruntime,sync-atomic.so
-
-go install -installsuffix="$mysuffix" -buildmode=shared $minpkgs || die "install -buildmode=shared failed"
-
-if [ ! -f "$std_install_dir/$soname" ]; then
- echo "$std_install_dir/$soname not found!"
- exit 1
-fi
-
-# The install command should have created a "shlibname" file for the
-# listed packages (and runtime/cgo) indicating the name of the shared
-# library containing it.
-for pkg in $minpkgs runtime/cgo; do
- if [ ! -f "$std_install_dir/$pkg.shlibname" ]; then
- die "no shlibname file for $pkg"
- fi
- if [ "$(cat "$std_install_dir/$pkg.shlibname")" != "$soname" ]; then
- die "shlibname file for $pkg has wrong contents"
- fi
-done
-
-# Build a trivial program that links against the shared library we
-# just made and check it runs.
-go install -installsuffix="$mysuffix" -linkshared trivial || die "build -linkshared failed"
-./bin/trivial || die "./bin/trivial failed"
-
-# And check that it is actually dynamically linked against the library
-# we hope it is linked against.
-
-ensure_ldd () {
- a="$(ldd $1)" || die "ldd $1 failed: $a"
- { echo "$a" | grep -q "$2"; } || die "$1 does not appear to be linked against $2"
-}
-
-ensure_ldd ./bin/trivial $std_install_dir/$soname
-
-# Build a GOPATH package into a shared library that links against the above one.
-rootdir="$(dirname $(go list -installsuffix="$mysuffix" -linkshared -f '{{.Target}}' dep))"
-go install -installsuffix="$mysuffix" -buildmode=shared -linkshared dep
-ensure_ldd $rootdir/libdep.so $std_install_dir/$soname
-
-
-# And exe that links against both
-go install -installsuffix="$mysuffix" -linkshared exe
-ensure_ldd ./bin/exe $rootdir/libdep.so
-ensure_ldd ./bin/exe $std_install_dir/$soname
-
-# Now, test rebuilding of shared libraries when they are stale.
-
-will_check_rebuilt () {
- for f in $@; do cp $f $f.bak; done
-}
-
-assert_rebuilt () {
- find $1 -newer $1.bak | grep -q . || die "$1 was not rebuilt"
-}
-
-assert_not_rebuilt () {
- find $1 -newer $1.bak | grep . && die "$1 was rebuilt" || true
-}
-
-# If the source is newer than both the .a file and the .so, both are rebuilt.
-touch src/dep/dep.go
-will_check_rebuilt $rootdir/libdep.so $rootdir/dep.a
-go install -installsuffix="$mysuffix" -linkshared exe
-assert_rebuilt $rootdir/dep.a
-assert_rebuilt $rootdir/libdep.so
-
-# If the .a file is newer than the .so, the .so is rebuilt (but not the .a)
-touch $rootdir/dep.a
-will_check_rebuilt $rootdir/libdep.so $rootdir/dep.a
-go install -installsuffix="$mysuffix" -linkshared exe
-assert_not_rebuilt $rootdir/dep.a
-assert_rebuilt $rootdir/libdep.so
diff --git a/misc/nacl/testzip.proto b/misc/nacl/testzip.proto
index 1c013c1784..8e53726ea5 100644
--- a/misc/nacl/testzip.proto
+++ b/misc/nacl/testzip.proto
@@ -35,7 +35,7 @@ go src=..
gofmt_test.go
testdata
+
- link
+ newlink
testdata
+
archive
diff --git a/misc/trace/README.md b/misc/trace/README.md
index b9364de78c..775fdb8c10 100644
--- a/misc/trace/README.md
+++ b/misc/trace/README.md
@@ -1,6 +1,37 @@
-This directory contains helper file for trace viewer (go tool trace).
+This directory contains helper file for trace viewer (`go tool trace`).
-trace_viewer_lean.html was generated following instructions in:
-https://github.com/google/trace-viewer/wiki/Embedding
-on revision 895aa74558d19d91906fb720df6458244ef160c6 using:
+`trace_viewer_lean.html` was generated by following
+[instructions](https://github.com/google/trace-viewer/wiki/Embedding)
+on revision `895aa74558d19d91906fb720df6458244ef160c6` using:
+```
trace-viewer$ ./vulcanize_trace_viewer --config=lean
+```
+
+The license for trace-viewer is as follows:
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/androidtest.bash b/src/androidtest.bash
index aad1f7ec8d..39e73c350b 100755
--- a/src/androidtest.bash
+++ b/src/androidtest.bash
@@ -24,10 +24,11 @@ if [ "$GOOS" != "android" ]; then
fi
export CGO_ENABLED=1
+unset GOBIN
-# Run the build for the host bootstrap, so we can build go_android_exec.
+# Do the build first, so we can build go_android_exec and cleaner.
# Also lets us fail early before the (slow) adb push if the build is broken.
-./make.bash
+. ./make.bash --no-banner
export GOROOT=$(dirname $(pwd))
export PATH=$GOROOT/bin:$PATH
GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH go build \
@@ -50,9 +51,21 @@ cp -a "${GOROOT}/test" "${FAKE_GOROOT}/"
cp -a "${GOROOT}/lib" "${FAKE_GOROOT}/"
cp -a "${GOROOT}/pkg/android_$GOARCH" "${FAKE_GOROOT}/pkg/"
echo '# Syncing test files to android device'
+adb shell mkdir -p /data/local/tmp/goroot
time adb sync data &> /dev/null
-echo ''
-rm -rf "$ANDROID_PRODUCT_OUT"
-# Run standard build and tests.
-./all.bash --no-clean
+export CLEANER=/tmp/androidcleaner-$$
+cp ../misc/android/cleaner.go $CLEANER.go
+echo 'var files = `' >> $CLEANER.go
+(cd $ANDROID_PRODUCT_OUT/data/local/tmp/goroot; find . >> $CLEANER.go)
+echo '`' >> $CLEANER.go
+go build -o $CLEANER $CLEANER.go
+adb push $CLEANER /data/local/tmp/cleaner
+rm $CLEANER $CLEANER.go
+adb shell /data/local/tmp/cleaner
+
+rm -rf "$ANDROID_PRODUCT_OUT"
+echo ''
+
+# Run standard tests.
+bash run.bash --no-rebuild
diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go
index abd8f148a7..cd23fb57d6 100644
--- a/src/archive/tar/reader.go
+++ b/src/archive/tar/reader.go
@@ -463,6 +463,10 @@ func (tr *Reader) readHeader() *Header {
hdr.Uid = int(tr.octal(s.next(8)))
hdr.Gid = int(tr.octal(s.next(8)))
hdr.Size = tr.octal(s.next(12))
+ if hdr.Size < 0 {
+ tr.err = ErrHeader
+ return nil
+ }
hdr.ModTime = time.Unix(tr.octal(s.next(12)), 0)
s.next(8) // chksum
hdr.Typeflag = s.next(1)[0]
diff --git a/src/archive/tar/reader_test.go b/src/archive/tar/reader_test.go
index 9601ffe459..ab1e8445a4 100644
--- a/src/archive/tar/reader_test.go
+++ b/src/archive/tar/reader_test.go
@@ -741,3 +741,19 @@ func TestUninitializedRead(t *testing.T) {
}
}
+
+// Negative header size should not cause panic.
+// Issues 10959 and 10960.
+func TestNegativeHdrSize(t *testing.T) {
+ f, err := os.Open("testdata/neg-size.tar")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+ r := NewReader(f)
+ _, err = r.Next()
+ if err != ErrHeader {
+ t.Error("want ErrHeader, got", err)
+ }
+ io.Copy(ioutil.Discard, r)
+}
diff --git a/src/archive/tar/testdata/neg-size.tar b/src/archive/tar/testdata/neg-size.tar
new file mode 100644
index 0000000000..5deea3d05c
Binary files /dev/null and b/src/archive/tar/testdata/neg-size.tar differ
diff --git a/src/archive/zip/reader.go b/src/archive/zip/reader.go
index 8136b840d4..f68ab09723 100644
--- a/src/archive/zip/reader.go
+++ b/src/archive/zip/reader.go
@@ -8,6 +8,7 @@ import (
"bufio"
"encoding/binary"
"errors"
+ "fmt"
"hash"
"hash/crc32"
"io"
@@ -77,6 +78,9 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
if err != nil {
return err
}
+ if end.directoryRecords > uint64(size)/fileHeaderLen {
+ return fmt.Errorf("archive/zip: TOC declares impossible %d files in %d byte zip", end.directoryRecords, size)
+ }
z.r = r
z.File = make([]*File, 0, end.directoryRecords)
z.Comment = end.comment
@@ -146,16 +150,22 @@ func (f *File) Open() (rc io.ReadCloser, err error) {
if f.hasDataDescriptor() {
desr = io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, dataDescriptorLen)
}
- rc = &checksumReader{rc, crc32.NewIEEE(), f, desr, nil}
+ rc = &checksumReader{
+ rc: rc,
+ hash: crc32.NewIEEE(),
+ f: f,
+ desr: desr,
+ }
return
}
type checksumReader struct {
- rc io.ReadCloser
- hash hash.Hash32
- f *File
- desr io.Reader // if non-nil, where to read the data descriptor
- err error // sticky error
+ rc io.ReadCloser
+ hash hash.Hash32
+ nread uint64 // number of bytes read so far
+ f *File
+ desr io.Reader // if non-nil, where to read the data descriptor
+ err error // sticky error
}
func (r *checksumReader) Read(b []byte) (n int, err error) {
@@ -164,10 +174,14 @@ func (r *checksumReader) Read(b []byte) (n int, err error) {
}
n, err = r.rc.Read(b)
r.hash.Write(b[:n])
+ r.nread += uint64(n)
if err == nil {
return
}
if err == io.EOF {
+ if r.nread != r.f.UncompressedSize64 {
+ return 0, io.ErrUnexpectedEOF
+ }
if r.desr != nil {
if err1 := readDataDescriptor(r.desr, r.f); err1 != nil {
err = err1
diff --git a/src/archive/zip/reader_test.go b/src/archive/zip/reader_test.go
index 29d0652dcc..4806b89458 100644
--- a/src/archive/zip/reader_test.go
+++ b/src/archive/zip/reader_test.go
@@ -531,3 +531,54 @@ func TestIssue8186(t *testing.T) {
}
}
}
+
+// Verify we return ErrUnexpectedEOF when length is short.
+func TestIssue10957(t *testing.T) {
+ data := []byte("PK\x03\x040000000PK\x01\x0200000" +
+ "0000000000000000000\x00" +
+ "\x00\x00\x00\x00\x00000000000000PK\x01" +
+ "\x020000000000000000000" +
+ "00000\v\x00\x00\x00\x00\x00000000000" +
+ "00000000000000PK\x01\x0200" +
+ "00000000000000000000" +
+ "00\v\x00\x00\x00\x00\x00000000000000" +
+ "00000000000PK\x01\x020000<" +
+ "0\x00\x0000000000000000\v\x00\v" +
+ "\x00\x00\x00\x00\x0000000000\x00\x00\x00\x00000" +
+ "00000000PK\x01\x0200000000" +
+ "0000000000000000\v\x00\x00\x00" +
+ "\x00\x0000PK\x05\x06000000\x05\x000000" +
+ "\v\x00\x00\x00\x00\x00")
+ z, err := NewReader(bytes.NewReader(data), int64(len(data)))
+ if err != nil {
+ t.Fatal(err)
+ }
+ for i, f := range z.File {
+ r, err := f.Open()
+ if err != nil {
+ continue
+ }
+ if f.UncompressedSize64 < 1e6 {
+ n, err := io.Copy(ioutil.Discard, r)
+ if i == 3 && err != io.ErrUnexpectedEOF {
+ t.Errorf("File[3] error = %v; want io.ErrUnexpectedEOF", err)
+ }
+ if err == nil && uint64(n) != f.UncompressedSize64 {
+ t.Errorf("file %d: bad size: copied=%d; want=%d", i, n, f.UncompressedSize64)
+ }
+ }
+ r.Close()
+ }
+}
+
+// Verify the number of files is sane.
+func TestIssue10956(t *testing.T) {
+ data := []byte("PK\x06\x06PK\x06\a0000\x00\x00\x00\x00\x00\x00\x00\x00" +
+ "0000PK\x05\x06000000000000" +
+ "0000\v\x00000\x00\x00\x00\x00\x00\x00\x000")
+ _, err := NewReader(bytes.NewReader(data), int64(len(data)))
+ const want = "TOC declares impossible 3472328296227680304 files in 57 byte"
+ if err == nil && !strings.Contains(err.Error(), want) {
+ t.Errorf("error = %v; want %q", err, want)
+ }
+}
diff --git a/src/buildall.bash b/src/buildall.bash
index a07529e733..ba23d31a50 100755
--- a/src/buildall.bash
+++ b/src/buildall.bash
@@ -36,7 +36,7 @@ fi
targets="$((ls runtime | sed -n 's/^rt0_\(.*\)_\(.*\)\.s/\1-\2/p'; echo linux-386-387 linux-arm-arm5) | sort | 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
+./make.bash || exit 1
GOROOT="$(cd .. && pwd)"
failed=false
diff --git a/src/cmd/6l/z.go b/src/cmd/6l/z.go
deleted file mode 100644
index 06ab7d0f9a..0000000000
--- a/src/cmd/6l/z.go
+++ /dev/null
@@ -1 +0,0 @@
-package main
diff --git a/src/cmd/asm/internal/asm/asm.go b/src/cmd/asm/internal/asm/asm.go
index 725c6352cb..d5d2772ef3 100644
--- a/src/cmd/asm/internal/asm/asm.go
+++ b/src/cmd/asm/internal/asm/asm.go
@@ -493,7 +493,10 @@ func (p *Parser) asmInstruction(op int, cond string, a []obj.Addr) {
if arch.IsARM64STLXR(op) {
prog.From = a[0]
prog.To = a[1]
- prog.To2 = a[2]
+ if a[2].Type != obj.TYPE_REG {
+ p.errorf("invalid addressing modes for third operand to %s instruction, must be register", obj.Aconv(op))
+ }
+ prog.RegTo2 = a[2].Reg
break
}
prog.From = a[0]
diff --git a/src/cmd/asm/internal/flags/flags.go b/src/cmd/asm/internal/flags/flags.go
index c74f26974a..bf5cb1eef3 100644
--- a/src/cmd/asm/internal/flags/flags.go
+++ b/src/cmd/asm/internal/flags/flags.go
@@ -51,7 +51,7 @@ func Usage() {
os.Exit(2)
}
-func Parse(theChar int) {
+func Parse() {
flag.Usage = Usage
flag.Parse()
if flag.NArg() != 1 {
@@ -64,6 +64,6 @@ func Parse(theChar int) {
if strings.HasSuffix(input, ".s") {
input = input[:len(input)-2]
}
- *OutputFile = fmt.Sprintf("%s.%c", input, theChar)
+ *OutputFile = fmt.Sprintf("%s.o", input)
}
}
diff --git a/src/cmd/asm/internal/lex/input.go b/src/cmd/asm/internal/lex/input.go
index 730042b149..7e495b8edf 100644
--- a/src/cmd/asm/internal/lex/input.go
+++ b/src/cmd/asm/internal/lex/input.go
@@ -13,7 +13,6 @@ import (
"text/scanner"
"cmd/asm/internal/flags"
- "cmd/internal/obj"
)
// Input is the main input: a stack of readers and some macro definitions.
@@ -436,7 +435,7 @@ func (in *Input) line() {
if tok != '\n' {
in.Error("unexpected token at end of #line: ", tok)
}
- obj.Linklinehist(linkCtxt, histLine, file, line)
+ linkCtxt.LineHist.Update(histLine, file, line)
in.Stack.SetPos(line, file)
}
diff --git a/src/cmd/asm/internal/lex/tokenizer.go b/src/cmd/asm/internal/lex/tokenizer.go
index 28a4b85253..6a4d95491f 100644
--- a/src/cmd/asm/internal/lex/tokenizer.go
+++ b/src/cmd/asm/internal/lex/tokenizer.go
@@ -10,8 +10,6 @@ import (
"strings"
"text/scanner"
"unicode"
-
- "cmd/internal/obj"
)
// A Tokenizer is a simple wrapping of text/scanner.Scanner, configured
@@ -40,7 +38,7 @@ func NewTokenizer(name string, r io.Reader, file *os.File) *Tokenizer {
s.Position.Filename = name
s.IsIdentRune = isIdentRune
if file != nil {
- obj.Linklinehist(linkCtxt, histLine, name, 0)
+ linkCtxt.LineHist.Push(histLine, name)
}
return &Tokenizer{
s: &s,
@@ -149,6 +147,6 @@ func (t *Tokenizer) Close() {
if t.file != nil {
t.file.Close()
// It's an open file, so pop the line history.
- obj.Linklinehist(linkCtxt, histLine, "", 0)
+ linkCtxt.LineHist.Pop(histLine)
}
}
diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go
index 32bdee6624..db0e28e2e5 100644
--- a/src/cmd/asm/main.go
+++ b/src/cmd/asm/main.go
@@ -29,7 +29,7 @@ func main() {
log.Fatalf("asm: unrecognized architecture %s", GOARCH)
}
- flags.Parse(architecture.Thechar)
+ flags.Parse()
// Create object file, write header.
fd, err := os.Create(*flags.OutputFile)
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index 30f828c4e9..87f21ed822 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -846,6 +846,8 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprint(fgo2, "}\n")
}
}
+
+ fmt.Fprintf(fgcch, "%s", gccExportHeaderEpilog)
}
// Write out the C header allowing C code to call exported gccgo functions.
@@ -1009,6 +1011,8 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprint(fgo2, ")\n")
fmt.Fprint(fgo2, "}\n")
}
+
+ fmt.Fprintf(fgcch, "%s", gccExportHeaderEpilog)
}
// writeExportHeader writes out the start of the _cgo_export.h file.
@@ -1374,6 +1378,17 @@ typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+`
+
+// gccExportHeaderEpilog goes at the end of the generated header file.
+const gccExportHeaderEpilog = `
+#ifdef __cplusplus
+}
+#endif
`
// gccgoExportFileProlog is written to the _cgo_export.c file when
diff --git a/src/cmd/6g/cgen.go b/src/cmd/compile/internal/amd64/cgen.go
similarity index 98%
rename from src/cmd/6g/cgen.go
rename to src/cmd/compile/internal/amd64/cgen.go
index 23e2d1b57f..71f8f88322 100644
--- a/src/cmd/6g/cgen.go
+++ b/src/cmd/compile/internal/amd64/cgen.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package amd64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/x86"
)
diff --git a/src/cmd/6g/galign.go b/src/cmd/compile/internal/amd64/galign.go
similarity index 97%
rename from src/cmd/6g/galign.go
rename to src/cmd/compile/internal/amd64/galign.go
index 0ca87537ff..79bf94a075 100644
--- a/src/cmd/6g/galign.go
+++ b/src/cmd/compile/internal/amd64/galign.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package amd64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/x86"
)
@@ -65,7 +65,7 @@ func betypeinit() {
}
}
-func main() {
+func Main() {
if obj.Getgoos() == "nacl" {
resvd = append(resvd, x86.REG_BP, x86.REG_R15)
} else if obj.Framepointer_enabled != 0 {
@@ -101,6 +101,7 @@ func main() {
gc.Thearch.Getg = getg
gc.Thearch.Gins = gins
gc.Thearch.Ginsboolval = ginsboolval
+ gc.Thearch.Ginscmp = ginscmp
gc.Thearch.Ginscon = ginscon
gc.Thearch.Ginsnop = ginsnop
gc.Thearch.Gmove = gmove
diff --git a/src/cmd/6g/ggen.go b/src/cmd/compile/internal/amd64/ggen.go
similarity index 97%
rename from src/cmd/6g/ggen.go
rename to src/cmd/compile/internal/amd64/ggen.go
index 6e5e6bc4ca..6425633818 100644
--- a/src/cmd/6g/ggen.go
+++ b/src/cmd/compile/internal/amd64/ggen.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package amd64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/x86"
)
@@ -32,7 +32,7 @@ func defframe(ptxt *obj.Prog) {
// iterate through declarations - they are sorted in decreasing xoffset order.
for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
n = l.N
- if !n.Needzero {
+ if !n.Name.Needzero {
continue
}
if n.Class != gc.PAUTO {
@@ -190,9 +190,9 @@ func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) {
check := 0
if gc.Issigned[t.Etype] {
check = 1
- if gc.Isconst(nl, gc.CTINT) && gc.Mpgetfix(nl.Val.U.Xval) != -(1<= uint64(nl.Type.Width*8) {
// large shift gets 2 shifts by width-1
var n3 gc.Node
diff --git a/src/cmd/6g/gsubr.go b/src/cmd/compile/internal/amd64/gsubr.go
similarity index 95%
rename from src/cmd/6g/gsubr.go
rename to src/cmd/compile/internal/amd64/gsubr.go
index 53d0f038d9..a8e4170bee 100644
--- a/src/cmd/6g/gsubr.go
+++ b/src/cmd/compile/internal/amd64/gsubr.go
@@ -28,10 +28,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package amd64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/big"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/x86"
"fmt"
@@ -99,33 +100,67 @@ func ginscon(as int, c int64, n2 *gc.Node) {
gins(as, &n1, n2)
}
+func ginscmp(op int, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
+ if gc.Isint[t.Etype] && n1.Op == gc.OLITERAL && gc.Smallintconst(n1) && n2.Op != gc.OLITERAL {
+ // Reverse comparison to place constant last.
+ op = gc.Brrev(op)
+ n1, n2 = n2, n1
+ }
+ // General case.
+ var r1, r2, g1, g2 gc.Node
+ if n1.Op == gc.ONAME && n1.Class&gc.PHEAP == 0 || n1.Op == gc.OINDREG {
+ r1 = *n1
+ } else {
+ gc.Regalloc(&r1, t, n1)
+ gc.Regalloc(&g1, n1.Type, &r1)
+ gc.Cgen(n1, &g1)
+ gmove(&g1, &r1)
+ }
+ if n2.Op == gc.OLITERAL && gc.Isint[t.Etype] && gc.Smallintconst(n2) {
+ r2 = *n2
+ } else {
+ gc.Regalloc(&r2, t, n2)
+ gc.Regalloc(&g2, n1.Type, &r2)
+ gc.Cgen(n2, &g2)
+ gmove(&g2, &r2)
+ }
+ gins(optoas(gc.OCMP, t), &r1, &r2)
+ if r1.Op == gc.OREGISTER {
+ gc.Regfree(&g1)
+ gc.Regfree(&r1)
+ }
+ if r2.Op == gc.OREGISTER {
+ gc.Regfree(&g2)
+ gc.Regfree(&r2)
+ }
+ return gc.Gbranch(optoas(op, t), nil, likely)
+}
+
func ginsboolval(a int, n *gc.Node) {
gins(jmptoset(a), nil, n)
}
-/*
- * set up nodes representing 2^63
- */
-var bigi gc.Node
-
-var bigf gc.Node
-
-var bignodes_did int
+// set up nodes representing 2^63
+var (
+ bigi gc.Node
+ bigf gc.Node
+ bignodes_did bool
+)
func bignodes() {
- if bignodes_did != 0 {
+ if bignodes_did {
return
}
- bignodes_did = 1
+ bignodes_did = true
- gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 1)
- gc.Mpshiftfix(bigi.Val.U.Xval, 63)
+ var i big.Int
+ i.SetInt64(1)
+ i.Lsh(&i, 63)
- bigf = bigi
- bigf.Type = gc.Types[gc.TFLOAT64]
- bigf.Val.Ctype = gc.CTFLT
- bigf.Val.U.Fval = new(gc.Mpflt)
- gc.Mpmovefixflt(bigf.Val.U.Fval, bigi.Val.U.Xval)
+ gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0)
+ bigi.SetBigInt(&i)
+
+ bigi.Convconst(&bigf, gc.Types[gc.TFLOAT64])
}
/*
@@ -156,7 +191,7 @@ func gmove(f *gc.Node, t *gc.Node) {
// convert constant to desired type
if f.Op == gc.OLITERAL {
var con gc.Node
- gc.Convconst(&con, t.Type, &f.Val)
+ f.Convconst(&con, t.Type)
f = &con
ft = tt // so big switch will choose a simple mov
@@ -170,10 +205,7 @@ func gmove(f *gc.Node, t *gc.Node) {
// 64-bit immediates are really 32-bit sign-extended
// unless moving into a register.
if gc.Isint[tt] {
- if gc.Mpcmpfixfix(con.Val.U.Xval, gc.Minintval[gc.TINT32]) < 0 {
- goto hard
- }
- if gc.Mpcmpfixfix(con.Val.U.Xval, gc.Maxintval[gc.TINT32]) > 0 {
+ if i := con.Int(); int64(int32(i)) != i {
goto hard
}
}
@@ -1237,7 +1269,7 @@ func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool {
if !gc.Isconst(n, gc.CTINT) {
break
}
- v := gc.Mpgetfix(n.Val.U.Xval)
+ v := n.Int()
if v >= 32000 || v <= -32000 {
break
}
diff --git a/src/cmd/6g/peep.go b/src/cmd/compile/internal/amd64/peep.go
similarity index 99%
rename from src/cmd/6g/peep.go
rename to src/cmd/compile/internal/amd64/peep.go
index cd07199ed1..19db68e944 100644
--- a/src/cmd/6g/peep.go
+++ b/src/cmd/compile/internal/amd64/peep.go
@@ -28,10 +28,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package amd64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/x86"
"fmt"
diff --git a/src/cmd/6g/prog.go b/src/cmd/compile/internal/amd64/prog.go
similarity index 99%
rename from src/cmd/6g/prog.go
rename to src/cmd/compile/internal/amd64/prog.go
index 5f604742c3..00918c8691 100644
--- a/src/cmd/6g/prog.go
+++ b/src/cmd/compile/internal/amd64/prog.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package amd64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/x86"
)
diff --git a/src/cmd/6g/reg.go b/src/cmd/compile/internal/amd64/reg.go
similarity index 98%
rename from src/cmd/6g/reg.go
rename to src/cmd/compile/internal/amd64/reg.go
index cab07b5b4e..7d4f40641d 100644
--- a/src/cmd/6g/reg.go
+++ b/src/cmd/compile/internal/amd64/reg.go
@@ -28,10 +28,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package amd64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/x86"
)
diff --git a/src/cmd/5g/cgen.go b/src/cmd/compile/internal/arm/cgen.go
similarity index 90%
rename from src/cmd/5g/cgen.go
rename to src/cmd/compile/internal/arm/cgen.go
index 2e922391cb..8ea6c5f3f2 100644
--- a/src/cmd/5g/cgen.go
+++ b/src/cmd/compile/internal/arm/cgen.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package arm
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/arm"
)
@@ -53,28 +53,6 @@ func igenindex(n *gc.Node, res *gc.Node, bounded bool) *obj.Prog {
return cgenindex(n, res, bounded)
}
-func gencmp0(n *gc.Node, t *gc.Type, o int, likely int, to *obj.Prog) {
- var n1 gc.Node
-
- gc.Regalloc(&n1, t, nil)
- gc.Cgen(n, &n1)
- a := optoas(gc.OCMP, t)
- if a != arm.ACMP {
- var n2 gc.Node
- gc.Nodconst(&n2, t, 0)
- var n3 gc.Node
- gc.Regalloc(&n3, t, nil)
- gmove(&n2, &n3)
- gins(a, &n1, &n3)
- gc.Regfree(&n3)
- } else {
- gins(arm.ATST, &n1, nil)
- }
- a = optoas(o, t)
- gc.Patch(gc.Gbranch(a, t, likely), to)
- gc.Regfree(&n1)
-}
-
func blockcopy(n, res *gc.Node, osrc, odst, w int64) {
// determine alignment.
// want to avoid unaligned access, so have to use
diff --git a/src/cmd/5g/cgen64.go b/src/cmd/compile/internal/arm/cgen64.go
similarity index 99%
rename from src/cmd/5g/cgen64.go
rename to src/cmd/compile/internal/arm/cgen64.go
index 699e555f71..6c88b76e20 100644
--- a/src/cmd/5g/cgen64.go
+++ b/src/cmd/compile/internal/arm/cgen64.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package arm
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/arm"
)
@@ -237,7 +237,7 @@ func cgen64(n *gc.Node, res *gc.Node) {
// shld hi:lo, c
// shld lo:t, c
case gc.OLROT:
- v := uint64(gc.Mpgetfix(r.Val.U.Xval))
+ v := uint64(r.Int())
var bl gc.Node
gc.Regalloc(&bl, lo1.Type, nil)
@@ -291,7 +291,7 @@ func cgen64(n *gc.Node, res *gc.Node) {
var p4 *obj.Prog
var p5 *obj.Prog
if r.Op == gc.OLITERAL {
- v := uint64(gc.Mpgetfix(r.Val.U.Xval))
+ v := uint64(r.Int())
if v >= 64 {
// TODO(kaib): replace with gins(AMOVW, nodintconst(0), &al)
// here and below (verify it optimizes to EOR)
@@ -452,7 +452,7 @@ func cgen64(n *gc.Node, res *gc.Node) {
var creg gc.Node
var p3 *obj.Prog
if r.Op == gc.OLITERAL {
- v := uint64(gc.Mpgetfix(r.Val.U.Xval))
+ v := uint64(r.Int())
if v >= 64 {
if bh.Type.Etype == gc.TINT32 {
// MOVW bh->31, al
diff --git a/src/cmd/5g/galign.go b/src/cmd/compile/internal/arm/galign.go
similarity index 96%
rename from src/cmd/5g/galign.go
rename to src/cmd/compile/internal/arm/galign.go
index 3c8ba519eb..60a39d3fe4 100644
--- a/src/cmd/5g/galign.go
+++ b/src/cmd/compile/internal/arm/galign.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package arm
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/arm"
)
@@ -37,7 +37,7 @@ func betypeinit() {
gc.Widthreg = 4
}
-func main() {
+func Main() {
gc.Thearch.Thechar = thechar
gc.Thearch.Thestring = thestring
gc.Thearch.Thelinkarch = thelinkarch
@@ -65,6 +65,7 @@ func main() {
gc.Thearch.Expandchecks = expandchecks
gc.Thearch.Getg = getg
gc.Thearch.Gins = gins
+ gc.Thearch.Ginscmp = ginscmp
gc.Thearch.Ginscon = ginscon
gc.Thearch.Ginsnop = ginsnop
gc.Thearch.Gmove = gmove
diff --git a/src/cmd/5g/ggen.go b/src/cmd/compile/internal/arm/ggen.go
similarity index 93%
rename from src/cmd/5g/ggen.go
rename to src/cmd/compile/internal/arm/ggen.go
index 0cf0d9299c..6633351032 100644
--- a/src/cmd/5g/ggen.go
+++ b/src/cmd/compile/internal/arm/ggen.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package arm
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/arm"
)
@@ -30,7 +30,7 @@ func defframe(ptxt *obj.Prog) {
r0 := uint32(0)
for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
n = l.N
- if !n.Needzero {
+ if !n.Name.Needzero {
continue
}
if n.Class != gc.PAUTO {
@@ -183,7 +183,7 @@ func cgen_shift(op int, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) {
w := int(nl.Type.Width * 8)
if op == gc.OLROT {
- v := int(gc.Mpgetfix(nr.Val.U.Xval))
+ v := nr.Int()
var n1 gc.Node
gc.Regalloc(&n1, nl.Type, res)
if w == 32 {
@@ -210,7 +210,7 @@ func cgen_shift(op int, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) {
var n1 gc.Node
gc.Regalloc(&n1, nl.Type, res)
gc.Cgen(nl, &n1)
- sc := uint64(gc.Mpgetfix(nr.Val.U.Xval))
+ sc := uint64(nr.Int())
if sc == 0 {
} else // nothing to do
if sc >= uint64(nl.Type.Width*8) {
@@ -479,6 +479,32 @@ func ginscon(as int, c int64, n *gc.Node) {
gc.Regfree(&n2)
}
+func ginscmp(op int, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
+ if gc.Isint[t.Etype] && n1.Op == gc.OLITERAL && n1.Int() == 0 && n2.Op != gc.OLITERAL {
+ op = gc.Brrev(op)
+ n1, n2 = n2, n1
+ }
+ var r1, r2, g1, g2 gc.Node
+ gc.Regalloc(&r1, t, n1)
+ gc.Regalloc(&g1, n1.Type, &r1)
+ gc.Cgen(n1, &g1)
+ gmove(&g1, &r1)
+ if gc.Isint[t.Etype] && n2.Op == gc.OLITERAL && n2.Int() == 0 {
+ gins(arm.ACMP, &r1, n2)
+ } else {
+ gc.Regalloc(&r2, t, n2)
+ gc.Regalloc(&g2, n1.Type, &r2)
+ gc.Cgen(n2, &g2)
+ gmove(&g2, &r2)
+ gins(optoas(gc.OCMP, t), &r1, &r2)
+ gc.Regfree(&g2)
+ gc.Regfree(&r2)
+ }
+ gc.Regfree(&g1)
+ gc.Regfree(&r1)
+ return gc.Gbranch(optoas(op, t), nil, likely)
+}
+
// addr += index*width if possible.
func addindex(index *gc.Node, width int64, addr *gc.Node) bool {
switch width {
diff --git a/src/cmd/5g/gsubr.go b/src/cmd/compile/internal/arm/gsubr.go
similarity index 98%
rename from src/cmd/5g/gsubr.go
rename to src/cmd/compile/internal/arm/gsubr.go
index 57d511e6f6..5263f15ac2 100644
--- a/src/cmd/5g/gsubr.go
+++ b/src/cmd/compile/internal/arm/gsubr.go
@@ -28,10 +28,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package arm
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/arm"
"fmt"
@@ -53,7 +53,7 @@ func ncon(i uint32) *gc.Node {
if ncon_n.Type == nil {
gc.Nodconst(&ncon_n, gc.Types[gc.TUINT32], 0)
}
- gc.Mpmovecfix(ncon_n.Val.U.Xval, int64(i))
+ ncon_n.SetInt(int64(i))
return &ncon_n
}
@@ -89,7 +89,7 @@ func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) {
case gc.ONAME:
if n.Class == gc.PPARAMREF {
var n1 gc.Node
- gc.Cgen(n.Heapaddr, &n1)
+ gc.Cgen(n.Name.Heapaddr, &n1)
sclean[nsclean-1] = n1
n = &n1
}
@@ -111,8 +111,8 @@ func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) {
case gc.OLITERAL:
var n1 gc.Node
- gc.Convconst(&n1, n.Type, &n.Val)
- i := gc.Mpgetfix(n1.Val.U.Xval)
+ n.Convconst(&n1, n.Type)
+ i := n1.Int()
gc.Nodconst(lo, gc.Types[gc.TUINT32], int64(uint32(i)))
i >>= 32
if n.Type.Etype == gc.TINT64 {
@@ -160,12 +160,12 @@ func gmove(f *gc.Node, t *gc.Node) {
var con gc.Node
switch tt {
default:
- gc.Convconst(&con, t.Type, &f.Val)
+ f.Convconst(&con, t.Type)
case gc.TINT16,
gc.TINT8:
var con gc.Node
- gc.Convconst(&con, gc.Types[gc.TINT32], &f.Val)
+ f.Convconst(&con, gc.Types[gc.TINT32])
var r1 gc.Node
gc.Regalloc(&r1, con.Type, t)
gins(arm.AMOVW, &con, &r1)
@@ -176,7 +176,7 @@ func gmove(f *gc.Node, t *gc.Node) {
case gc.TUINT16,
gc.TUINT8:
var con gc.Node
- gc.Convconst(&con, gc.Types[gc.TUINT32], &f.Val)
+ f.Convconst(&con, gc.Types[gc.TUINT32])
var r1 gc.Node
gc.Regalloc(&r1, con.Type, t)
gins(arm.AMOVW, &con, &r1)
@@ -1118,7 +1118,7 @@ func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool {
if !gc.Isconst(n, gc.CTINT) {
break
}
- v := gc.Mpgetfix(n.Val.U.Xval)
+ v := n.Int()
if v >= 32000 || v <= -32000 {
break
}
diff --git a/src/cmd/5g/peep.go b/src/cmd/compile/internal/arm/peep.go
similarity index 99%
rename from src/cmd/5g/peep.go
rename to src/cmd/compile/internal/arm/peep.go
index b76719d74e..66eba417c0 100644
--- a/src/cmd/5g/peep.go
+++ b/src/cmd/compile/internal/arm/peep.go
@@ -28,10 +28,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package arm
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/arm"
"fmt"
diff --git a/src/cmd/5g/prog.go b/src/cmd/compile/internal/arm/prog.go
similarity index 99%
rename from src/cmd/5g/prog.go
rename to src/cmd/compile/internal/arm/prog.go
index c472cdf042..cdf9d29192 100644
--- a/src/cmd/5g/prog.go
+++ b/src/cmd/compile/internal/arm/prog.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package arm
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/arm"
)
diff --git a/src/cmd/5g/reg.go b/src/cmd/compile/internal/arm/reg.go
similarity index 98%
rename from src/cmd/5g/reg.go
rename to src/cmd/compile/internal/arm/reg.go
index 2afdf12416..b72ccc9815 100644
--- a/src/cmd/5g/reg.go
+++ b/src/cmd/compile/internal/arm/reg.go
@@ -28,10 +28,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package arm
import "cmd/internal/obj/arm"
-import "cmd/internal/gc"
+import "cmd/compile/internal/gc"
const (
NREGVAR = 32
diff --git a/src/cmd/7g/cgen.go b/src/cmd/compile/internal/arm64/cgen.go
similarity index 98%
rename from src/cmd/7g/cgen.go
rename to src/cmd/compile/internal/arm64/cgen.go
index 6f268b4185..30326d73e2 100644
--- a/src/cmd/7g/cgen.go
+++ b/src/cmd/compile/internal/arm64/cgen.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package arm64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/arm64"
)
diff --git a/src/cmd/7g/galign.go b/src/cmd/compile/internal/arm64/galign.go
similarity index 96%
rename from src/cmd/7g/galign.go
rename to src/cmd/compile/internal/arm64/galign.go
index 34b4ab6142..38def8f5a4 100644
--- a/src/cmd/7g/galign.go
+++ b/src/cmd/compile/internal/arm64/galign.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package arm64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/arm64"
)
@@ -37,7 +37,7 @@ func betypeinit() {
gc.Widthreg = 8
}
-func main() {
+func Main() {
gc.Thearch.Thechar = thechar
gc.Thearch.Thestring = thestring
gc.Thearch.Thelinkarch = thelinkarch
@@ -65,6 +65,7 @@ func main() {
gc.Thearch.Expandchecks = expandchecks
gc.Thearch.Getg = getg
gc.Thearch.Gins = gins
+ gc.Thearch.Ginscmp = ginscmp
gc.Thearch.Ginscon = ginscon
gc.Thearch.Ginsnop = ginsnop
gc.Thearch.Gmove = gmove
diff --git a/src/cmd/7g/ggen.go b/src/cmd/compile/internal/arm64/ggen.go
similarity index 97%
rename from src/cmd/7g/ggen.go
rename to src/cmd/compile/internal/arm64/ggen.go
index b824a3a18c..851ca4e30f 100644
--- a/src/cmd/7g/ggen.go
+++ b/src/cmd/compile/internal/arm64/ggen.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package arm64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/arm64"
"fmt"
@@ -32,7 +32,7 @@ func defframe(ptxt *obj.Prog) {
// iterate through declarations - they are sorted in decreasing xoffset order.
for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
n = l.N
- if !n.Needzero {
+ if !n.Name.Needzero {
continue
}
if n.Class != gc.PAUTO {
@@ -147,9 +147,9 @@ func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) {
check := 0
if gc.Issigned[t.Etype] {
check = 1
- if gc.Isconst(nl, gc.CTINT) && gc.Mpgetfix(nl.Val.U.Xval) != -(1<= uint64(nl.Type.Width*8) {
// large shift gets 2 shifts by width-1
var n3 gc.Node
diff --git a/src/cmd/7g/gsubr.go b/src/cmd/compile/internal/arm64/gsubr.go
similarity index 95%
rename from src/cmd/7g/gsubr.go
rename to src/cmd/compile/internal/arm64/gsubr.go
index a34a4306ae..0a14654d83 100644
--- a/src/cmd/7g/gsubr.go
+++ b/src/cmd/compile/internal/arm64/gsubr.go
@@ -28,10 +28,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package arm64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/arm64"
"fmt"
@@ -102,6 +102,34 @@ func ginscon2(as int, n2 *gc.Node, c int64) {
gc.Regfree(&ntmp)
}
+func ginscmp(op int, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
+ if gc.Isint[t.Etype] && n1.Op == gc.OLITERAL && n2.Op != gc.OLITERAL {
+ // Reverse comparison to place constant last.
+ op = gc.Brrev(op)
+ n1, n2 = n2, n1
+ }
+
+ var r1, r2, g1, g2 gc.Node
+ gc.Regalloc(&r1, t, n1)
+ gc.Regalloc(&g1, n1.Type, &r1)
+ gc.Cgen(n1, &g1)
+ gmove(&g1, &r1)
+ if gc.Isint[t.Etype] && gc.Isconst(n2, gc.CTINT) {
+ ginscon2(optoas(gc.OCMP, t), &r1, n2.Int())
+ } else {
+ gc.Regalloc(&r2, t, n2)
+ gc.Regalloc(&g2, n1.Type, &r2)
+ gc.Cgen(n2, &g2)
+ gmove(&g2, &r2)
+ gcmp(optoas(gc.OCMP, t), &r1, &r2)
+ gc.Regfree(&g2)
+ gc.Regfree(&r2)
+ }
+ gc.Regfree(&g1)
+ gc.Regfree(&r1)
+ return gc.Gbranch(optoas(op, t), nil, likely)
+}
+
/*
* generate move:
* t = f
@@ -133,13 +161,13 @@ func gmove(f *gc.Node, t *gc.Node) {
var con gc.Node
switch tt {
default:
- gc.Convconst(&con, t.Type, &f.Val)
+ f.Convconst(&con, t.Type)
case gc.TINT32,
gc.TINT16,
gc.TINT8:
var con gc.Node
- gc.Convconst(&con, gc.Types[gc.TINT64], &f.Val)
+ f.Convconst(&con, gc.Types[gc.TINT64])
var r1 gc.Node
gc.Regalloc(&r1, con.Type, t)
gins(arm64.AMOVD, &con, &r1)
@@ -151,7 +179,7 @@ func gmove(f *gc.Node, t *gc.Node) {
gc.TUINT16,
gc.TUINT8:
var con gc.Node
- gc.Convconst(&con, gc.Types[gc.TUINT64], &f.Val)
+ f.Convconst(&con, gc.Types[gc.TUINT64])
var r1 gc.Node
gc.Regalloc(&r1, con.Type, t)
gins(arm64.AMOVD, &con, &r1)
@@ -440,14 +468,13 @@ hard:
}
func intLiteral(n *gc.Node) (x int64, ok bool) {
- if n == nil || n.Op != gc.OLITERAL {
+ switch {
+ case n == nil:
return
- }
- switch n.Val.Ctype {
- case gc.CTINT, gc.CTRUNE:
- return gc.Mpgetfix(n.Val.U.Xval), true
- case gc.CTBOOL:
- return int64(obj.Bool2int(n.Val.U.Bval)), true
+ case gc.Isconst(n, gc.CTINT):
+ return n.Int(), true
+ case gc.Isconst(n, gc.CTBOOL):
+ return int64(obj.Bool2int(n.Bool())), true
}
return
}
diff --git a/src/cmd/7g/peep.go b/src/cmd/compile/internal/arm64/peep.go
similarity index 99%
rename from src/cmd/7g/peep.go
rename to src/cmd/compile/internal/arm64/peep.go
index 49bc69b132..3dbccb70b2 100644
--- a/src/cmd/7g/peep.go
+++ b/src/cmd/compile/internal/arm64/peep.go
@@ -28,10 +28,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package arm64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/arm64"
"fmt"
@@ -422,9 +422,9 @@ func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
// 7g never generates a from3
fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(&p.From3))
}
- if p.To2.Type != obj.TYPE_NONE {
+ if p.RegTo2 != obj.REG_NONE {
// 7g never generates a to2
- fmt.Printf("copyu: to2 (%v) not implemented\n", gc.Ctxt.Dconv(&p.To2))
+ fmt.Printf("copyu: RegTo2 (%v) not implemented\n", obj.Rconv(int(p.RegTo2)))
}
switch p.As {
diff --git a/src/cmd/7g/prog.go b/src/cmd/compile/internal/arm64/prog.go
similarity index 99%
rename from src/cmd/7g/prog.go
rename to src/cmd/compile/internal/arm64/prog.go
index 023f302e14..1106e788a5 100644
--- a/src/cmd/7g/prog.go
+++ b/src/cmd/compile/internal/arm64/prog.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package arm64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/arm64"
)
diff --git a/src/cmd/7g/reg.go b/src/cmd/compile/internal/arm64/reg.go
similarity index 98%
rename from src/cmd/7g/reg.go
rename to src/cmd/compile/internal/arm64/reg.go
index 0e5ac73499..7bc756b7bf 100644
--- a/src/cmd/7g/reg.go
+++ b/src/cmd/compile/internal/arm64/reg.go
@@ -28,10 +28,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package arm64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj/arm64"
)
diff --git a/src/cmd/internal/gc/big/accuracy_string.go b/src/cmd/compile/internal/big/accuracy_string.go
similarity index 100%
rename from src/cmd/internal/gc/big/accuracy_string.go
rename to src/cmd/compile/internal/big/accuracy_string.go
diff --git a/src/cmd/internal/gc/big/arith.go b/src/cmd/compile/internal/big/arith.go
similarity index 99%
rename from src/cmd/internal/gc/big/arith.go
rename to src/cmd/compile/internal/big/arith.go
index 328c85c4f7..1ff6349d9d 100644
--- a/src/cmd/internal/gc/big/arith.go
+++ b/src/cmd/compile/internal/big/arith.go
@@ -196,7 +196,6 @@ func subVV_g(z, x, y []Word) (c Word) {
return
}
-// Argument y must be either 0 or 1.
// The resulting carry c is either 0 or 1.
func addVW_g(z, x []Word, y Word) (c Word) {
if use_addWW_g {
diff --git a/src/cmd/internal/gc/big/arith_decl.go b/src/cmd/compile/internal/big/arith_decl.go
similarity index 100%
rename from src/cmd/internal/gc/big/arith_decl.go
rename to src/cmd/compile/internal/big/arith_decl.go
diff --git a/src/cmd/internal/gc/big/arith_test.go b/src/cmd/compile/internal/big/arith_test.go
similarity index 99%
rename from src/cmd/internal/gc/big/arith_test.go
rename to src/cmd/compile/internal/big/arith_test.go
index cd92dd7173..f46a494f17 100644
--- a/src/cmd/internal/gc/big/arith_test.go
+++ b/src/cmd/compile/internal/big/arith_test.go
@@ -155,6 +155,7 @@ var sumVW = []argVW{
{nat{1}, nat{1}, 0, 0},
{nat{0}, nat{_M}, 1, 1},
{nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, 1, 1},
+ {nat{585}, nat{314}, 271, 0},
}
var prodVW = []argVW{
diff --git a/src/cmd/internal/gc/big/bits_test.go b/src/cmd/compile/internal/big/bits_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/bits_test.go
rename to src/cmd/compile/internal/big/bits_test.go
diff --git a/src/cmd/internal/gc/big/calibrate_test.go b/src/cmd/compile/internal/big/calibrate_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/calibrate_test.go
rename to src/cmd/compile/internal/big/calibrate_test.go
diff --git a/src/cmd/internal/gc/big/decimal.go b/src/cmd/compile/internal/big/decimal.go
similarity index 100%
rename from src/cmd/internal/gc/big/decimal.go
rename to src/cmd/compile/internal/big/decimal.go
diff --git a/src/cmd/internal/gc/big/decimal_test.go b/src/cmd/compile/internal/big/decimal_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/decimal_test.go
rename to src/cmd/compile/internal/big/decimal_test.go
diff --git a/src/cmd/internal/gc/big/example_test.go b/src/cmd/compile/internal/big/example_test.go
similarity index 97%
rename from src/cmd/internal/gc/big/example_test.go
rename to src/cmd/compile/internal/big/example_test.go
index 078be47f95..cb91bc23bd 100644
--- a/src/cmd/internal/gc/big/example_test.go
+++ b/src/cmd/compile/internal/big/example_test.go
@@ -5,9 +5,9 @@
package big_test
import (
+ "cmd/compile/internal/big"
"fmt"
"log"
- "math/big"
)
func ExampleRat_SetString() {
diff --git a/src/cmd/internal/gc/big/float.go b/src/cmd/compile/internal/big/float.go
similarity index 93%
rename from src/cmd/internal/gc/big/float.go
rename to src/cmd/compile/internal/big/float.go
index ed55e8e513..dcb72c5754 100644
--- a/src/cmd/internal/gc/big/float.go
+++ b/src/cmd/compile/internal/big/float.go
@@ -65,12 +65,16 @@ type Float struct {
exp int32
}
-// Float operations that would lead to a NaN under IEEE-754 rules cause
-// a run-time panic of ErrNaN type.
+// An ErrNaN panic is raised by a Float operation that would lead to
+// a NaN under IEEE-754 rules. An ErrNaN implements the error interface.
type ErrNaN struct {
msg string
}
+func (err ErrNaN) Error() string {
+ return err.msg
+}
+
// NewFloat allocates and returns a new Float set to x,
// with precision 53 and rounding mode ToNearestEven.
// NewFloat panics with ErrNaN if x is a NaN.
@@ -849,9 +853,6 @@ func (x *Float) Int64() (int64, Accuracy) {
panic("unreachable")
}
-// TODO(gri) Float32 and Float64 are very similar internally but for the
-// floatxx parameters and some conversions. Should factor out shared code.
-
// Float32 returns the float32 value nearest to x. If x is too small to be
// represented by a float32 (|x| < math.SmallestNonzeroFloat32), the result
// is (0, Below) or (-0, Above), respectively, depending on the sign of x.
@@ -876,64 +877,70 @@ func (x *Float) Float32() (float32, Accuracy) {
emax = bias // 127 largest unbiased exponent (normal)
)
- // Float mantissae m have an explicit msb and are in the range 0.5 <= m < 1.0.
- // floatxx mantissae have an implicit msb and are in the range 1.0 <= m < 2.0.
- // For a given mantissa m, we need to add 1 to a floatxx exponent to get the
- // corresponding Float exponent.
- // (see also implementation of math.Ldexp for similar code)
+ // Float mantissa m is 0.5 <= m < 1.0; compute exponent for floatxx mantissa.
+ e := x.exp - 1 // exponent for mantissa m with 1.0 <= m < 2.0
+ p := mbits + 1 // precision of normal float
- if x.exp < dmin+1 {
- // underflow
- if x.neg {
- var z float32
- return -z, Above
+ // If the exponent is too small, we may have a denormal number
+ // in which case we have fewer mantissa bits available: reduce
+ // precision accordingly.
+ if e < emin {
+ p -= emin - int(e)
+ // Make sure we have at least 1 bit so that we don't
+ // lose numbers rounded up to the smallest denormal.
+ if p < 1 {
+ p = 1
}
- return 0.0, Below
}
- // x.exp >= dmin+1
+ // round
var r Float
- r.prec = mbits + 1 // +1 for implicit msb
- if x.exp < emin+1 {
- // denormal number - round to fewer bits
- r.prec = uint32(x.exp - dmin)
- }
+ r.prec = uint32(p)
r.Set(x)
+ e = r.exp - 1
// Rounding may have caused r to overflow to ±Inf
// (rounding never causes underflows to 0).
if r.form == inf {
- r.exp = emax + 2 // cause overflow below
+ e = emax + 1 // cause overflow below
}
- if r.exp > emax+1 {
+ // If the exponent is too large, overflow to ±Inf.
+ if e > emax {
// overflow
if x.neg {
return float32(math.Inf(-1)), Below
}
return float32(math.Inf(+1)), Above
}
- // dmin+1 <= r.exp <= emax+1
- var s uint32
- if r.neg {
- s = 1 << (fbits - 1)
+ // Determine sign, biased exponent, and mantissa.
+ var sign, bexp, mant uint32
+ if x.neg {
+ sign = 1 << (fbits - 1)
}
- m := high32(r.mant) >> ebits & (1<> (fbits - r.prec)
+ } else {
+ // normal number: emin <= e <= emax
+ bexp = uint32(e+bias) << mbits
+ mant = high32(r.mant) >> ebits & (1<= dmin+1
+ // round
var r Float
- r.prec = mbits + 1 // +1 for implicit msb
- if x.exp < emin+1 {
- // denormal number - round to fewer bits
- r.prec = uint32(x.exp - dmin)
- }
+ r.prec = uint32(p)
r.Set(x)
+ e = r.exp - 1
// Rounding may have caused r to overflow to ±Inf
// (rounding never causes underflows to 0).
if r.form == inf {
- r.exp = emax + 2 // cause overflow below
+ e = emax + 1 // cause overflow below
}
- if r.exp > emax+1 {
+ // If the exponent is too large, overflow to ±Inf.
+ if e > emax {
// overflow
if x.neg {
return math.Inf(-1), Below
}
return math.Inf(+1), Above
}
- // dmin+1 <= r.exp <= emax+1
- var s uint64
- if r.neg {
- s = 1 << (fbits - 1)
+ // Determine sign, biased exponent, and mantissa.
+ var sign, bexp, mant uint64
+ if x.neg {
+ sign = 1 << (fbits - 1)
}
- m := high64(r.mant) >> ebits & (1<> (fbits - r.prec)
+ } else {
+ // normal number: emin <= e <= emax
+ bexp = uint64(e+bias) << mbits
+ mant = high64(r.mant) >> ebits & (1< 0
+
+ // handle factors of 2 in 'a'
+ s := a.abs.trailingZeroBits()
+ if s&1 != 0 {
+ bmod8 := b.abs[0] & 7
+ if bmod8 == 3 || bmod8 == 5 {
+ j = -j
+ }
+ }
+ c.Rsh(&a, s) // a = 2^s*c
+
+ // swap numerator and denominator
+ if b.abs[0]&3 == 3 && c.abs[0]&3 == 3 {
+ j = -j
+ }
+ a.Set(&b)
+ b.Set(&c)
+ }
+}
+
+// ModSqrt sets z to a square root of x mod p if such a square root exists, and
+// returns z. The modulus p must be an odd prime. If x is not a square mod p,
+// ModSqrt leaves z unchanged and returns nil. This function panics if p is
+// not an odd integer.
+func (z *Int) ModSqrt(x, p *Int) *Int {
+ switch Jacobi(x, p) {
+ case -1:
+ return nil // x is not a square mod p
+ case 0:
+ return z.SetInt64(0) // sqrt(0) mod p = 0
+ case 1:
+ break
+ }
+ if x.neg || x.Cmp(p) >= 0 { // ensure 0 <= x < p
+ x = new(Int).Mod(x, p)
+ }
+
+ // Break p-1 into s*2^e such that s is odd.
+ var s Int
+ s.Sub(p, intOne)
+ e := s.abs.trailingZeroBits()
+ s.Rsh(&s, e)
+
+ // find some non-square n
+ var n Int
+ n.SetInt64(2)
+ for Jacobi(&n, p) != -1 {
+ n.Add(&n, intOne)
+ }
+
+ // Core of the Tonelli-Shanks algorithm. Follows the description in
+ // section 6 of "Square roots from 1; 24, 51, 10 to Dan Shanks" by Ezra
+ // Brown:
+ // https://www.maa.org/sites/default/files/pdf/upload_library/22/Polya/07468342.di020786.02p0470a.pdf
+ var y, b, g, t Int
+ y.Add(&s, intOne)
+ y.Rsh(&y, 1)
+ y.Exp(x, &y, p) // y = x^((s+1)/2)
+ b.Exp(x, &s, p) // b = x^s
+ g.Exp(&n, &s, p) // g = n^s
+ r := e
+ for {
+ // find the least m such that ord_p(b) = 2^m
+ var m uint
+ t.Set(&b)
+ for t.Cmp(intOne) != 0 {
+ t.Mul(&t, &t).Mod(&t, p)
+ m++
+ }
+
+ if m == 0 {
+ return z.Set(&y)
+ }
+
+ t.SetInt64(0).SetBit(&t, int(r-m-1), 1).Exp(&g, &t, p)
+ // t = g^(2^(r-m-1)) mod p
+ g.Mul(&t, &t).Mod(&g, p) // g = g^(2^(r-m)) mod p
+ y.Mul(&y, &t).Mod(&y, p)
+ b.Mul(&b, &g).Mod(&b, p)
+ r = m
+ }
+}
+
// Lsh sets z = x << n and returns z.
func (z *Int) Lsh(x *Int, n uint) *Int {
z.abs = z.abs.shl(x.abs, n)
diff --git a/src/cmd/internal/gc/big/int_test.go b/src/cmd/compile/internal/big/int_test.go
similarity index 90%
rename from src/cmd/internal/gc/big/int_test.go
rename to src/cmd/compile/internal/big/int_test.go
index a972a7249b..c19e88addb 100644
--- a/src/cmd/internal/gc/big/int_test.go
+++ b/src/cmd/compile/internal/big/int_test.go
@@ -525,6 +525,7 @@ var expTests = []struct {
{"1234", "-1", "1", "0"},
// misc
+ {"5", "1", "3", "2"},
{"5", "-7", "", "1"},
{"-5", "-7", "", "1"},
{"5", "0", "", "1"},
@@ -703,6 +704,13 @@ var primes = []string{
"230975859993204150666423538988557839555560243929065415434980904258310530753006723857139742334640122533598517597674807096648905501653461687601339782814316124971547968912893214002992086353183070342498989426570593",
"5521712099665906221540423207019333379125265462121169655563495403888449493493629943498064604536961775110765377745550377067893607246020694972959780839151452457728855382113555867743022746090187341871655890805971735385789993",
"203956878356401977405765866929034577280193993314348263094772646453283062722701277632936616063144088173312372882677123879538709400158306567338328279154499698366071906766440037074217117805690872792848149112022286332144876183376326512083574821647933992961249917319836219304274280243803104015000563790123",
+
+ // ECC primes: http://tools.ietf.org/html/draft-ladd-safecurves-02
+ "3618502788666131106986593281521497120414687020801267626233049500247285301239", // Curve1174: 2^251-9
+ "57896044618658097711785492504343953926634992332820282019728792003956564819949", // Curve25519: 2^255-19
+ "9850501549098619803069760025035903451269934817616361666987073351061430442874302652853566563721228910201656997576599", // E-382: 2^382-105
+ "42307582002575910332922579714097346549017899709713998034217522897561970639123926132812109468141778230245837569601494931472367", // Curve41417: 2^414-17
+ "6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151", // E-521: 2^521-1
}
var composites = []string{
@@ -1248,6 +1256,136 @@ func TestModInverse(t *testing.T) {
}
}
+// testModSqrt is a helper for TestModSqrt,
+// which checks that ModSqrt can compute a square-root of elt^2.
+func testModSqrt(t *testing.T, elt, mod, sq, sqrt *Int) bool {
+ var sqChk, sqrtChk, sqrtsq Int
+ sq.Mul(elt, elt)
+ sq.Mod(sq, mod)
+ z := sqrt.ModSqrt(sq, mod)
+ if z != sqrt {
+ t.Errorf("ModSqrt returned wrong value %s", z)
+ }
+
+ // test ModSqrt arguments outside the range [0,mod)
+ sqChk.Add(sq, mod)
+ z = sqrtChk.ModSqrt(&sqChk, mod)
+ if z != &sqrtChk || z.Cmp(sqrt) != 0 {
+ t.Errorf("ModSqrt returned inconsistent value %s", z)
+ }
+ sqChk.Sub(sq, mod)
+ z = sqrtChk.ModSqrt(&sqChk, mod)
+ if z != &sqrtChk || z.Cmp(sqrt) != 0 {
+ t.Errorf("ModSqrt returned inconsistent value %s", z)
+ }
+
+ // make sure we actually got a square root
+ if sqrt.Cmp(elt) == 0 {
+ return true // we found the "desired" square root
+ }
+ sqrtsq.Mul(sqrt, sqrt) // make sure we found the "other" one
+ sqrtsq.Mod(&sqrtsq, mod)
+ return sq.Cmp(&sqrtsq) == 0
+}
+
+func TestModSqrt(t *testing.T) {
+ var elt, mod, modx4, sq, sqrt Int
+ r := rand.New(rand.NewSource(9))
+ for i, s := range primes[1:] { // skip 2, use only odd primes
+ mod.SetString(s, 10)
+ modx4.Lsh(&mod, 2)
+
+ // test a few random elements per prime
+ for x := 1; x < 5; x++ {
+ elt.Rand(r, &modx4)
+ elt.Sub(&elt, &mod) // test range [-mod, 3*mod)
+ if !testModSqrt(t, &elt, &mod, &sq, &sqrt) {
+ t.Errorf("#%d: failed (sqrt(e) = %s)", i, &sqrt)
+ }
+ }
+ }
+
+ // exhaustive test for small values
+ for n := 3; n < 100; n++ {
+ mod.SetInt64(int64(n))
+ if !mod.ProbablyPrime(10) {
+ continue
+ }
+ isSquare := make([]bool, n)
+
+ // test all the squares
+ for x := 1; x < n; x++ {
+ elt.SetInt64(int64(x))
+ if !testModSqrt(t, &elt, &mod, &sq, &sqrt) {
+ t.Errorf("#%d: failed (sqrt(%d,%d) = %s)", x, &elt, &mod, &sqrt)
+ }
+ isSquare[sq.Uint64()] = true
+ }
+
+ // test all non-squares
+ for x := 1; x < n; x++ {
+ sq.SetInt64(int64(x))
+ z := sqrt.ModSqrt(&sq, &mod)
+ if !isSquare[x] && z != nil {
+ t.Errorf("#%d: failed (sqrt(%d,%d) = nil)", x, &sqrt, &mod)
+ }
+ }
+ }
+}
+
+func TestJacobi(t *testing.T) {
+ testCases := []struct {
+ x, y int64
+ result int
+ }{
+ {0, 1, 1},
+ {0, -1, 1},
+ {1, 1, 1},
+ {1, -1, 1},
+ {0, 5, 0},
+ {1, 5, 1},
+ {2, 5, -1},
+ {-2, 5, -1},
+ {2, -5, -1},
+ {-2, -5, 1},
+ {3, 5, -1},
+ {5, 5, 0},
+ {-5, 5, 0},
+ {6, 5, 1},
+ {6, -5, 1},
+ {-6, 5, 1},
+ {-6, -5, -1},
+ }
+
+ var x, y Int
+
+ for i, test := range testCases {
+ x.SetInt64(test.x)
+ y.SetInt64(test.y)
+ expected := test.result
+ actual := Jacobi(&x, &y)
+ if actual != expected {
+ t.Errorf("#%d: Jacobi(%d, %d) = %d, but expected %d", i, test.x, test.y, actual, expected)
+ }
+ }
+}
+
+func TestJacobiPanic(t *testing.T) {
+ const failureMsg = "test failure"
+ defer func() {
+ msg := recover()
+ if msg == nil || msg == failureMsg {
+ panic(msg)
+ }
+ t.Log(msg)
+ }()
+ x := NewInt(1)
+ y := NewInt(2)
+ // Jacobi should panic when the second argument is even.
+ Jacobi(x, y)
+ panic(failureMsg)
+}
+
var encodingTests = []string{
"-539345864568634858364538753846587364875430589374589",
"-678645873",
diff --git a/src/cmd/internal/gc/big/intconv.go b/src/cmd/compile/internal/big/intconv.go
similarity index 100%
rename from src/cmd/internal/gc/big/intconv.go
rename to src/cmd/compile/internal/big/intconv.go
diff --git a/src/cmd/internal/gc/big/intconv_test.go b/src/cmd/compile/internal/big/intconv_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/intconv_test.go
rename to src/cmd/compile/internal/big/intconv_test.go
diff --git a/src/cmd/internal/gc/big/nat.go b/src/cmd/compile/internal/big/nat.go
similarity index 89%
rename from src/cmd/internal/gc/big/nat.go
rename to src/cmd/compile/internal/big/nat.go
index 2a279d186c..c3eef76fa1 100644
--- a/src/cmd/internal/gc/big/nat.go
+++ b/src/cmd/compile/internal/big/nat.go
@@ -216,6 +216,34 @@ func basicMul(z, x, y nat) {
}
}
+// montgomery computes x*y*2^(-n*_W) mod m,
+// assuming k = -1/m mod 2^_W.
+// z is used for storing the result which is returned;
+// z must not alias x, y or m.
+func (z nat) montgomery(x, y, m nat, k Word, n int) nat {
+ var c1, c2 Word
+ z = z.make(n)
+ z.clear()
+ for i := 0; i < n; i++ {
+ d := y[i]
+ c1 += addMulVVW(z, x, d)
+ t := z[0] * k
+ c2 = addMulVVW(z, m, t)
+
+ copy(z, z[1:])
+ z[n-1] = c1 + c2
+ if z[n-1] < c1 {
+ c1 = 1
+ } else {
+ c1 = 0
+ }
+ }
+ if c1 != 0 {
+ subVV(z, z, m)
+ }
+ return z
+}
+
// Fast version of z[0:n+n>>1].add(z[0:n+n>>1], x[0:n]) w/o bounds checks.
// Factored out for readability - do not use outside karatsuba.
func karatsubaAdd(z, x nat, n int) {
@@ -888,6 +916,13 @@ func (z nat) expNN(x, y, m nat) nat {
}
// y > 0
+ // x**1 mod m == x mod m
+ if len(y) == 1 && y[0] == 1 && len(m) != 0 {
+ _, z = z.div(z, x, m)
+ return z
+ }
+ // y > 1
+
if len(m) != 0 {
// We likely end up being as long as the modulus.
z = z.make(len(m))
@@ -898,8 +933,11 @@ func (z nat) expNN(x, y, m nat) nat {
// 4-bit, windowed exponentiation. This involves precomputing 14 values
// (x^2...x^15) but then reduces the number of multiply-reduces by a
// third. Even for a 32-bit exponent, this reduces the number of
- // operations.
+ // operations. Uses Montgomery method for odd moduli.
if len(x) > 1 && len(y) > 1 && len(m) > 0 {
+ if m[0]&1 == 1 {
+ return z.expNNMontgomery(x, y, m)
+ }
return z.expNNWindowed(x, y, m)
}
@@ -1022,6 +1060,87 @@ func (z nat) expNNWindowed(x, y, m nat) nat {
return z.norm()
}
+// expNNMontgomery calculates x**y mod m using a fixed, 4-bit window.
+// Uses Montgomery representation.
+func (z nat) expNNMontgomery(x, y, m nat) nat {
+ var zz, one, rr, RR nat
+
+ numWords := len(m)
+
+ // We want the lengths of x and m to be equal.
+ if len(x) > numWords {
+ _, rr = rr.div(rr, x, m)
+ } else if len(x) < numWords {
+ rr = rr.make(numWords)
+ rr.clear()
+ for i := range x {
+ rr[i] = x[i]
+ }
+ } else {
+ rr = x
+ }
+ x = rr
+
+ // Ideally the precomputations would be performed outside, and reused
+ // k0 = -mˆ-1 mod 2ˆ_W. Algorithm from: Dumas, J.G. "On Newton–Raphson
+ // Iteration for Multiplicative Inverses Modulo Prime Powers".
+ k0 := 2 - m[0]
+ t := m[0] - 1
+ for i := 1; i < _W; i <<= 1 {
+ t *= t
+ k0 *= (t + 1)
+ }
+ k0 = -k0
+
+ // RR = 2ˆ(2*_W*len(m)) mod m
+ RR = RR.setWord(1)
+ zz = zz.shl(RR, uint(2*numWords*_W))
+ _, RR = RR.div(RR, zz, m)
+ if len(RR) < numWords {
+ zz = zz.make(numWords)
+ copy(zz, RR)
+ RR = zz
+ }
+ // one = 1, with equal length to that of m
+ one = one.make(numWords)
+ one.clear()
+ one[0] = 1
+
+ const n = 4
+ // powers[i] contains x^i
+ var powers [1 << n]nat
+ powers[0] = powers[0].montgomery(one, RR, m, k0, numWords)
+ powers[1] = powers[1].montgomery(x, RR, m, k0, numWords)
+ for i := 2; i < 1<= 0; i-- {
+ yi := y[i]
+ for j := 0; j < _W; j += n {
+ if i != len(y)-1 || j != 0 {
+ zz = zz.montgomery(z, z, m, k0, numWords)
+ z = z.montgomery(zz, zz, m, k0, numWords)
+ zz = zz.montgomery(z, z, m, k0, numWords)
+ z = z.montgomery(zz, zz, m, k0, numWords)
+ }
+ zz = zz.montgomery(z, powers[yi>>(_W-n)], m, k0, numWords)
+ z, zz = zz, z
+ yi <<= n
+ }
+ }
+ // convert to regular number
+ zz = zz.montgomery(z, one, m, k0, numWords)
+ return zz.norm()
+}
+
// probablyPrime performs reps Miller-Rabin tests to check whether n is prime.
// If it returns true, n is prime with probability 1 - 1/4^reps.
// If it returns false, n is not prime.
diff --git a/src/cmd/internal/gc/big/nat_test.go b/src/cmd/compile/internal/big/nat_test.go
similarity index 83%
rename from src/cmd/internal/gc/big/nat_test.go
rename to src/cmd/compile/internal/big/nat_test.go
index b25a89f731..a15a2bcac0 100644
--- a/src/cmd/internal/gc/big/nat_test.go
+++ b/src/cmd/compile/internal/big/nat_test.go
@@ -332,6 +332,67 @@ func TestTrailingZeroBits(t *testing.T) {
}
}
+var montgomeryTests = []struct {
+ x, y, m string
+ k0 uint64
+ out32, out64 string
+}{
+ {
+ "0xffffffffffffffffffffffffffffffffffffffffffffffffe",
+ "0xffffffffffffffffffffffffffffffffffffffffffffffffe",
+ "0xfffffffffffffffffffffffffffffffffffffffffffffffff",
+ 0x0000000000000000,
+ "0xffffffffffffffffffffffffffffffffffffffffff",
+ "0xffffffffffffffffffffffffffffffffff",
+ },
+ {
+ "0x0000000080000000",
+ "0x00000000ffffffff",
+ "0x0000000010000001",
+ 0xff0000000fffffff,
+ "0x0000000088000000",
+ "0x0000000007800001",
+ },
+ {
+ "0xffffffffffffffffffffffffffffffff00000000000022222223333333333444444444",
+ "0xffffffffffffffffffffffffffffffff999999999999999aaabbbbbbbbcccccccccccc",
+ "0x33377fffffffffffffffffffffffffffffffffffffffffffff0000000000022222eee1",
+ 0xdecc8f1249812adf,
+ "0x22bb05b6d95eaaeca2bb7c05e51f807bce9064b5fbad177161695e4558f9474e91cd79",
+ "0x14beb58d230f85b6d95eaaeca2bb7c05e51f807bce9064b5fb45669afa695f228e48cd",
+ },
+ {
+ "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000022222223333333333444444444",
+ "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff999999999999999aaabbbbbbbbcccccccccccc",
+ "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff33377fffffffffffffffffffffffffffffffffffffffffffff0000000000022222eee1",
+ 0xdecc8f1249812adf,
+ "0x5c0d52f451aec609b15da8e5e5626c4eaa88723bdeac9d25ca9b961269400410ca208a16af9c2fb07d7a11c7772cba02c22f9711078d51a3797eb18e691295293284d988e349fa6deba46b25a4ecd9f715",
+ "0x92fcad4b5c0d52f451aec609b15da8e5e5626c4eaa88723bdeac9d25ca9b961269400410ca208a16af9c2fb07d799c32fe2f3cc5422f9711078d51a3797eb18e691295293284d8f5e69caf6decddfe1df6",
+ },
+}
+
+func TestMontgomery(t *testing.T) {
+ for i, test := range montgomeryTests {
+ x := natFromString(test.x)
+ y := natFromString(test.y)
+ m := natFromString(test.m)
+
+ var out nat
+ if _W == 32 {
+ out = natFromString(test.out32)
+ } else {
+ out = natFromString(test.out64)
+ }
+
+ k0 := Word(test.k0 & _M) // mask k0 to ensure that it fits for 32-bit systems.
+ z := nat(nil).montgomery(x, y, m, k0, len(m))
+ z = z.norm()
+ if z.cmp(out) != 0 {
+ t.Errorf("#%d got %s want %s", i, z.decimalString(), out.decimalString())
+ }
+ }
+}
+
var expNNTests = []struct {
x, y, m string
out string
diff --git a/src/cmd/internal/gc/big/natconv.go b/src/cmd/compile/internal/big/natconv.go
similarity index 100%
rename from src/cmd/internal/gc/big/natconv.go
rename to src/cmd/compile/internal/big/natconv.go
diff --git a/src/cmd/internal/gc/big/natconv_test.go b/src/cmd/compile/internal/big/natconv_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/natconv_test.go
rename to src/cmd/compile/internal/big/natconv_test.go
diff --git a/src/cmd/internal/gc/big/rat.go b/src/cmd/compile/internal/big/rat.go
similarity index 100%
rename from src/cmd/internal/gc/big/rat.go
rename to src/cmd/compile/internal/big/rat.go
diff --git a/src/cmd/internal/gc/big/rat_test.go b/src/cmd/compile/internal/big/rat_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/rat_test.go
rename to src/cmd/compile/internal/big/rat_test.go
diff --git a/src/cmd/internal/gc/big/ratconv.go b/src/cmd/compile/internal/big/ratconv.go
similarity index 100%
rename from src/cmd/internal/gc/big/ratconv.go
rename to src/cmd/compile/internal/big/ratconv.go
diff --git a/src/cmd/internal/gc/big/ratconv_test.go b/src/cmd/compile/internal/big/ratconv_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/ratconv_test.go
rename to src/cmd/compile/internal/big/ratconv_test.go
diff --git a/src/cmd/internal/gc/big/roundingmode_string.go b/src/cmd/compile/internal/big/roundingmode_string.go
similarity index 100%
rename from src/cmd/internal/gc/big/roundingmode_string.go
rename to src/cmd/compile/internal/big/roundingmode_string.go
diff --git a/src/cmd/internal/gc/big/vendor.bash b/src/cmd/compile/internal/big/vendor.bash
similarity index 66%
rename from src/cmd/internal/gc/big/vendor.bash
rename to src/cmd/compile/internal/big/vendor.bash
index 84aa750462..1b191ccb8f 100755
--- a/src/cmd/internal/gc/big/vendor.bash
+++ b/src/cmd/compile/internal/big/vendor.bash
@@ -15,9 +15,15 @@ rm *.go
cp $BIGDIR/*.go .
# Use pure Go arith ops w/o build tag.
-sed 's/^\/\/ \+build math_big_pure_go$//' arith_decl_pure.go > arith_decl.go
+sed 's|^// \+build math_big_pure_go$||' arith_decl_pure.go > arith_decl.go
rm arith_decl_pure.go
+# Import vendored math/big in external tests (e.g., floatexample_test.go).
+for f in *_test.go; do
+ sed 's|"math/big"|"cmd/compile/internal/big"|' $f > foo.go
+ mv foo.go $f
+done
+
# gofmt to clean up after sed
gofmt -w .
diff --git a/src/cmd/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go
similarity index 99%
rename from src/cmd/internal/gc/align.go
rename to src/cmd/compile/internal/gc/align.go
index 789e59bfd0..892595a214 100644
--- a/src/cmd/internal/gc/align.go
+++ b/src/cmd/compile/internal/gc/align.go
@@ -71,8 +71,8 @@ func widstruct(errtype *Type, t *Type, o int64, flag int) int64 {
// in typecheck.c. usually addrescapes runs after
// widstruct, in which case we could drop this,
// but function closure functions are the exception.
- if f.Nname.Stackparam != nil {
- f.Nname.Stackparam.Xoffset = o
+ if f.Nname.Param.Stackparam != nil {
+ f.Nname.Param.Stackparam.Xoffset = o
f.Nname.Xoffset = 0
} else {
f.Nname.Xoffset = o
diff --git a/src/cmd/internal/gc/builtin.go b/src/cmd/compile/internal/gc/builtin.go
similarity index 100%
rename from src/cmd/internal/gc/builtin.go
rename to src/cmd/compile/internal/gc/builtin.go
diff --git a/src/cmd/internal/gc/builtin/runtime.go b/src/cmd/compile/internal/gc/builtin/runtime.go
similarity index 100%
rename from src/cmd/internal/gc/builtin/runtime.go
rename to src/cmd/compile/internal/gc/builtin/runtime.go
diff --git a/src/cmd/internal/gc/builtin/unsafe.go b/src/cmd/compile/internal/gc/builtin/unsafe.go
similarity index 100%
rename from src/cmd/internal/gc/builtin/unsafe.go
rename to src/cmd/compile/internal/gc/builtin/unsafe.go
diff --git a/src/cmd/internal/gc/bv.go b/src/cmd/compile/internal/gc/bv.go
similarity index 100%
rename from src/cmd/internal/gc/bv.go
rename to src/cmd/compile/internal/gc/bv.go
diff --git a/src/cmd/internal/gc/cgen.go b/src/cmd/compile/internal/gc/cgen.go
similarity index 73%
rename from src/cmd/internal/gc/cgen.go
rename to src/cmd/compile/internal/gc/cgen.go
index 501cdcb1c8..ca58b1c6a3 100644
--- a/src/cmd/internal/gc/cgen.go
+++ b/src/cmd/compile/internal/gc/cgen.go
@@ -43,14 +43,7 @@ func cgen_wb(n, res *Node, wb bool) {
switch n.Op {
case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
- if res.Op != ONAME || !res.Addable || wb {
- var n1 Node
- Tempname(&n1, n.Type)
- Cgen_slice(n, &n1)
- cgen_wb(&n1, res, wb)
- } else {
- Cgen_slice(n, res)
- }
+ cgen_slice(n, res, wb)
return
case OEFACE:
@@ -67,6 +60,10 @@ func cgen_wb(n, res *Node, wb bool) {
case ODOTTYPE:
cgen_dottype(n, res, nil, wb)
return
+
+ case OAPPEND:
+ cgen_append(n, res)
+ return
}
if n.Ullman >= UINF {
@@ -545,7 +542,7 @@ func cgen_wb(n, res *Node, wb bool) {
var n1 Node
Regalloc(&n1, Types[Tptr], res)
p1 := Thearch.Gins(Thearch.Optoas(OAS, n1.Type), nil, &n1)
- Datastring(nl.Val.U.Sval, &p1.From)
+ Datastring(nl.Val.U.(string), &p1.From)
p1.From.Type = obj.TYPE_ADDR
Thearch.Gmove(&n1, res)
Regfree(&n1)
@@ -569,8 +566,7 @@ func cgen_wb(n, res *Node, wb bool) {
var n2 Node
Nodconst(&n2, Types[Tptr], 0)
- Thearch.Gins(Thearch.Optoas(OCMP, Types[Tptr]), &n1, &n2)
- p1 := Gbranch(Thearch.Optoas(OEQ, Types[Tptr]), nil, 0)
+ p1 := Thearch.Ginscmp(OEQ, Types[Tptr], &n1, &n2, 0)
n2 = n1
n2.Op = OINDREG
@@ -610,8 +606,7 @@ func cgen_wb(n, res *Node, wb bool) {
var n2 Node
Nodconst(&n2, Types[Tptr], 0)
- Thearch.Gins(Thearch.Optoas(OCMP, Types[Tptr]), &n1, &n2)
- p1 := Gbranch(Thearch.Optoas(OEQ, Types[Tptr]), nil, 0)
+ p1 := Thearch.Ginscmp(OEQ, Types[Tptr], &n1, &n2, 0)
n2 = n1
n2.Op = OINDREG
@@ -790,6 +785,9 @@ abop: // asymmetric binary
var sys_wbptr *Node
func cgen_wbptr(n, res *Node) {
+ if Curfn != nil && Curfn.Func.Nowritebarrier {
+ Yyerror("write barrier prohibited")
+ }
if Debug_wb > 0 {
Warn("write barrier")
}
@@ -804,19 +802,7 @@ func cgen_wbptr(n, res *Node) {
}
wbEnabled := syslook("writeBarrierEnabled", 0)
- switch Ctxt.Arch.Thechar {
- default:
- Fatal("cgen_wbptr: unknown architecture")
- case '5', '7', '9':
- var tmp Node
- Regalloc(&tmp, Types[TUINT8], nil)
- Thearch.Gmove(wbEnabled, &tmp)
- Thearch.Gins(Thearch.Optoas(OCMP, Types[TUINT8]), &tmp, Nodintconst(0))
- Regfree(&tmp)
- case '6', '8':
- Thearch.Gins(Thearch.Optoas(OCMP, Types[TUINT8]), wbEnabled, Nodintconst(0))
- }
- pbr := Gbranch(Thearch.Optoas(ONE, Types[TUINT8]), nil, -1)
+ pbr := Thearch.Ginscmp(ONE, Types[TUINT8], wbEnabled, Nodintconst(0), -1)
Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, &dst)
pjmp := Gbranch(obj.AJMP, nil, 0)
Patch(pbr, Pc)
@@ -845,6 +831,9 @@ func cgen_wbptr(n, res *Node) {
}
func cgen_wbfat(n, res *Node) {
+ if Curfn != nil && Curfn.Func.Nowritebarrier {
+ Yyerror("write barrier prohibited")
+ }
if Debug_wb > 0 {
Warn("write barrier")
}
@@ -1047,7 +1036,7 @@ func Agenr(n *Node, a *Node, res *Node) {
if Isconst(nl, CTSTR) {
Fatal("constant string constant index")
}
- v := uint64(Mpgetfix(nr.Val.U.Xval))
+ v := uint64(Mpgetfix(nr.Val.U.(*Mpint)))
var n2 Node
if Isslice(nl.Type) || nl.Type.Etype == TSTRING {
if Debug['B'] == 0 && !n.Bounded {
@@ -1055,14 +1044,9 @@ func Agenr(n *Node, a *Node, res *Node) {
n1.Op = OINDREG
n1.Type = Types[Tptr]
n1.Xoffset = int64(Array_nel)
- var n4 Node
- Regalloc(&n4, n1.Type, nil)
- Thearch.Gmove(&n1, &n4)
Nodconst(&n2, Types[TUINT32], int64(v))
- Thearch.Gins(Thearch.Optoas(OCMP, Types[TUINT32]), &n4, &n2)
- Regfree(&n4)
- p1 := Gbranch(Thearch.Optoas(OGT, Types[TUINT32]), nil, +1)
- Ginscall(Panicindex, 0)
+ p1 := Thearch.Ginscmp(OGT, Types[TUINT32], &n1, &n2, +1)
+ Ginscall(Panicindex, -1)
Patch(p1, Pc)
}
@@ -1088,7 +1072,7 @@ func Agenr(n *Node, a *Node, res *Node) {
if Debug['B'] == 0 && !n.Bounded {
// check bounds
if Isconst(nl, CTSTR) {
- Nodconst(&n4, Types[TUINT32], int64(len(nl.Val.U.Sval)))
+ Nodconst(&n4, Types[TUINT32], int64(len(nl.Val.U.(string))))
} else if Isslice(nl.Type) || nl.Type.Etype == TSTRING {
n1 = n3
n1.Op = OINDREG
@@ -1099,23 +1083,21 @@ func Agenr(n *Node, a *Node, res *Node) {
} else {
Nodconst(&n4, Types[TUINT32], nl.Type.Bound)
}
-
- Thearch.Gins(Thearch.Optoas(OCMP, Types[TUINT32]), &n2, &n4)
+ p1 := Thearch.Ginscmp(OLT, Types[TUINT32], &n2, &n4, +1)
if n4.Op == OREGISTER {
Regfree(&n4)
}
- p1 := Gbranch(Thearch.Optoas(OLT, Types[TUINT32]), nil, +1)
if p2 != nil {
Patch(p2, Pc)
}
- Ginscall(Panicindex, 0)
+ Ginscall(Panicindex, -1)
Patch(p1, Pc)
}
if Isconst(nl, CTSTR) {
Regalloc(&n3, Types[Tptr], res)
p1 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &n3)
- Datastring(nl.Val.U.Sval, &p1.From)
+ Datastring(nl.Val.U.(string), &p1.From)
p1.From.Type = obj.TYPE_ADDR
} else if Isslice(nl.Type) || nl.Type.Etype == TSTRING {
n1 = n3
@@ -1206,15 +1188,14 @@ func Agenr(n *Node, a *Node, res *Node) {
if Isconst(nl, CTSTR) {
Fatal("constant string constant index") // front end should handle
}
- v := uint64(Mpgetfix(nr.Val.U.Xval))
+ v := uint64(Mpgetfix(nr.Val.U.(*Mpint)))
if Isslice(nl.Type) || nl.Type.Etype == TSTRING {
if Debug['B'] == 0 && !n.Bounded {
nlen := n3
nlen.Type = Types[TUINT32]
nlen.Xoffset += int64(Array_nel)
Nodconst(&n2, Types[TUINT32], int64(v))
- Thearch.Gins(Thearch.Optoas(OCMP, Types[TUINT32]), &nlen, &n2)
- p1 := Gbranch(Thearch.Optoas(OGT, Types[TUINT32]), nil, +1)
+ p1 := Thearch.Ginscmp(OGT, Types[TUINT32], &nlen, &n2, +1)
Ginscall(Panicindex, -1)
Patch(p1, Pc)
}
@@ -1252,7 +1233,7 @@ func Agenr(n *Node, a *Node, res *Node) {
var nlen Node
if Isconst(nl, CTSTR) {
- Nodconst(&nlen, t, int64(len(nl.Val.U.Sval)))
+ Nodconst(&nlen, t, int64(len(nl.Val.U.(string))))
} else if Isslice(nl.Type) || nl.Type.Etype == TSTRING {
nlen = n3
nlen.Type = t
@@ -1261,8 +1242,7 @@ func Agenr(n *Node, a *Node, res *Node) {
Nodconst(&nlen, t, nl.Type.Bound)
}
- Thearch.Gins(Thearch.Optoas(OCMP, t), &n2, &nlen)
- p1 := Gbranch(Thearch.Optoas(OLT, t), nil, +1)
+ p1 := Thearch.Ginscmp(OLT, t, &n2, &nlen, +1)
if p2 != nil {
Patch(p2, Pc)
}
@@ -1273,7 +1253,7 @@ func Agenr(n *Node, a *Node, res *Node) {
if Isconst(nl, CTSTR) {
Regalloc(&n3, Types[Tptr], res)
p1 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &n3)
- Datastring(nl.Val.U.Sval, &p1.From)
+ Datastring(nl.Val.U.(string), &p1.From)
p1.From.Type = obj.TYPE_ADDR
Thearch.Gins(Thearch.Optoas(OADD, n3.Type), &n2, &n3)
goto indexdone1
@@ -1398,28 +1378,10 @@ func Agenr(n *Node, a *Node, res *Node) {
if Isconst(nl, CTSTR) {
Fatal("constant string constant index") // front end should handle
}
- v := uint64(Mpgetfix(nr.Val.U.Xval))
+ v := uint64(Mpgetfix(nr.Val.U.(*Mpint)))
if Isslice(nl.Type) || nl.Type.Etype == TSTRING {
if Debug['B'] == 0 && !n.Bounded {
- if nlen.Op != OREGISTER && (Ctxt.Arch.Thechar == '7' || Ctxt.Arch.Thechar == '9') {
- var tmp2 Node
- Regalloc(&tmp2, Types[Simtype[TUINT]], nil)
- Thearch.Gmove(&nlen, &tmp2)
- Regfree(&nlen) // in case it is OINDREG
- nlen = tmp2
- }
- var n2 Node
- Nodconst(&n2, Types[Simtype[TUINT]], int64(v))
- if Smallintconst(nr) {
- Thearch.Gins(Thearch.Optoas(OCMP, Types[Simtype[TUINT]]), &nlen, &n2)
- } else {
- Regalloc(&tmp, Types[Simtype[TUINT]], nil)
- Thearch.Gmove(&n2, &tmp)
- Thearch.Gins(Thearch.Optoas(OCMP, Types[Simtype[TUINT]]), &nlen, &tmp)
- Regfree(&tmp)
- }
-
- p1 := Gbranch(Thearch.Optoas(OGT, Types[Simtype[TUINT]]), nil, +1)
+ p1 := Thearch.Ginscmp(OGT, Types[Simtype[TUINT]], &nlen, Nodintconst(int64(v)), +1)
Ginscall(Panicindex, -1)
Patch(p1, Pc)
}
@@ -1454,28 +1416,14 @@ func Agenr(n *Node, a *Node, res *Node) {
t = Types[TUINT64]
}
if Isconst(nl, CTSTR) {
- Nodconst(&nlen, t, int64(len(nl.Val.U.Sval)))
+ Nodconst(&nlen, t, int64(len(nl.Val.U.(string))))
} else if Isslice(nl.Type) || nl.Type.Etype == TSTRING {
- if Is64(nr.Type) || Ctxt.Arch.Thechar == '7' || Ctxt.Arch.Thechar == '9' {
- var n5 Node
- Regalloc(&n5, t, nil)
- Thearch.Gmove(&nlen, &n5)
- Regfree(&nlen)
- nlen = n5
- }
+ // nlen already initialized
} else {
Nodconst(&nlen, t, nl.Type.Bound)
- if !Smallintconst(&nlen) {
- var n5 Node
- Regalloc(&n5, t, nil)
- Thearch.Gmove(&nlen, &n5)
- nlen = n5
- freelen = 1
- }
}
- Thearch.Gins(Thearch.Optoas(OCMP, t), &n2, &nlen)
- p1 := Gbranch(Thearch.Optoas(OLT, t), nil, +1)
+ p1 := Thearch.Ginscmp(OLT, t, &n2, &nlen, +1)
Ginscall(Panicindex, -1)
Patch(p1, Pc)
}
@@ -1483,7 +1431,7 @@ func Agenr(n *Node, a *Node, res *Node) {
if Isconst(nl, CTSTR) {
Regalloc(&n3, Types[Tptr], res)
p1 := Thearch.Gins(Thearch.Optoas(OAS, n3.Type), nil, &n3) // XXX was LEAQ!
- Datastring(nl.Val.U.Sval, &p1.From)
+ Datastring(nl.Val.U.(string), &p1.From)
p1.From.Type = obj.TYPE_ADDR
Thearch.Gins(Thearch.Optoas(OADD, n3.Type), &n2, &n3)
goto indexdone
@@ -1637,7 +1585,7 @@ func Agen(n *Node, res *Node) {
Fatal("agen: bad ONAME class %#x", n.Class)
}
- Cgen(n.Heapaddr, res)
+ Cgen(n.Name.Heapaddr, res)
if n.Xoffset != 0 {
addOffset(res, n.Xoffset)
}
@@ -1770,7 +1718,7 @@ func Igen(n *Node, a *Node, res *Node) {
// Compute &a[i] as &a + i*width.
a.Type = n.Type
- a.Xoffset += Mpgetfix(n.Right.Val.U.Xval) * n.Type.Width
+ a.Xoffset += Mpgetfix(n.Right.Val.U.(*Mpint)) * n.Type.Width
Fixlargeoffset(a)
return
}
@@ -1920,11 +1868,11 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) {
Fatal("bgen: non-bool const %v\n", Nconv(n, obj.FmtLong))
}
if genval {
- Cgen(Nodbool(wantTrue == n.Val.U.Bval), res)
+ Cgen(Nodbool(wantTrue == n.Val.U.(bool)), res)
return
}
// If n == wantTrue, jump; otherwise do nothing.
- if wantTrue == n.Val.U.Bval {
+ if wantTrue == n.Val.U.(bool) {
Patch(Gbranch(obj.AJMP, nil, likely), to)
}
return
@@ -2214,14 +2162,27 @@ func bins(typ *Type, res *Node, a, likely int, to *obj.Prog) {
}
}
-/*
- * n is on stack, either local variable
- * or return value from function call.
- * return n's offset from SP.
- */
+// stkof returns n's offset from SP if n is on the stack
+// (either a local variable or the return value from a function call
+// or the arguments to a function call).
+// If n is not on the stack, stkof returns -1000.
+// If n is on the stack but in an unknown location
+// (due to array index arithmetic), stkof returns +1000.
+//
+// NOTE(rsc): It is possible that the ODOT and OINDEX cases
+// are not relevant here, since it shouldn't be possible for them
+// to be involved in an overlapping copy. Only function results
+// from one call and the arguments to the next can overlap in
+// any non-trivial way. If they can be dropped, then this function
+// becomes much simpler and also more trustworthy.
+// The fact that it works at all today is probably due to the fact
+// that ODOT and OINDEX are irrelevant.
func stkof(n *Node) int64 {
switch n.Op {
case OINDREG:
+ if n.Reg != int16(Thearch.REGSP) {
+ return -1000 // not on stack
+ }
return n.Xoffset
case ODOT:
@@ -2230,7 +2191,7 @@ func stkof(n *Node) int64 {
break
}
off := stkof(n.Left)
- if off == -1000 || off == 1000 {
+ if off == -1000 || off == +1000 {
return off
}
return off + n.Xoffset
@@ -2241,13 +2202,13 @@ func stkof(n *Node) int64 {
break
}
off := stkof(n.Left)
- if off == -1000 || off == 1000 {
+ if off == -1000 || off == +1000 {
return off
}
if Isconst(n.Right, CTINT) {
- return off + t.Type.Width*Mpgetfix(n.Right.Val.U.Xval)
+ return off + t.Type.Width*Mpgetfix(n.Right.Val.U.(*Mpint))
}
- return 1000
+ return +1000 // on stack but not sure exactly where
case OCALLMETH, OCALLINTER, OCALLFUNC:
t := n.Left.Type
@@ -2268,7 +2229,7 @@ func stkof(n *Node) int64 {
// botch - probably failing to recognize address
// arithmetic on the above. eg INDEX and DOT
- return -1000
+ return -1000 // not on stack
}
/*
@@ -2446,8 +2407,7 @@ func Ginscall(f *Node, proc int) {
if proc == 2 {
Nodreg(®, Types[TINT32], Thearch.REGRETURN)
- Thearch.Gins(Thearch.Optoas(OCMP, Types[TINT32]), ®, Nodintconst(0))
- p := Gbranch(Thearch.Optoas(OEQ, Types[TINT32]), nil, +1)
+ p := Thearch.Ginscmp(OEQ, Types[TINT32], ®, Nodintconst(0), +1)
cgen_ret(nil)
Patch(p, Pc)
}
@@ -2576,7 +2536,7 @@ func cgen_call(n *Node, proc int) {
}
// call direct
- n.Left.Method = true
+ n.Left.Name.Method = true
Ginscall(n.Left, proc)
}
@@ -2701,7 +2661,7 @@ func cgen_div(op int, nl *Node, nr *Node, res *Node) {
case TUINT64:
var m Magic
m.W = w
- m.Ud = uint64(Mpgetfix(nr.Val.U.Xval))
+ m.Ud = uint64(Mpgetfix(nr.Val.U.(*Mpint)))
Umagic(&m)
if m.Bad != 0 {
break
@@ -2739,7 +2699,7 @@ func cgen_div(op int, nl *Node, nr *Node, res *Node) {
case TINT64:
var m Magic
m.W = w
- m.Sd = Mpgetfix(nr.Val.U.Xval)
+ m.Sd = Mpgetfix(nr.Val.U.(*Mpint))
Smagic(&m)
if m.Bad != 0 {
break
@@ -2843,3 +2803,762 @@ func Fixlargeoffset(n *Node) {
n.Xoffset = 0
}
}
+
+func cgen_append(n, res *Node) {
+ if Debug['g'] != 0 {
+ Dump("cgen_append-n", n)
+ Dump("cgen_append-res", res)
+ }
+ if res.Op != ONAME && !samesafeexpr(res, n.List.N) {
+ Dump("cgen_append-n", n)
+ Dump("cgen_append-res", res)
+ Fatal("append not lowered")
+ }
+ for l := n.List; l != nil; l = l.Next {
+ if l.N.Ullman >= UINF {
+ Fatal("append with function call arguments")
+ }
+ }
+
+ // res = append(src, x, y, z)
+ //
+ // If res and src are the same, we can avoid writing to base and cap
+ // unless we grow the underlying array.
+ needFullUpdate := !samesafeexpr(res, n.List.N)
+
+ // Copy src triple into base, len, cap.
+ base := temp(Types[Tptr])
+ len := temp(Types[TUINT])
+ cap := temp(Types[TUINT])
+
+ var src Node
+ Igen(n.List.N, &src, nil)
+ src.Type = Types[Tptr]
+ Thearch.Gmove(&src, base)
+ src.Type = Types[TUINT]
+ src.Xoffset += int64(Widthptr)
+ Thearch.Gmove(&src, len)
+ src.Xoffset += int64(Widthptr)
+ Thearch.Gmove(&src, cap)
+
+ // if len+argc <= cap goto L1
+ var rlen Node
+ Regalloc(&rlen, Types[TUINT], nil)
+ Thearch.Gmove(len, &rlen)
+ Thearch.Ginscon(Thearch.Optoas(OADD, Types[TUINT]), int64(count(n.List)-1), &rlen)
+ p := Thearch.Ginscmp(OLE, Types[TUINT], &rlen, cap, +1)
+ // Note: rlen and src are Regrealloc'ed below at the target of the
+ // branch we just emitted; do not reuse these Go variables for
+ // other purposes. They need to still describe the same things
+ // below that they describe right here.
+ Regfree(&src)
+
+ // base, len, cap = growslice(type, base, len, cap, newlen)
+ var arg Node
+ arg.Op = OINDREG
+ arg.Reg = int16(Thearch.REGSP)
+ arg.Addable = true
+ arg.Xoffset = 0
+ if HasLinkRegister() {
+ arg.Xoffset = int64(Ctxt.Arch.Ptrsize)
+ }
+ arg.Type = Ptrto(Types[TUINT8])
+ Cgen(typename(res.Type), &arg)
+ arg.Xoffset += int64(Widthptr)
+
+ arg.Type = Types[Tptr]
+ Cgen(base, &arg)
+ arg.Xoffset += int64(Widthptr)
+
+ arg.Type = Types[TUINT]
+ Cgen(len, &arg)
+ arg.Xoffset += int64(Widthptr)
+
+ arg.Type = Types[TUINT]
+ Cgen(cap, &arg)
+ arg.Xoffset += int64(Widthptr)
+
+ arg.Type = Types[TUINT]
+ Cgen(&rlen, &arg)
+ arg.Xoffset += int64(Widthptr)
+ Regfree(&rlen)
+
+ fn := syslook("growslice", 1)
+ substArgTypes(fn, res.Type.Type, res.Type.Type)
+ Ginscall(fn, 0)
+
+ if Widthptr == 4 && Widthreg == 8 {
+ arg.Xoffset += 4
+ }
+
+ arg.Type = Types[Tptr]
+ Cgen(&arg, base)
+ arg.Xoffset += int64(Widthptr)
+
+ arg.Type = Types[TUINT]
+ Cgen(&arg, len)
+ arg.Xoffset += int64(Widthptr)
+
+ arg.Type = Types[TUINT]
+ Cgen(&arg, cap)
+
+ // Update res with base, len+argc, cap.
+ if needFullUpdate {
+ if Debug_append > 0 {
+ Warn("append: full update")
+ }
+ Patch(p, Pc)
+ }
+ if res.Op == ONAME {
+ Gvardef(res)
+ }
+ var dst, r1 Node
+ Igen(res, &dst, nil)
+ dst.Type = Types[TUINT]
+ dst.Xoffset += int64(Widthptr)
+ Regalloc(&r1, Types[TUINT], nil)
+ Thearch.Gmove(len, &r1)
+ Thearch.Ginscon(Thearch.Optoas(OADD, Types[TUINT]), int64(count(n.List)-1), &r1)
+ Thearch.Gmove(&r1, &dst)
+ Regfree(&r1)
+ dst.Xoffset += int64(Widthptr)
+ Thearch.Gmove(cap, &dst)
+ dst.Type = Types[Tptr]
+ dst.Xoffset -= 2 * int64(Widthptr)
+ cgen_wb(base, &dst, needwritebarrier(&dst, base))
+ Regfree(&dst)
+
+ if !needFullUpdate {
+ if Debug_append > 0 {
+ Warn("append: len-only update")
+ }
+ // goto L2;
+ // L1:
+ // update len only
+ // L2:
+ q := Gbranch(obj.AJMP, nil, 0)
+ Patch(p, Pc)
+ // At the goto above, src refers to cap and rlen holds the new len
+ if src.Op == OREGISTER || src.Op == OINDREG {
+ Regrealloc(&src)
+ }
+ Regrealloc(&rlen)
+ src.Xoffset -= int64(Widthptr)
+ Thearch.Gmove(&rlen, &src)
+ Regfree(&src)
+ Regfree(&rlen)
+ Patch(q, Pc)
+ }
+
+ // Copy data into place.
+ // Could do write barrier check around entire copy instead of each element.
+ // Could avoid reloading registers on each iteration if we know the cgen_wb
+ // is not going to use a write barrier.
+ i := 0
+ var r2 Node
+ for l := n.List.Next; l != nil; l = l.Next {
+ Regalloc(&r1, Types[Tptr], nil)
+ Thearch.Gmove(base, &r1)
+ Regalloc(&r2, Types[TUINT], nil)
+ Thearch.Gmove(len, &r2)
+ if i > 0 {
+ Thearch.Gins(Thearch.Optoas(OADD, Types[TUINT]), Nodintconst(int64(i)), &r2)
+ }
+ w := res.Type.Type.Width
+ if Thearch.AddIndex != nil && Thearch.AddIndex(&r2, w, &r1) {
+ // r1 updated by back end
+ } else if w == 1 {
+ Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &r2, &r1)
+ } else {
+ Thearch.Ginscon(Thearch.Optoas(OMUL, Types[TUINT]), int64(w), &r2)
+ Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &r2, &r1)
+ }
+ Regfree(&r2)
+
+ r1.Op = OINDREG
+ r1.Type = res.Type.Type
+ cgen_wb(l.N, &r1, needwritebarrier(&r1, l.N))
+ Regfree(&r1)
+ i++
+ }
+}
+
+// Generate res = n, where n is x[i:j] or x[i:j:k].
+// If wb is true, need write barrier updating res's base pointer.
+// On systems with 32-bit ints, i, j, k are guaranteed to be 32-bit values.
+func cgen_slice(n, res *Node, wb bool) {
+ if Debug['g'] != 0 {
+ Dump("cgen_slice-n", n)
+ Dump("cgen_slice-res", res)
+ }
+
+ needFullUpdate := !samesafeexpr(n.Left, res)
+
+ // orderexpr has made sure that x is safe (but possibly expensive)
+ // and i, j, k are cheap. On a system with registers (anything but 386)
+ // we can evaluate x first and then know we have enough registers
+ // for i, j, k as well.
+ var x, xbase, xlen, xcap, i, j, k Node
+ if n.Op != OSLICEARR && n.Op != OSLICE3ARR {
+ Igen(n.Left, &x, nil)
+ }
+
+ indexRegType := Types[TUINT]
+ if Widthreg > Widthptr { // amd64p32
+ indexRegType = Types[TUINT64]
+ }
+
+ // On most systems, we use registers.
+ // The 386 has basically no registers, so substitute functions
+ // that can work with temporaries instead.
+ regalloc := Regalloc
+ ginscon := Thearch.Ginscon
+ gins := Thearch.Gins
+ if Thearch.Thechar == '8' {
+ regalloc = func(n *Node, t *Type, reuse *Node) {
+ Tempname(n, t)
+ }
+ ginscon = func(as int, c int64, n *Node) {
+ var n1 Node
+ Regalloc(&n1, n.Type, n)
+ Thearch.Gmove(n, &n1)
+ Thearch.Ginscon(as, c, &n1)
+ Thearch.Gmove(&n1, n)
+ Regfree(&n1)
+ }
+ gins = func(as int, f, t *Node) *obj.Prog {
+ var n1 Node
+ Regalloc(&n1, t.Type, t)
+ Thearch.Gmove(t, &n1)
+ Thearch.Gins(as, f, &n1)
+ Thearch.Gmove(&n1, t)
+ Regfree(&n1)
+ return nil
+ }
+ }
+
+ panics := make([]*obj.Prog, 0, 6) // 3 loads + 3 checks
+
+ loadlen := func() {
+ if xlen.Op != 0 {
+ return
+ }
+ if n.Op == OSLICEARR || n.Op == OSLICE3ARR {
+ Nodconst(&xlen, indexRegType, n.Left.Type.Type.Bound)
+ return
+ }
+ if n.Op == OSLICESTR && Isconst(n.Left, CTSTR) {
+ Nodconst(&xlen, indexRegType, int64(len(n.Left.Val.U.(string))))
+ return
+ }
+ regalloc(&xlen, indexRegType, nil)
+ x.Xoffset += int64(Widthptr)
+ x.Type = Types[TUINT]
+ Thearch.Gmove(&x, &xlen)
+ x.Xoffset -= int64(Widthptr)
+ }
+
+ loadcap := func() {
+ if xcap.Op != 0 {
+ return
+ }
+ if n.Op == OSLICEARR || n.Op == OSLICE3ARR || n.Op == OSLICESTR {
+ loadlen()
+ xcap = xlen
+ if xcap.Op == OREGISTER {
+ Regrealloc(&xcap)
+ }
+ return
+ }
+ regalloc(&xcap, indexRegType, nil)
+ x.Xoffset += 2 * int64(Widthptr)
+ x.Type = Types[TUINT]
+ Thearch.Gmove(&x, &xcap)
+ x.Xoffset -= 2 * int64(Widthptr)
+ }
+
+ var x1, x2, x3 *Node // unevaluated index arguments
+ x1 = n.Right.Left
+ switch n.Op {
+ default:
+ x2 = n.Right.Right
+ case OSLICE3, OSLICE3ARR:
+ x2 = n.Right.Right.Left
+ x3 = n.Right.Right.Right
+ }
+
+ // load computes src into targ, but if src refers to the len or cap of n.Left,
+ // load copies those from xlen, xcap, loading xlen if needed.
+ // If targ.Op == OREGISTER on return, it must be Regfreed,
+ // but it should not be modified without first checking whether it is
+ // xlen or xcap's register.
+ load := func(src, targ *Node) {
+ if src == nil {
+ return
+ }
+ switch src.Op {
+ case OLITERAL:
+ *targ = *src
+ return
+ case OLEN:
+ // NOTE(rsc): This doesn't actually trigger, because order.go
+ // has pulled all the len and cap calls into separate assignments
+ // to temporaries. There are tests in test/sliceopt.go that could
+ // be enabled if this is fixed.
+ if samesafeexpr(n.Left, src.Left) {
+ if Debug_slice > 0 {
+ Warn("slice: reuse len")
+ }
+ loadlen()
+ *targ = xlen
+ if targ.Op == OREGISTER {
+ Regrealloc(targ)
+ }
+ return
+ }
+ case OCAP:
+ // NOTE(rsc): This doesn't actually trigger; see note in case OLEN above.
+ if samesafeexpr(n.Left, src.Left) {
+ if Debug_slice > 0 {
+ Warn("slice: reuse cap")
+ }
+ loadcap()
+ *targ = xcap
+ if targ.Op == OREGISTER {
+ Regrealloc(targ)
+ }
+ return
+ }
+ }
+ if i.Op != 0 && samesafeexpr(x1, src) {
+ if Debug_slice > 0 {
+ Warn("slice: reuse 1st index")
+ }
+ *targ = i
+ if targ.Op == OREGISTER {
+ Regrealloc(targ)
+ }
+ return
+ }
+ if j.Op != 0 && samesafeexpr(x2, src) {
+ if Debug_slice > 0 {
+ Warn("slice: reuse 2nd index")
+ }
+ *targ = j
+ if targ.Op == OREGISTER {
+ Regrealloc(targ)
+ }
+ return
+ }
+ if Thearch.Cgenindex != nil {
+ regalloc(targ, indexRegType, nil)
+ p := Thearch.Cgenindex(src, targ, false)
+ if p != nil {
+ panics = append(panics, p)
+ }
+ } else if Thearch.Igenindex != nil {
+ p := Thearch.Igenindex(src, targ, false)
+ if p != nil {
+ panics = append(panics, p)
+ }
+ } else {
+ regalloc(targ, indexRegType, nil)
+ var tmp Node
+ Cgenr(src, &tmp, targ)
+ Thearch.Gmove(&tmp, targ)
+ Regfree(&tmp)
+ }
+ }
+
+ load(x1, &i)
+ load(x2, &j)
+ load(x3, &k)
+
+ // i defaults to 0.
+ if i.Op == 0 {
+ Nodconst(&i, indexRegType, 0)
+ }
+
+ // j defaults to len(x)
+ if j.Op == 0 {
+ loadlen()
+ j = xlen
+ if j.Op == OREGISTER {
+ Regrealloc(&j)
+ }
+ }
+
+ // k defaults to cap(x)
+ // Only need to load it if we're recalculating cap or doing a full update.
+ if k.Op == 0 && n.Op != OSLICESTR && (!iszero(&i) || needFullUpdate) {
+ loadcap()
+ k = xcap
+ if k.Op == OREGISTER {
+ Regrealloc(&k)
+ }
+ }
+
+ // Check constant indexes for negative values, and against constant length if known.
+ // The func obvious below checks for out-of-order constant indexes.
+ var bound int64 = -1
+ if n.Op == OSLICEARR || n.Op == OSLICE3ARR {
+ bound = n.Left.Type.Type.Bound
+ } else if n.Op == OSLICESTR && Isconst(n.Left, CTSTR) {
+ bound = int64(len(n.Left.Val.U.(string)))
+ }
+ if Isconst(&i, CTINT) {
+ if mpcmpfixc(i.Val.U.(*Mpint), 0) < 0 || bound >= 0 && mpcmpfixc(i.Val.U.(*Mpint), bound) > 0 {
+ Yyerror("slice index out of bounds")
+ }
+ }
+ if Isconst(&j, CTINT) {
+ if mpcmpfixc(j.Val.U.(*Mpint), 0) < 0 || bound >= 0 && mpcmpfixc(j.Val.U.(*Mpint), bound) > 0 {
+ Yyerror("slice index out of bounds")
+ }
+ }
+ if Isconst(&k, CTINT) {
+ if mpcmpfixc(k.Val.U.(*Mpint), 0) < 0 || bound >= 0 && mpcmpfixc(k.Val.U.(*Mpint), bound) > 0 {
+ Yyerror("slice index out of bounds")
+ }
+ }
+
+ // same reports whether n1 and n2 are the same register or constant.
+ same := func(n1, n2 *Node) bool {
+ return n1.Op == OREGISTER && n2.Op == OREGISTER && n1.Reg == n2.Reg ||
+ n1.Op == ONAME && n2.Op == ONAME && n1.Orig == n2.Orig && n1.Type == n2.Type && n1.Xoffset == n2.Xoffset ||
+ n1.Op == OLITERAL && n2.Op == OLITERAL && Mpcmpfixfix(n1.Val.U.(*Mpint), n2.Val.U.(*Mpint)) == 0
+ }
+
+ // obvious reports whether n1 <= n2 is obviously true,
+ // and it calls Yyerror if n1 <= n2 is obviously false.
+ obvious := func(n1, n2 *Node) bool {
+ if Debug['B'] != 0 { // -B disables bounds checks
+ return true
+ }
+ if same(n1, n2) {
+ return true // n1 == n2
+ }
+ if iszero(n1) {
+ return true // using unsigned compare, so 0 <= n2 always true
+ }
+ if xlen.Op != 0 && same(n1, &xlen) && xcap.Op != 0 && same(n2, &xcap) {
+ return true // len(x) <= cap(x) always true
+ }
+ if Isconst(n1, CTINT) && Isconst(n2, CTINT) {
+ if Mpcmpfixfix(n1.Val.U.(*Mpint), n2.Val.U.(*Mpint)) <= 0 {
+ return true // n1, n2 constants such that n1 <= n2
+ }
+ Yyerror("slice index out of bounds")
+ return true
+ }
+ return false
+ }
+
+ compare := func(n1, n2 *Node) {
+ // n1 might be a 64-bit constant, even on 32-bit architectures,
+ // but it will be represented in 32 bits.
+ if Ctxt.Arch.Regsize == 4 && Is64(n1.Type) {
+ if mpcmpfixc(n1.Val.U.(*Mpint), 1<<31) >= 0 {
+ Fatal("missed slice out of bounds check")
+ }
+ var tmp Node
+ Nodconst(&tmp, indexRegType, Mpgetfix(n1.Val.U.(*Mpint)))
+ n1 = &tmp
+ }
+ p := Thearch.Ginscmp(OGT, indexRegType, n1, n2, -1)
+ panics = append(panics, p)
+ }
+
+ loadcap()
+ max := &xcap
+ if k.Op != 0 && (n.Op == OSLICE3 || n.Op == OSLICE3ARR) {
+ if obvious(&k, max) {
+ if Debug_slice > 0 {
+ Warn("slice: omit check for 3rd index")
+ }
+ } else {
+ compare(&k, max)
+ }
+ max = &k
+ }
+ if j.Op != 0 {
+ if obvious(&j, max) {
+ if Debug_slice > 0 {
+ Warn("slice: omit check for 2nd index")
+ }
+ } else {
+ compare(&j, max)
+ }
+ max = &j
+ }
+ if i.Op != 0 {
+ if obvious(&i, max) {
+ if Debug_slice > 0 {
+ Warn("slice: omit check for 1st index")
+ }
+ } else {
+ compare(&i, max)
+ }
+ max = &i
+ }
+ if k.Op != 0 && i.Op != 0 {
+ obvious(&i, &k) // emit compile-time error for x[3:n:2]
+ }
+
+ if len(panics) > 0 {
+ p := Gbranch(obj.AJMP, nil, 0)
+ for _, q := range panics {
+ Patch(q, Pc)
+ }
+ Ginscall(panicslice, -1)
+ Patch(p, Pc)
+ }
+
+ // Checks are done.
+ // Compute new len as j-i, cap as k-i.
+ // If i and j are same register, len is constant 0.
+ // If i and k are same register, cap is constant 0.
+ // If j and k are same register, len and cap are same.
+
+ // Done with xlen and xcap.
+ // Now safe to modify j and k even if they alias xlen, xcap.
+ if xlen.Op == OREGISTER {
+ Regfree(&xlen)
+ }
+ if xcap.Op == OREGISTER {
+ Regfree(&xcap)
+ }
+
+ // are j and k the same value?
+ sameJK := same(&j, &k)
+
+ if i.Op != 0 {
+ // j -= i
+ if same(&i, &j) {
+ if Debug_slice > 0 {
+ Warn("slice: result len == 0")
+ }
+ if j.Op == OREGISTER {
+ Regfree(&j)
+ }
+ Nodconst(&j, indexRegType, 0)
+ } else {
+ switch j.Op {
+ case OLITERAL:
+ if Isconst(&i, CTINT) {
+ Nodconst(&j, indexRegType, Mpgetfix(j.Val.U.(*Mpint))-Mpgetfix(i.Val.U.(*Mpint)))
+ if Debug_slice > 0 {
+ Warn("slice: result len == %d", Mpgetfix(j.Val.U.(*Mpint)))
+ }
+ break
+ }
+ fallthrough
+ case ONAME:
+ if !istemp(&j) {
+ var r Node
+ regalloc(&r, indexRegType, nil)
+ Thearch.Gmove(&j, &r)
+ j = r
+ }
+ fallthrough
+ case OREGISTER:
+ if i.Op == OLITERAL {
+ v := Mpgetfix(i.Val.U.(*Mpint))
+ if v != 0 {
+ ginscon(Thearch.Optoas(OSUB, indexRegType), v, &j)
+ }
+ } else {
+ gins(Thearch.Optoas(OSUB, indexRegType), &i, &j)
+ }
+ }
+ }
+
+ // k -= i if k different from j and cap is needed.j
+ // (The modifications to j above cannot affect i: if j and i were aliased,
+ // we replace j with a constant 0 instead of doing a subtraction,
+ // leaving i unmodified.)
+ if k.Op == 0 {
+ if Debug_slice > 0 && n.Op != OSLICESTR {
+ Warn("slice: result cap not computed")
+ }
+ // no need
+ } else if same(&i, &k) {
+ if k.Op == OREGISTER {
+ Regfree(&k)
+ }
+ Nodconst(&k, indexRegType, 0)
+ if Debug_slice > 0 {
+ Warn("slice: result cap == 0")
+ }
+ } else if sameJK {
+ if Debug_slice > 0 {
+ Warn("slice: result cap == result len")
+ }
+ // k and j were the same value; make k-i the same as j-i.
+ if k.Op == OREGISTER {
+ Regfree(&k)
+ }
+ k = j
+ if k.Op == OREGISTER {
+ Regrealloc(&k)
+ }
+ } else {
+ switch k.Op {
+ case OLITERAL:
+ if Isconst(&i, CTINT) {
+ Nodconst(&k, indexRegType, Mpgetfix(k.Val.U.(*Mpint))-Mpgetfix(i.Val.U.(*Mpint)))
+ if Debug_slice > 0 {
+ Warn("slice: result cap == %d", Mpgetfix(k.Val.U.(*Mpint)))
+ }
+ break
+ }
+ fallthrough
+ case ONAME:
+ if !istemp(&k) {
+ var r Node
+ regalloc(&r, indexRegType, nil)
+ Thearch.Gmove(&k, &r)
+ k = r
+ }
+ fallthrough
+ case OREGISTER:
+ if same(&i, &k) {
+ Regfree(&k)
+ Nodconst(&k, indexRegType, 0)
+ if Debug_slice > 0 {
+ Warn("slice: result cap == 0")
+ }
+ } else if i.Op == OLITERAL {
+ v := Mpgetfix(i.Val.U.(*Mpint))
+ if v != 0 {
+ ginscon(Thearch.Optoas(OSUB, indexRegType), v, &k)
+ }
+ } else {
+ gins(Thearch.Optoas(OSUB, indexRegType), &i, &k)
+ }
+ }
+ }
+ }
+
+ adjustBase := true
+ if i.Op == 0 || iszero(&i) {
+ if Debug_slice > 0 {
+ Warn("slice: skip base adjustment for 1st index 0")
+ }
+ adjustBase = false
+ } else if k.Op != 0 && iszero(&k) || k.Op == 0 && iszero(&j) {
+ if Debug_slice > 0 {
+ if n.Op == OSLICESTR {
+ Warn("slice: skip base adjustment for string len == 0")
+ } else {
+ Warn("slice: skip base adjustment for cap == 0")
+ }
+ }
+ adjustBase = false
+ }
+
+ if !adjustBase && !needFullUpdate {
+ if Debug_slice > 0 {
+ if k.Op != 0 {
+ Warn("slice: len/cap-only update")
+ } else {
+ Warn("slice: len-only update")
+ }
+ }
+ if i.Op == OREGISTER {
+ Regfree(&i)
+ }
+ // Write len (and cap if needed) back to x.
+ x.Xoffset += int64(Widthptr)
+ x.Type = Types[TUINT]
+ Thearch.Gmove(&j, &x)
+ x.Xoffset -= int64(Widthptr)
+ if k.Op != 0 {
+ x.Xoffset += 2 * int64(Widthptr)
+ x.Type = Types[TUINT]
+ Thearch.Gmove(&k, &x)
+ x.Xoffset -= 2 * int64(Widthptr)
+ }
+ Regfree(&x)
+ } else {
+ // Compute new base. May smash i.
+ if n.Op == OSLICEARR || n.Op == OSLICE3ARR {
+ Cgenr(n.Left, &xbase, nil)
+ Cgen_checknil(&xbase)
+ } else {
+ regalloc(&xbase, Ptrto(res.Type.Type), nil)
+ x.Type = xbase.Type
+ Thearch.Gmove(&x, &xbase)
+ Regfree(&x)
+ }
+ if i.Op != 0 && adjustBase {
+ // Branch around the base adjustment if the resulting cap will be 0.
+ var p *obj.Prog
+ size := &k
+ if k.Op == 0 {
+ size = &j
+ }
+ if Isconst(size, CTINT) {
+ // zero was checked above, must be non-zero.
+ } else {
+ var tmp Node
+ Nodconst(&tmp, indexRegType, 0)
+ p = Thearch.Ginscmp(OEQ, indexRegType, size, &tmp, -1)
+ }
+ var w int64
+ if n.Op == OSLICESTR {
+ w = 1 // res is string, elem size is 1 (byte)
+ } else {
+ w = res.Type.Type.Width // res is []T, elem size is T.width
+ }
+ if Isconst(&i, CTINT) {
+ ginscon(Thearch.Optoas(OADD, xbase.Type), Mpgetfix(i.Val.U.(*Mpint))*w, &xbase)
+ } else if Thearch.AddIndex != nil && Thearch.AddIndex(&i, w, &xbase) {
+ // done by back end
+ } else if w == 1 {
+ gins(Thearch.Optoas(OADD, xbase.Type), &i, &xbase)
+ } else {
+ if i.Op == ONAME && !istemp(&i) {
+ var tmp Node
+ Tempname(&tmp, i.Type)
+ Thearch.Gmove(&i, &tmp)
+ i = tmp
+ }
+ ginscon(Thearch.Optoas(OMUL, i.Type), w, &i)
+ gins(Thearch.Optoas(OADD, xbase.Type), &i, &xbase)
+ }
+ if p != nil {
+ Patch(p, Pc)
+ }
+ }
+ if i.Op == OREGISTER {
+ Regfree(&i)
+ }
+
+ // Write len, cap, base to result.
+ if res.Op == ONAME {
+ Gvardef(res)
+ }
+ Igen(res, &x, nil)
+ x.Xoffset += int64(Widthptr)
+ x.Type = Types[TUINT]
+ Thearch.Gmove(&j, &x)
+ x.Xoffset -= int64(Widthptr)
+ if k.Op != 0 {
+ x.Xoffset += 2 * int64(Widthptr)
+ Thearch.Gmove(&k, &x)
+ x.Xoffset -= 2 * int64(Widthptr)
+ }
+ x.Type = xbase.Type
+ cgen_wb(&xbase, &x, wb)
+ Regfree(&xbase)
+ Regfree(&x)
+ }
+
+ if j.Op == OREGISTER {
+ Regfree(&j)
+ }
+ if k.Op == OREGISTER {
+ Regfree(&k)
+ }
+}
diff --git a/src/cmd/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go
similarity index 91%
rename from src/cmd/internal/gc/closure.go
rename to src/cmd/compile/internal/gc/closure.go
index 8d5fd5a600..64cd97206c 100644
--- a/src/cmd/internal/gc/closure.go
+++ b/src/cmd/compile/internal/gc/closure.go
@@ -17,7 +17,7 @@ func closurehdr(ntype *Node) {
var a *Node
n := Nod(OCLOSURE, nil, nil)
- n.Ntype = ntype
+ n.Param.Ntype = ntype
n.Funcdepth = Funcdepth
n.Func.Outerfunc = Curfn
@@ -72,8 +72,8 @@ func closurebody(body *NodeList) *Node {
var v *Node
for l := func_.Func.Cvars; l != nil; l = l.Next {
v = l.N
- v.Closure.Closure = v.Outer
- v.Outerexpr = oldname(v.Sym)
+ v.Param.Closure.Param.Closure = v.Param.Outer
+ v.Param.Outerexpr = oldname(v.Sym)
}
return func_
@@ -83,16 +83,16 @@ func typecheckclosure(func_ *Node, top int) {
var n *Node
for l := func_.Func.Cvars; l != nil; l = l.Next {
- n = l.N.Closure
- if !n.Captured {
- n.Captured = true
- if n.Decldepth == 0 {
+ n = l.N.Param.Closure
+ if !n.Name.Captured {
+ n.Name.Captured = true
+ if n.Name.Decldepth == 0 {
Fatal("typecheckclosure: var %v does not have decldepth assigned", Nconv(n, obj.FmtShort))
}
// Ignore assignments to the variable in straightline code
// preceding the first capturing by a closure.
- if n.Decldepth == decldepth {
+ if n.Name.Decldepth == decldepth {
n.Assigned = false
}
}
@@ -100,14 +100,14 @@ 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.Decldepth = 1
+ l.N.Name.Decldepth = 1
}
}
oldfn := Curfn
- typecheck(&func_.Ntype, Etype)
- func_.Type = func_.Ntype.Type
- func_.Top = top
+ typecheck(&func_.Param.Ntype, Etype)
+ func_.Type = func_.Param.Ntype.Type
+ func_.Param.Top = top
// Type check the body now, but only if we're inside a function.
// At top level (in a variable initialization: curfn==nil) we're not
@@ -193,7 +193,7 @@ func makeclosure(func_ *Node) *Node {
xfunc.Nname = newfuncname(closurename(func_))
xfunc.Nname.Sym.Flags |= SymExported // disable export
- xfunc.Nname.Ntype = xtype
+ xfunc.Nname.Param.Ntype = xtype
xfunc.Nname.Defn = xfunc
declare(xfunc.Nname, PFUNC)
xfunc.Nname.Funcdepth = func_.Funcdepth
@@ -207,8 +207,8 @@ func makeclosure(func_ *Node) *Node {
}
typecheck(&xfunc, Etop)
- xfunc.Closure = func_
- func_.Closure = xfunc
+ xfunc.Param.Closure = func_
+ func_.Param.Closure = xfunc
func_.Nbody = nil
func_.List = nil
@@ -229,7 +229,7 @@ func capturevars(xfunc *Node) {
lno := int(lineno)
lineno = xfunc.Lineno
- func_ := xfunc.Closure
+ func_ := xfunc.Param.Closure
func_.Func.Enter = nil
for l := func_.Func.Cvars; l != nil; l = l.Next {
v = l.N
@@ -249,14 +249,14 @@ func capturevars(xfunc *Node) {
// so that the outer frame also grabs them and knows they escape.
dowidth(v.Type)
- outer = v.Outerexpr
- v.Outerexpr = nil
+ outer = v.Param.Outerexpr
+ v.Param.Outerexpr = nil
// out parameters will be assigned to implicitly upon return.
- if outer.Class != PPARAMOUT && !v.Closure.Addrtaken && !v.Closure.Assigned && v.Type.Width <= 128 {
- v.Byval = true
+ if outer.Class != PPARAMOUT && !v.Param.Closure.Addrtaken && !v.Param.Closure.Assigned && v.Type.Width <= 128 {
+ v.Name.Byval = true
} else {
- v.Closure.Addrtaken = true
+ v.Param.Closure.Addrtaken = true
outer = Nod(OADDR, outer, nil)
}
@@ -266,10 +266,10 @@ func capturevars(xfunc *Node) {
name = v.Curfn.Nname.Sym
}
how := "ref"
- if v.Byval {
+ if v.Name.Byval {
how = "value"
}
- Warnl(int(v.Lineno), "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, v.Closure.Addrtaken, v.Closure.Assigned, int32(v.Type.Width))
+ Warnl(int(v.Lineno), "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, v.Param.Closure.Addrtaken, v.Param.Closure.Assigned, int32(v.Type.Width))
}
typecheck(&outer, Erv)
@@ -284,9 +284,9 @@ func capturevars(xfunc *Node) {
func transformclosure(xfunc *Node) {
lno := int(lineno)
lineno = xfunc.Lineno
- func_ := xfunc.Closure
+ func_ := xfunc.Param.Closure
- if func_.Top&Ecall != 0 {
+ if func_.Param.Top&Ecall != 0 {
// If the closure is directly called, we transform it to a plain function call
// with variables passed as args. This avoids allocation of a closure object.
// Here we do only a part of the transformation. Walk of OCALLFUNC(OCLOSURE)
@@ -321,7 +321,7 @@ func transformclosure(xfunc *Node) {
}
fld = typ(TFIELD)
fld.Funarg = 1
- if v.Byval {
+ if v.Name.Byval {
// If v is captured by value, we merely downgrade it to PPARAM.
v.Class = PPARAM
@@ -335,7 +335,7 @@ func transformclosure(xfunc *Node) {
addr = newname(Lookupf("&%s", v.Sym.Name))
addr.Type = Ptrto(v.Type)
addr.Class = PPARAM
- v.Heapaddr = addr
+ v.Name.Heapaddr = addr
fld.Nname = addr
}
@@ -375,14 +375,14 @@ func transformclosure(xfunc *Node) {
cv = Nod(OCLOSUREVAR, nil, nil)
cv.Type = v.Type
- if !v.Byval {
+ if !v.Name.Byval {
cv.Type = Ptrto(v.Type)
}
offset = Rnd(offset, int64(cv.Type.Align))
cv.Xoffset = offset
offset += cv.Type.Width
- if v.Byval && v.Type.Width <= int64(2*Widthptr) && Thearch.Thechar == '6' {
+ if v.Name.Byval && v.Type.Width <= int64(2*Widthptr) && Thearch.Thechar == '6' {
// If it is a small variable captured by value, downgrade it to PAUTO.
// This optimization is currently enabled only for amd64, see:
// https://github.com/golang/go/issues/9865
@@ -395,13 +395,13 @@ func transformclosure(xfunc *Node) {
// Declare variable holding addresses taken from closure
// and initialize in entry prologue.
addr = newname(Lookupf("&%s", v.Sym.Name))
- addr.Ntype = Nod(OIND, typenod(v.Type), nil)
+ addr.Param.Ntype = Nod(OIND, typenod(v.Type), nil)
addr.Class = PAUTO
addr.Used = true
addr.Curfn = xfunc
xfunc.Func.Dcl = list(xfunc.Func.Dcl, addr)
- v.Heapaddr = addr
- if v.Byval {
+ v.Name.Heapaddr = addr
+ if v.Name.Byval {
cv = Nod(OADDR, cv, nil)
}
body = list(body, Nod(OAS, addr, cv))
@@ -420,7 +420,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 {
- return func_.Closure.Nname
+ return func_.Param.Closure.Nname
}
// Create closure in the form of a composite literal.
@@ -448,7 +448,7 @@ func walkclosure(func_ *Node, init **NodeList) *Node {
continue
}
typ1 = typenod(v.Type)
- if !v.Byval {
+ if !v.Name.Byval {
typ1 = Nod(OIND, typ1, nil)
}
typ.List = list(typ.List, Nod(ODCLFIELD, newname(v.Sym), typ1))
@@ -457,7 +457,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_.Closure.Nname, nil)), func_.Func.Enter)
+ clos.List = concat(list1(Nod(OCFUNC, func_.Param.Closure.Nname, nil)), func_.Func.Enter)
// Force type conversion from *struct to the func type.
clos = Nod(OCONVNOP, clos, nil)
@@ -583,7 +583,7 @@ func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node {
xfunc.Func.Dupok = true
xfunc.Nname = newfuncname(sym)
xfunc.Nname.Sym.Flags |= SymExported // disable export
- xfunc.Nname.Ntype = xtype
+ xfunc.Nname.Param.Ntype = xtype
xfunc.Nname.Defn = xfunc
declare(xfunc.Nname, PFUNC)
@@ -606,10 +606,10 @@ func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node {
xfunc.Func.Dcl = list(xfunc.Func.Dcl, ptr)
var body *NodeList
if Isptr[rcvrtype.Etype] || Isinter(rcvrtype) {
- ptr.Ntype = typenod(rcvrtype)
+ ptr.Param.Ntype = typenod(rcvrtype)
body = list(body, Nod(OAS, ptr, cv))
} else {
- ptr.Ntype = typenod(Ptrto(rcvrtype))
+ ptr.Param.Ntype = typenod(Ptrto(rcvrtype))
body = list(body, Nod(OAS, ptr, Nod(OADDR, cv, nil)))
}
diff --git a/src/cmd/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go
similarity index 78%
rename from src/cmd/internal/gc/const.go
rename to src/cmd/compile/internal/gc/const.go
index ad2915812e..b3605ab206 100644
--- a/src/cmd/internal/gc/const.go
+++ b/src/cmd/compile/internal/gc/const.go
@@ -5,10 +5,47 @@
package gc
import (
+ "cmd/compile/internal/big"
"cmd/internal/obj"
"strings"
)
+// Int returns n as an int.
+// n must be an integer constant.
+func (n *Node) Int() int64 {
+ if !Isconst(n, CTINT) {
+ Fatal("Int(%v)", n)
+ }
+ return Mpgetfix(n.Val.U.(*Mpint))
+}
+
+// SetInt sets n's value to i.
+// n must be an integer constant.
+func (n *Node) SetInt(i int64) {
+ if !Isconst(n, CTINT) {
+ Fatal("SetInt(%v)", n)
+ }
+ Mpmovecfix(n.Val.U.(*Mpint), i)
+}
+
+// SetBigInt sets n's value to x.
+// n must be an integer constant.
+func (n *Node) SetBigInt(x *big.Int) {
+ if !Isconst(n, CTINT) {
+ Fatal("SetBigInt(%v)", n)
+ }
+ n.Val.U.(*Mpint).Val.Set(x)
+}
+
+// Bool returns n as an bool.
+// n must be an boolean constant.
+func (n *Node) Bool() bool {
+ if !Isconst(n, CTBOOL) {
+ Fatal("Int(%v)", n)
+ }
+ return n.Val.U.(bool)
+}
+
/*
* truncate float literal fv to 32-bit or 64-bit precision
* according to type; return truncated value.
@@ -20,7 +57,7 @@ func truncfltlit(oldv *Mpflt, t *Type) *Mpflt {
var v Val
v.Ctype = CTFLT
- v.U.Fval = oldv
+ v.U = oldv
overflow(v, t)
fv := newMpflt()
@@ -190,8 +227,8 @@ func convlit1(np **Node, t *Type, explicit bool) {
// if it is an unsafe.Pointer
case TUINTPTR:
if n.Type.Etype == TUNSAFEPTR {
- n.Val.U.Xval = new(Mpint)
- Mpmovecfix(n.Val.U.Xval, 0)
+ n.Val.U = new(Mpint)
+ Mpmovecfix(n.Val.U.(*Mpint), 0)
n.Val.Ctype = CTINT
} else {
goto bad
@@ -204,6 +241,9 @@ func convlit1(np **Node, t *Type, explicit bool) {
}
case CTINT, CTRUNE, CTFLT, CTCPLX:
+ if n.Type.Etype == TUNSAFEPTR && t.Etype != TUINTPTR {
+ goto bad
+ }
ct := int(n.Val.Ctype)
if Isint[et] {
switch ct {
@@ -229,7 +269,7 @@ func convlit1(np **Node, t *Type, explicit bool) {
// flowthrough
case CTFLT:
- n.Val.U.Fval = truncfltlit(n.Val.U.Fval, t)
+ n.Val.U = truncfltlit(n.Val.U.(*Mpflt), t)
}
} else if Iscomplex[et] {
switch ct {
@@ -264,27 +304,25 @@ bad:
defaultlit(&n, nil)
*np = n
}
-
- return
}
func copyval(v Val) Val {
switch v.Ctype {
case CTINT, CTRUNE:
i := new(Mpint)
- mpmovefixfix(i, v.U.Xval)
- v.U.Xval = i
+ mpmovefixfix(i, v.U.(*Mpint))
+ v.U = i
case CTFLT:
f := newMpflt()
- mpmovefltflt(f, v.U.Fval)
- v.U.Fval = f
+ mpmovefltflt(f, v.U.(*Mpflt))
+ v.U = f
case CTCPLX:
c := new(Mpcplx)
- mpmovefltflt(&c.Real, &v.U.Cval.Real)
- mpmovefltflt(&c.Imag, &v.U.Cval.Imag)
- v.U.Cval = c
+ mpmovefltflt(&c.Real, &v.U.(*Mpcplx).Real)
+ mpmovefltflt(&c.Imag, &v.U.(*Mpcplx).Imag)
+ v.U = c
}
return v
@@ -294,17 +332,17 @@ func tocplx(v Val) Val {
switch v.Ctype {
case CTINT, CTRUNE:
c := new(Mpcplx)
- Mpmovefixflt(&c.Real, v.U.Xval)
+ Mpmovefixflt(&c.Real, v.U.(*Mpint))
Mpmovecflt(&c.Imag, 0.0)
v.Ctype = CTCPLX
- v.U.Cval = c
+ v.U = c
case CTFLT:
c := new(Mpcplx)
- mpmovefltflt(&c.Real, v.U.Fval)
+ mpmovefltflt(&c.Real, v.U.(*Mpflt))
Mpmovecflt(&c.Imag, 0.0)
v.Ctype = CTCPLX
- v.U.Cval = c
+ v.U = c
}
return v
@@ -314,18 +352,18 @@ func toflt(v Val) Val {
switch v.Ctype {
case CTINT, CTRUNE:
f := newMpflt()
- Mpmovefixflt(f, v.U.Xval)
+ Mpmovefixflt(f, v.U.(*Mpint))
v.Ctype = CTFLT
- v.U.Fval = f
+ v.U = f
case CTCPLX:
f := newMpflt()
- mpmovefltflt(f, &v.U.Cval.Real)
- if mpcmpfltc(&v.U.Cval.Imag, 0) != 0 {
- Yyerror("constant %v%vi truncated to real", Fconv(&v.U.Cval.Real, obj.FmtSharp), Fconv(&v.U.Cval.Imag, obj.FmtSharp|obj.FmtSign))
+ mpmovefltflt(f, &v.U.(*Mpcplx).Real)
+ if mpcmpfltc(&v.U.(*Mpcplx).Imag, 0) != 0 {
+ Yyerror("constant %v%vi truncated to real", Fconv(&v.U.(*Mpcplx).Real, obj.FmtSharp), Fconv(&v.U.(*Mpcplx).Imag, obj.FmtSharp|obj.FmtSign))
}
v.Ctype = CTFLT
- v.U.Fval = f
+ v.U = f
}
return v
@@ -338,22 +376,22 @@ func toint(v Val) Val {
case CTFLT:
i := new(Mpint)
- if mpmovefltfix(i, v.U.Fval) < 0 {
- Yyerror("constant %v truncated to integer", Fconv(v.U.Fval, obj.FmtSharp))
+ if mpmovefltfix(i, v.U.(*Mpflt)) < 0 {
+ Yyerror("constant %v truncated to integer", Fconv(v.U.(*Mpflt), obj.FmtSharp))
}
v.Ctype = CTINT
- v.U.Xval = i
+ v.U = i
case CTCPLX:
i := new(Mpint)
- if mpmovefltfix(i, &v.U.Cval.Real) < 0 {
- Yyerror("constant %v%vi truncated to integer", Fconv(&v.U.Cval.Real, obj.FmtSharp), Fconv(&v.U.Cval.Imag, obj.FmtSharp|obj.FmtSign))
+ if mpmovefltfix(i, &v.U.(*Mpcplx).Real) < 0 {
+ Yyerror("constant %v%vi truncated to integer", Fconv(&v.U.(*Mpcplx).Real, obj.FmtSharp), Fconv(&v.U.(*Mpcplx).Imag, obj.FmtSharp|obj.FmtSign))
}
- if mpcmpfltc(&v.U.Cval.Imag, 0) != 0 {
- Yyerror("constant %v%vi truncated to real", Fconv(&v.U.Cval.Real, obj.FmtSharp), Fconv(&v.U.Cval.Imag, obj.FmtSharp|obj.FmtSign))
+ if mpcmpfltc(&v.U.(*Mpcplx).Imag, 0) != 0 {
+ Yyerror("constant %v%vi truncated to real", Fconv(&v.U.(*Mpcplx).Real, obj.FmtSharp), Fconv(&v.U.(*Mpcplx).Imag, obj.FmtSharp|obj.FmtSign))
}
v.Ctype = CTINT
- v.U.Xval = i
+ v.U = i
}
return v
@@ -365,7 +403,7 @@ func doesoverflow(v Val, t *Type) bool {
if !Isint[t.Etype] {
Fatal("overflow: %v integer constant", t)
}
- if Mpcmpfixfix(v.U.Xval, Minintval[t.Etype]) < 0 || Mpcmpfixfix(v.U.Xval, Maxintval[t.Etype]) > 0 {
+ if Mpcmpfixfix(v.U.(*Mpint), Minintval[t.Etype]) < 0 || Mpcmpfixfix(v.U.(*Mpint), Maxintval[t.Etype]) > 0 {
return true
}
@@ -373,7 +411,7 @@ func doesoverflow(v Val, t *Type) bool {
if !Isfloat[t.Etype] {
Fatal("overflow: %v floating-point constant", t)
}
- if mpcmpfltflt(v.U.Fval, minfltval[t.Etype]) <= 0 || mpcmpfltflt(v.U.Fval, maxfltval[t.Etype]) >= 0 {
+ if mpcmpfltflt(v.U.(*Mpflt), minfltval[t.Etype]) <= 0 || mpcmpfltflt(v.U.(*Mpflt), maxfltval[t.Etype]) >= 0 {
return true
}
@@ -381,7 +419,7 @@ func doesoverflow(v Val, t *Type) bool {
if !Iscomplex[t.Etype] {
Fatal("overflow: %v complex constant", t)
}
- if mpcmpfltflt(&v.U.Cval.Real, minfltval[t.Etype]) <= 0 || mpcmpfltflt(&v.U.Cval.Real, maxfltval[t.Etype]) >= 0 || mpcmpfltflt(&v.U.Cval.Imag, minfltval[t.Etype]) <= 0 || mpcmpfltflt(&v.U.Cval.Imag, maxfltval[t.Etype]) >= 0 {
+ if mpcmpfltflt(&v.U.(*Mpcplx).Real, minfltval[t.Etype]) <= 0 || mpcmpfltflt(&v.U.(*Mpcplx).Real, maxfltval[t.Etype]) >= 0 || mpcmpfltflt(&v.U.(*Mpcplx).Imag, minfltval[t.Etype]) <= 0 || mpcmpfltflt(&v.U.(*Mpcplx).Imag, maxfltval[t.Etype]) >= 0 {
return true
}
}
@@ -396,32 +434,37 @@ func overflow(v Val, t *Type) {
return
}
+ // Only uintptrs may be converted to unsafe.Pointer, which cannot overflow.
+ if t.Etype == TUNSAFEPTR {
+ return
+ }
+
if !doesoverflow(v, t) {
return
}
switch v.Ctype {
case CTINT, CTRUNE:
- Yyerror("constant %v overflows %v", v.U.Xval, t)
+ Yyerror("constant %v overflows %v", v.U.(*Mpint), t)
case CTFLT:
- Yyerror("constant %v overflows %v", Fconv(v.U.Fval, obj.FmtSharp), t)
+ Yyerror("constant %v overflows %v", Fconv(v.U.(*Mpflt), obj.FmtSharp), t)
case CTCPLX:
- Yyerror("constant %v overflows %v", Fconv(v.U.Fval, obj.FmtSharp), t)
+ Yyerror("constant %v overflows %v", Fconv(v.U.(*Mpflt), obj.FmtSharp), t)
}
}
func tostr(v Val) Val {
switch v.Ctype {
case CTINT, CTRUNE:
- if Mpcmpfixfix(v.U.Xval, Minintval[TINT]) < 0 || Mpcmpfixfix(v.U.Xval, Maxintval[TINT]) > 0 {
+ if Mpcmpfixfix(v.U.(*Mpint), Minintval[TINT]) < 0 || Mpcmpfixfix(v.U.(*Mpint), Maxintval[TINT]) > 0 {
Yyerror("overflow in int -> string")
}
- r := uint(Mpgetfix(v.U.Xval))
+ r := uint(Mpgetfix(v.U.(*Mpint)))
v = Val{}
v.Ctype = CTSTR
- v.U.Sval = string(r)
+ v.U = string(r)
case CTFLT:
Yyerror("no float -> string")
@@ -430,7 +473,7 @@ func tostr(v Val) Val {
case CTNIL:
v = Val{}
v.Ctype = CTSTR
- v.U.Sval = ""
+ v.U = ""
}
return v
@@ -519,7 +562,7 @@ func evconst(n *Node) {
l2 = l1
for l2 != nil && Isconst(l2.N, CTSTR) {
nr = l2.N
- strs = append(strs, nr.Val.U.Sval)
+ strs = append(strs, nr.Val.U.(string))
l2 = l2.Next
}
@@ -527,7 +570,7 @@ func evconst(n *Node) {
*nl = *l1.N
nl.Orig = nl
nl.Val.Ctype = CTSTR
- nl.Val.U.Sval = strings.Join(strs, "")
+ nl.Val.U = strings.Join(strs, "")
l1.N = nl
l1.Next = l2
}
@@ -607,7 +650,7 @@ func evconst(n *Node) {
case OMINUS<<16 | CTINT,
OMINUS<<16 | CTRUNE:
- mpnegfix(v.U.Xval)
+ mpnegfix(v.U.(*Mpint))
case OCOM<<16 | CTINT,
OCOM<<16 | CTRUNE:
@@ -634,23 +677,23 @@ func evconst(n *Node) {
mpmovefixfix(&b, Maxintval[et])
}
- mpxorfixfix(v.U.Xval, &b)
+ mpxorfixfix(v.U.(*Mpint), &b)
case OPLUS<<16 | CTFLT:
break
case OMINUS<<16 | CTFLT:
- mpnegflt(v.U.Fval)
+ mpnegflt(v.U.(*Mpflt))
case OPLUS<<16 | CTCPLX:
break
case OMINUS<<16 | CTCPLX:
- mpnegflt(&v.U.Cval.Real)
- mpnegflt(&v.U.Cval.Imag)
+ mpnegflt(&v.U.(*Mpcplx).Real)
+ mpnegflt(&v.U.(*Mpcplx).Imag)
case ONOT<<16 | CTBOOL:
- if !v.U.Bval {
+ if !v.U.(bool) {
goto settrue
}
goto setfalse
@@ -754,77 +797,77 @@ func evconst(n *Node) {
case OADD<<16 | CTINT,
OADD<<16 | CTRUNE:
- mpaddfixfix(v.U.Xval, rv.U.Xval, 0)
+ mpaddfixfix(v.U.(*Mpint), rv.U.(*Mpint), 0)
case OSUB<<16 | CTINT,
OSUB<<16 | CTRUNE:
- mpsubfixfix(v.U.Xval, rv.U.Xval)
+ mpsubfixfix(v.U.(*Mpint), rv.U.(*Mpint))
case OMUL<<16 | CTINT,
OMUL<<16 | CTRUNE:
- mpmulfixfix(v.U.Xval, rv.U.Xval)
+ mpmulfixfix(v.U.(*Mpint), rv.U.(*Mpint))
case ODIV<<16 | CTINT,
ODIV<<16 | CTRUNE:
- if mpcmpfixc(rv.U.Xval, 0) == 0 {
+ if mpcmpfixc(rv.U.(*Mpint), 0) == 0 {
Yyerror("division by zero")
- Mpmovecfix(v.U.Xval, 1)
+ mpsetovf(v.U.(*Mpint))
break
}
- mpdivfixfix(v.U.Xval, rv.U.Xval)
+ mpdivfixfix(v.U.(*Mpint), rv.U.(*Mpint))
case OMOD<<16 | CTINT,
OMOD<<16 | CTRUNE:
- if mpcmpfixc(rv.U.Xval, 0) == 0 {
+ if mpcmpfixc(rv.U.(*Mpint), 0) == 0 {
Yyerror("division by zero")
- Mpmovecfix(v.U.Xval, 1)
+ mpsetovf(v.U.(*Mpint))
break
}
- mpmodfixfix(v.U.Xval, rv.U.Xval)
+ mpmodfixfix(v.U.(*Mpint), rv.U.(*Mpint))
case OLSH<<16 | CTINT,
OLSH<<16 | CTRUNE:
- mplshfixfix(v.U.Xval, rv.U.Xval)
+ mplshfixfix(v.U.(*Mpint), rv.U.(*Mpint))
case ORSH<<16 | CTINT,
ORSH<<16 | CTRUNE:
- mprshfixfix(v.U.Xval, rv.U.Xval)
+ mprshfixfix(v.U.(*Mpint), rv.U.(*Mpint))
case OOR<<16 | CTINT,
OOR<<16 | CTRUNE:
- mporfixfix(v.U.Xval, rv.U.Xval)
+ mporfixfix(v.U.(*Mpint), rv.U.(*Mpint))
case OAND<<16 | CTINT,
OAND<<16 | CTRUNE:
- mpandfixfix(v.U.Xval, rv.U.Xval)
+ mpandfixfix(v.U.(*Mpint), rv.U.(*Mpint))
case OANDNOT<<16 | CTINT,
OANDNOT<<16 | CTRUNE:
- mpandnotfixfix(v.U.Xval, rv.U.Xval)
+ mpandnotfixfix(v.U.(*Mpint), rv.U.(*Mpint))
case OXOR<<16 | CTINT,
OXOR<<16 | CTRUNE:
- mpxorfixfix(v.U.Xval, rv.U.Xval)
+ mpxorfixfix(v.U.(*Mpint), rv.U.(*Mpint))
case OADD<<16 | CTFLT:
- mpaddfltflt(v.U.Fval, rv.U.Fval)
+ mpaddfltflt(v.U.(*Mpflt), rv.U.(*Mpflt))
case OSUB<<16 | CTFLT:
- mpsubfltflt(v.U.Fval, rv.U.Fval)
+ mpsubfltflt(v.U.(*Mpflt), rv.U.(*Mpflt))
case OMUL<<16 | CTFLT:
- mpmulfltflt(v.U.Fval, rv.U.Fval)
+ mpmulfltflt(v.U.(*Mpflt), rv.U.(*Mpflt))
case ODIV<<16 | CTFLT:
- if mpcmpfltc(rv.U.Fval, 0) == 0 {
+ if mpcmpfltc(rv.U.(*Mpflt), 0) == 0 {
Yyerror("division by zero")
- Mpmovecflt(v.U.Fval, 1.0)
+ Mpmovecflt(v.U.(*Mpflt), 1.0)
break
}
- mpdivfltflt(v.U.Fval, rv.U.Fval)
+ mpdivfltflt(v.U.(*Mpflt), rv.U.(*Mpflt))
// The default case above would print 'ideal % ideal',
// which is not quite an ideal error.
@@ -837,25 +880,25 @@ func evconst(n *Node) {
return
case OADD<<16 | CTCPLX:
- mpaddfltflt(&v.U.Cval.Real, &rv.U.Cval.Real)
- mpaddfltflt(&v.U.Cval.Imag, &rv.U.Cval.Imag)
+ mpaddfltflt(&v.U.(*Mpcplx).Real, &rv.U.(*Mpcplx).Real)
+ mpaddfltflt(&v.U.(*Mpcplx).Imag, &rv.U.(*Mpcplx).Imag)
case OSUB<<16 | CTCPLX:
- mpsubfltflt(&v.U.Cval.Real, &rv.U.Cval.Real)
- mpsubfltflt(&v.U.Cval.Imag, &rv.U.Cval.Imag)
+ mpsubfltflt(&v.U.(*Mpcplx).Real, &rv.U.(*Mpcplx).Real)
+ mpsubfltflt(&v.U.(*Mpcplx).Imag, &rv.U.(*Mpcplx).Imag)
case OMUL<<16 | CTCPLX:
- cmplxmpy(v.U.Cval, rv.U.Cval)
+ cmplxmpy(v.U.(*Mpcplx), rv.U.(*Mpcplx))
case ODIV<<16 | CTCPLX:
- if mpcmpfltc(&rv.U.Cval.Real, 0) == 0 && mpcmpfltc(&rv.U.Cval.Imag, 0) == 0 {
+ if mpcmpfltc(&rv.U.(*Mpcplx).Real, 0) == 0 && mpcmpfltc(&rv.U.(*Mpcplx).Imag, 0) == 0 {
Yyerror("complex division by zero")
- Mpmovecflt(&rv.U.Cval.Real, 1.0)
- Mpmovecflt(&rv.U.Cval.Imag, 0.0)
+ Mpmovecflt(&rv.U.(*Mpcplx).Real, 1.0)
+ Mpmovecflt(&rv.U.(*Mpcplx).Imag, 0.0)
break
}
- cmplxdiv(v.U.Cval, rv.U.Cval)
+ cmplxdiv(v.U.(*Mpcplx), rv.U.(*Mpcplx))
case OEQ<<16 | CTNIL:
goto settrue
@@ -865,90 +908,90 @@ func evconst(n *Node) {
case OEQ<<16 | CTINT,
OEQ<<16 | CTRUNE:
- if Mpcmpfixfix(v.U.Xval, rv.U.Xval) == 0 {
+ if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) == 0 {
goto settrue
}
goto setfalse
case ONE<<16 | CTINT,
ONE<<16 | CTRUNE:
- if Mpcmpfixfix(v.U.Xval, rv.U.Xval) != 0 {
+ if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) != 0 {
goto settrue
}
goto setfalse
case OLT<<16 | CTINT,
OLT<<16 | CTRUNE:
- if Mpcmpfixfix(v.U.Xval, rv.U.Xval) < 0 {
+ if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) < 0 {
goto settrue
}
goto setfalse
case OLE<<16 | CTINT,
OLE<<16 | CTRUNE:
- if Mpcmpfixfix(v.U.Xval, rv.U.Xval) <= 0 {
+ if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) <= 0 {
goto settrue
}
goto setfalse
case OGE<<16 | CTINT,
OGE<<16 | CTRUNE:
- if Mpcmpfixfix(v.U.Xval, rv.U.Xval) >= 0 {
+ if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) >= 0 {
goto settrue
}
goto setfalse
case OGT<<16 | CTINT,
OGT<<16 | CTRUNE:
- if Mpcmpfixfix(v.U.Xval, rv.U.Xval) > 0 {
+ if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) > 0 {
goto settrue
}
goto setfalse
case OEQ<<16 | CTFLT:
- if mpcmpfltflt(v.U.Fval, rv.U.Fval) == 0 {
+ if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) == 0 {
goto settrue
}
goto setfalse
case ONE<<16 | CTFLT:
- if mpcmpfltflt(v.U.Fval, rv.U.Fval) != 0 {
+ if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) != 0 {
goto settrue
}
goto setfalse
case OLT<<16 | CTFLT:
- if mpcmpfltflt(v.U.Fval, rv.U.Fval) < 0 {
+ if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) < 0 {
goto settrue
}
goto setfalse
case OLE<<16 | CTFLT:
- if mpcmpfltflt(v.U.Fval, rv.U.Fval) <= 0 {
+ if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) <= 0 {
goto settrue
}
goto setfalse
case OGE<<16 | CTFLT:
- if mpcmpfltflt(v.U.Fval, rv.U.Fval) >= 0 {
+ if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) >= 0 {
goto settrue
}
goto setfalse
case OGT<<16 | CTFLT:
- if mpcmpfltflt(v.U.Fval, rv.U.Fval) > 0 {
+ if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) > 0 {
goto settrue
}
goto setfalse
case OEQ<<16 | CTCPLX:
- if mpcmpfltflt(&v.U.Cval.Real, &rv.U.Cval.Real) == 0 && mpcmpfltflt(&v.U.Cval.Imag, &rv.U.Cval.Imag) == 0 {
+ if mpcmpfltflt(&v.U.(*Mpcplx).Real, &rv.U.(*Mpcplx).Real) == 0 && mpcmpfltflt(&v.U.(*Mpcplx).Imag, &rv.U.(*Mpcplx).Imag) == 0 {
goto settrue
}
goto setfalse
case ONE<<16 | CTCPLX:
- if mpcmpfltflt(&v.U.Cval.Real, &rv.U.Cval.Real) != 0 || mpcmpfltflt(&v.U.Cval.Imag, &rv.U.Cval.Imag) != 0 {
+ if mpcmpfltflt(&v.U.(*Mpcplx).Real, &rv.U.(*Mpcplx).Real) != 0 || mpcmpfltflt(&v.U.(*Mpcplx).Imag, &rv.U.(*Mpcplx).Imag) != 0 {
goto settrue
}
goto setfalse
@@ -990,25 +1033,25 @@ func evconst(n *Node) {
goto setfalse
case OOROR<<16 | CTBOOL:
- if v.U.Bval || rv.U.Bval {
+ if v.U.(bool) || rv.U.(bool) {
goto settrue
}
goto setfalse
case OANDAND<<16 | CTBOOL:
- if v.U.Bval && rv.U.Bval {
+ if v.U.(bool) && rv.U.(bool) {
goto settrue
}
goto setfalse
case OEQ<<16 | CTBOOL:
- if v.U.Bval == rv.U.Bval {
+ if v.U.(bool) == rv.U.(bool) {
goto settrue
}
goto setfalse
case ONE<<16 | CTBOOL:
- if v.U.Bval != rv.U.Bval {
+ if v.U.(bool) != rv.U.(bool) {
goto settrue
}
goto setfalse
@@ -1033,7 +1076,7 @@ ret:
// truncate precision for non-ideal float.
if v.Ctype == CTFLT && n.Type.Etype != TIDEAL {
- n.Val.U.Fval = truncfltlit(v.U.Fval, n.Type)
+ n.Val.U = truncfltlit(v.U.(*Mpflt), n.Type)
}
return
@@ -1088,15 +1131,15 @@ func nodcplxlit(r Val, i Val) *Node {
c := new(Mpcplx)
n := Nod(OLITERAL, nil, nil)
n.Type = Types[TIDEAL]
- n.Val.U.Cval = c
+ n.Val.U = c
n.Val.Ctype = CTCPLX
if r.Ctype != CTFLT || i.Ctype != CTFLT {
Fatal("nodcplxlit ctype %d/%d", r.Ctype, i.Ctype)
}
- mpmovefltflt(&c.Real, r.U.Fval)
- mpmovefltflt(&c.Imag, i.U.Fval)
+ mpmovefltflt(&c.Real, r.U.(*Mpflt))
+ mpmovefltflt(&c.Imag, i.U.(*Mpflt))
return n
}
@@ -1311,7 +1354,7 @@ func defaultlit2(lp **Node, rp **Node, force int) {
}
func cmpslit(l, r *Node) int {
- return stringsCompare(l.Val.U.Sval, r.Val.U.Sval)
+ return stringsCompare(l.Val.U.(string), r.Val.U.(string))
}
func Smallintconst(n *Node) bool {
@@ -1328,7 +1371,7 @@ func Smallintconst(n *Node) bool {
return true
case TIDEAL, TINT64, TUINT64, TPTR64:
- if Mpcmpfixfix(n.Val.U.Xval, Minintval[TINT32]) < 0 || Mpcmpfixfix(n.Val.U.Xval, Maxintval[TINT32]) > 0 {
+ if Mpcmpfixfix(n.Val.U.(*Mpint), Minintval[TINT32]) < 0 || Mpcmpfixfix(n.Val.U.(*Mpint), Maxintval[TINT32]) > 0 {
break
}
return true
@@ -1351,10 +1394,10 @@ func nonnegconst(n *Node) int {
TINT64,
TUINT64,
TIDEAL:
- if Mpcmpfixfix(n.Val.U.Xval, Minintval[TUINT32]) < 0 || Mpcmpfixfix(n.Val.U.Xval, Maxintval[TINT32]) > 0 {
+ if Mpcmpfixfix(n.Val.U.(*Mpint), Minintval[TUINT32]) < 0 || Mpcmpfixfix(n.Val.U.(*Mpint), Maxintval[TINT32]) > 0 {
break
}
- return int(Mpgetfix(n.Val.U.Xval))
+ return int(Mpgetfix(n.Val.U.(*Mpint)))
}
}
@@ -1392,39 +1435,37 @@ func iconv(x int64, et int) int64 {
return x
}
-/*
- * convert constant val to type t; leave in con.
- * for back end.
- */
-func Convconst(con *Node, t *Type, val *Val) {
+// Convconst converts constant node n to type t and
+// places the result in con.
+func (n *Node) Convconst(con *Node, t *Type) {
tt := Simsimtype(t)
// copy the constant for conversion
Nodconst(con, Types[TINT8], 0)
con.Type = t
- con.Val = *val
+ con.Val = n.Val
if Isint[tt] {
con.Val.Ctype = CTINT
- con.Val.U.Xval = new(Mpint)
+ con.Val.U = new(Mpint)
var i int64
- switch val.Ctype {
+ switch n.Val.Ctype {
default:
- Fatal("convconst ctype=%d %v", val.Ctype, Tconv(t, obj.FmtLong))
+ Fatal("convconst ctype=%d %v", n.Val.Ctype, Tconv(t, obj.FmtLong))
case CTINT, CTRUNE:
- i = Mpgetfix(val.U.Xval)
+ i = Mpgetfix(n.Val.U.(*Mpint))
case CTBOOL:
- i = int64(obj.Bool2int(val.U.Bval))
+ i = int64(obj.Bool2int(n.Val.U.(bool)))
case CTNIL:
i = 0
}
i = iconv(i, tt)
- Mpmovecfix(con.Val.U.Xval, i)
+ Mpmovecfix(con.Val.U.(*Mpint), i)
return
}
@@ -1434,7 +1475,7 @@ func Convconst(con *Node, t *Type, val *Val) {
Fatal("convconst ctype=%d %v", con.Val.Ctype, t)
}
if tt == TFLOAT32 {
- con.Val.U.Fval = truncfltlit(con.Val.U.Fval, t)
+ con.Val.U = truncfltlit(con.Val.U.(*Mpflt), t)
}
return
}
@@ -1442,10 +1483,9 @@ func Convconst(con *Node, t *Type, val *Val) {
if Iscomplex[tt] {
con.Val = tocplx(con.Val)
if tt == TCOMPLEX64 {
- con.Val.U.Cval.Real = *truncfltlit(&con.Val.U.Cval.Real, Types[TFLOAT32])
- con.Val.U.Cval.Imag = *truncfltlit(&con.Val.U.Cval.Imag, Types[TFLOAT32])
+ con.Val.U.(*Mpcplx).Real = *truncfltlit(&con.Val.U.(*Mpcplx).Real, Types[TFLOAT32])
+ con.Val.U.(*Mpcplx).Imag = *truncfltlit(&con.Val.U.(*Mpcplx).Imag, Types[TFLOAT32])
}
-
return
}
diff --git a/src/cmd/internal/gc/cplx.go b/src/cmd/compile/internal/gc/cplx.go
similarity index 98%
rename from src/cmd/internal/gc/cplx.go
rename to src/cmd/compile/internal/gc/cplx.go
index cf48c922d7..56a4892636 100644
--- a/src/cmd/internal/gc/cplx.go
+++ b/src/cmd/compile/internal/gc/cplx.go
@@ -89,8 +89,8 @@ func subnode(nr *Node, ni *Node, nc *Node) {
t := Types[tc]
if nc.Op == OLITERAL {
- nodfconst(nr, t, &nc.Val.U.Cval.Real)
- nodfconst(ni, t, &nc.Val.U.Cval.Imag)
+ nodfconst(nr, t, &nc.Val.U.(*Mpcplx).Real)
+ nodfconst(ni, t, &nc.Val.U.(*Mpcplx).Imag)
return
}
@@ -226,7 +226,7 @@ func nodfconst(n *Node, t *Type, fval *Mpflt) {
n.Op = OLITERAL
n.Addable = true
ullmancalc(n)
- n.Val.U.Fval = fval
+ n.Val.U = fval
n.Val.Ctype = CTFLT
n.Type = t
diff --git a/src/cmd/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go
similarity index 97%
rename from src/cmd/internal/gc/dcl.go
rename to src/cmd/compile/internal/gc/dcl.go
index 08d2469094..4a9cb295c8 100644
--- a/src/cmd/internal/gc/dcl.go
+++ b/src/cmd/compile/internal/gc/dcl.go
@@ -183,7 +183,7 @@ func declare(n *Node, ctxt uint8) {
}
if ctxt == PEXTERN && s.Name == "init" {
- Yyerror("cannot declare init - must be func", s)
+ Yyerror("cannot declare init - must be func")
}
gen := 0
@@ -260,7 +260,7 @@ func variter(vl *NodeList, t *Node, el *NodeList) *NodeList {
v = vl.N
v.Op = ONAME
declare(v, dclcontext)
- v.Ntype = t
+ v.Param.Ntype = t
v.Defn = as2
if Funcdepth > 0 {
init = list(init, Nod(ODCL, v, nil))
@@ -288,7 +288,7 @@ func variter(vl *NodeList, t *Node, el *NodeList) *NodeList {
v = vl.N
v.Op = ONAME
declare(v, dclcontext)
- v.Ntype = t
+ v.Param.Ntype = t
if e != nil || Funcdepth > 0 || isblank(v) {
if Funcdepth > 0 {
@@ -313,18 +313,19 @@ func variter(vl *NodeList, t *Node, el *NodeList) *NodeList {
* new_name_list [[type] = expr_list]
*/
func constiter(vl *NodeList, t *Node, cl *NodeList) *NodeList {
+ lno := int32(0) // default is to leave line number alone in listtreecopy
if cl == nil {
if t != nil {
Yyerror("const declaration cannot have type without expression")
}
cl = lastconst
t = lasttype
+ lno = vl.N.Lineno
} else {
lastconst = cl
lasttype = t
}
-
- cl = listtreecopy(cl)
+ cl = listtreecopy(cl, lno)
var v *Node
var c *Node
@@ -342,7 +343,7 @@ func constiter(vl *NodeList, t *Node, cl *NodeList) *NodeList {
v.Op = OLITERAL
declare(v, dclcontext)
- v.Ntype = t
+ v.Param.Ntype = t
v.Defn = c
vv = list(vv, Nod(ODCLCONST, v, nil))
@@ -430,7 +431,7 @@ func oldname(s *Sym) *Node {
// are parsing x := 5 inside the closure, until we get to
// the := it looks like a reference to the outer x so we'll
// make x a closure variable unnecessarily.
- if n.Closure == nil || n.Closure.Funcdepth != Funcdepth {
+ if n.Param.Closure == nil || n.Param.Closure.Funcdepth != Funcdepth {
// create new closure var.
c := Nod(ONAME, nil, nil)
@@ -441,15 +442,15 @@ func oldname(s *Sym) *Node {
c.Addable = false
c.Ullman = 2
c.Funcdepth = Funcdepth
- c.Outer = n.Closure
- n.Closure = c
- c.Closure = n
+ c.Param.Outer = n.Param.Closure
+ n.Param.Closure = c
+ c.Param.Closure = n
c.Xoffset = 0
Curfn.Func.Cvars = list(Curfn.Func.Cvars, c)
}
// return ref to closure var, not original
- return n.Closure
+ return n.Param.Closure
}
return n
@@ -554,7 +555,7 @@ func ifacedcl(n *Node) {
dclcontext = PPARAM
markdcl()
Funcdepth++
- n.Outer = Curfn
+ n.Param.Outer = Curfn
Curfn = n
funcargs(n.Right)
@@ -583,13 +584,13 @@ func funchdr(n *Node) {
markdcl()
Funcdepth++
- n.Outer = Curfn
+ n.Param.Outer = Curfn
Curfn = n
if n.Nname != nil {
- funcargs(n.Nname.Ntype)
- } else if n.Ntype != nil {
- funcargs(n.Ntype)
+ funcargs(n.Nname.Param.Ntype)
+ } else if n.Param.Ntype != nil {
+ funcargs(n.Param.Ntype)
} else {
funcargs2(n.Type)
}
@@ -615,7 +616,7 @@ func funcargs(nt *Node) {
}
if n.Left != nil {
n.Left.Op = ONAME
- n.Left.Ntype = n.Right
+ n.Left.Param.Ntype = n.Right
declare(n.Left, PPARAM)
if dclcontext == PAUTO {
vargen++
@@ -632,7 +633,7 @@ func funcargs(nt *Node) {
}
if n.Left != nil {
n.Left.Op = ONAME
- n.Left.Ntype = n.Right
+ n.Left.Param.Ntype = n.Right
declare(n.Left, PPARAM)
if dclcontext == PAUTO {
vargen++
@@ -679,7 +680,7 @@ func funcargs(nt *Node) {
n.Left = nn
}
- n.Left.Ntype = n.Right
+ n.Left.Param.Ntype = n.Right
declare(n.Left, PPARAMOUT)
if dclcontext == PAUTO {
i++
@@ -747,8 +748,8 @@ func funcbody(n *Node) {
}
popdcl()
Funcdepth--
- Curfn = n.Outer
- n.Outer = nil
+ Curfn = n.Param.Outer
+ n.Param.Outer = nil
if Funcdepth == 0 {
dclcontext = PEXTERN
}
@@ -770,7 +771,7 @@ func typedcl0(s *Sym) *Node {
* return the ODCLTYPE node to use.
*/
func typedcl1(n *Node, t *Node, local bool) *Node {
- n.Ntype = t
+ n.Param.Ntype = t
n.Local = local
return Nod(ODCLTYPE, n, nil)
}
@@ -830,7 +831,7 @@ func structfield(n *Node) *Type {
switch n.Val.Ctype {
case CTSTR:
f.Note = new(string)
- *f.Note = n.Val.U.Sval
+ *f.Note = n.Val.U.(string)
default:
Yyerror("field annotation must be string")
diff --git a/src/cmd/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go
similarity index 93%
rename from src/cmd/internal/gc/esc.go
rename to src/cmd/compile/internal/gc/esc.go
index c816feaa7f..2c134933c4 100644
--- a/src/cmd/internal/gc/esc.go
+++ b/src/cmd/compile/internal/gc/esc.go
@@ -154,7 +154,7 @@ func (v *bottomUpVisitor) visitcode(n *Node, min uint32) uint32 {
}
if n.Op == OCLOSURE {
- m := v.visit(n.Closure)
+ m := v.visit(n.Param.Closure)
if m < min {
min = m
}
@@ -379,7 +379,7 @@ type EscState struct {
theSink Node
dsts *NodeList // all dst nodes
- loopdepth int // for detecting nested loop scopes
+ loopdepth int32 // for detecting nested loop scopes
pdepth int // for debug printing in recursions.
dstcount int // diagnostic
edgecount int // diagnostic
@@ -387,6 +387,19 @@ type EscState struct {
recursive bool // recursive function or group of mutually recursive functions.
}
+// funcSym returns n.Nname.Sym if no nils are encountered along the way.
+func funcSym(n *Node) *Sym {
+ if n == nil || n.Nname == nil {
+ return nil
+ }
+ return n.Nname.Sym
+}
+
+// curfnSym returns n.Curfn.Nname.Sym if no nils are encountered along the way.
+func curfnSym(n *Node) *Sym {
+ return funcSym(n.Curfn)
+}
+
func escAnalyze(all *NodeList, recursive bool) {
var es EscState
e := &es
@@ -428,13 +441,7 @@ func escAnalyze(all *NodeList, recursive bool) {
if Debug['m'] != 0 {
for l := e.noesc; l != nil; l = l.Next {
if l.N.Esc == EscNone {
- var tmp *Sym
- if l.N.Curfn != nil && l.N.Curfn.Nname != nil {
- tmp = l.N.Curfn.Nname.Sym
- } else {
- tmp = nil
- }
- Warnl(int(l.N.Lineno), "%v %v does not escape", tmp, Nconv(l.N, obj.FmtShort))
+ Warnl(int(l.N.Lineno), "%v %v does not escape", curfnSym(l.N), Nconv(l.N, obj.FmtShort))
}
}
}
@@ -579,6 +586,19 @@ func esc(e *EscState, n *Node, up *Node) {
}
}
+ // Big stuff escapes unconditionally
+ // "Big" conditions that were scattered around in walk have been gathered here
+ if n.Esc != EscHeap && n.Type != nil && (n.Type.Width > MaxStackVarSize ||
+ n.Op == ONEW && n.Type.Type.Width >= 1<<16 ||
+ n.Op == OMAKESLICE && !isSmallMakeSlice(n)) {
+ if Debug['m'] > 1 {
+ Warnl(int(n.Lineno), "%v is too large for stack", n)
+ }
+ n.Esc = EscHeap
+ addrescapes(n)
+ escassign(e, &e.theSink, n)
+ }
+
esc(e, n.Left, n)
esc(e, n.Right, n)
esc(e, n.Ntest, n)
@@ -593,13 +613,7 @@ func esc(e *EscState, n *Node, up *Node) {
}
if Debug['m'] > 1 {
- var tmp *Sym
- if Curfn != nil && Curfn.Nname != nil {
- tmp = Curfn.Nname.Sym
- } else {
- tmp = nil
- }
- fmt.Printf("%v:[%d] %v esc: %v\n", Ctxt.Line(int(lineno)), e.loopdepth, tmp, n)
+ fmt.Printf("%v:[%d] %v esc: %v\n", Ctxt.Line(int(lineno)), e.loopdepth, funcSym(Curfn), n)
}
switch n.Op {
@@ -629,8 +643,12 @@ func esc(e *EscState, n *Node, up *Node) {
// Everything but fixed array is a dereference.
case ORANGE:
- if Isfixedarray(n.Type) && n.List != nil && n.List.Next != nil {
- escassign(e, n.List.Next.N, n.Right)
+ if n.List != nil && n.List.Next != nil {
+ if Isfixedarray(n.Type) {
+ escassign(e, n.List.Next.N, n.Right)
+ } else {
+ escassign(e, n.List.Next.N, addDereference(n.Right))
+ }
}
case OSWITCH:
@@ -670,13 +688,7 @@ func esc(e *EscState, n *Node, up *Node) {
// b escapes as well. If we ignore such OSLICEARR, we will conclude
// that b does not escape when b contents do.
if Debug['m'] != 0 {
- var tmp *Sym
- if n.Curfn != nil && n.Curfn.Nname != nil {
- tmp = n.Curfn.Nname.Sym
- } else {
- tmp = nil
- }
- Warnl(int(n.Lineno), "%v ignoring self-assignment to %v", tmp, Nconv(n.Left, obj.FmtShort))
+ Warnl(int(n.Lineno), "%v ignoring self-assignment to %v", curfnSym(n), Nconv(n.Left, obj.FmtShort))
}
break
@@ -763,7 +775,15 @@ func esc(e *EscState, n *Node, up *Node) {
for ll := n.List.Next; ll != nil; ll = ll.Next {
escassign(e, &e.theSink, ll.N) // lose track of assign to dereference
}
+ } else {
+ // append(slice1, slice2...) -- slice2 itself does not escape, but contents do.
+ slice2 := n.List.Next.N
+ escassign(e, &e.theSink, addDereference(slice2)) // lose track of assign of dereference
+ if Debug['m'] > 2 {
+ Warnl(int(n.Lineno), "%v special treatment of append(slice1, slice2...) %v", curfnSym(n), Nconv(n, obj.FmtShort))
+ }
}
+ escassign(e, &e.theSink, addDereference(n.List.N)) // The original elements are now leaked, too
case OCONV, OCONVNOP:
escassign(e, n, n.Left)
@@ -776,19 +796,15 @@ func esc(e *EscState, n *Node, up *Node) {
case OARRAYLIT:
if Isslice(n.Type) {
- n.Esc = EscNone // until proven otherwise
+ // Slice itself is not leaked until proven otherwise
+ n.Esc = EscNone
e.noesc = list(e.noesc, n)
n.Escloopdepth = e.loopdepth
+ }
- // Values make it to memory, lose track.
- for ll := n.List; ll != nil; ll = ll.Next {
- escassign(e, &e.theSink, ll.N.Right)
- }
- } else {
- // Link values to array.
- for ll := n.List; ll != nil; ll = ll.Next {
- escassign(e, n, ll.N.Right)
- }
+ // Link values to array/slice
+ for ll := n.List; ll != nil; ll = ll.Next {
+ escassign(e, n, ll.N.Right)
}
// Link values to struct.
@@ -833,8 +849,8 @@ func esc(e *EscState, n *Node, up *Node) {
if v.Op == OXXX { // unnamed out argument; see dcl.c:/^funcargs
continue
}
- a = v.Closure
- if !v.Byval {
+ a = v.Param.Closure
+ if !v.Name.Byval {
a = Nod(OADDR, a, nil)
a.Lineno = v.Lineno
a.Escloopdepth = e.loopdepth
@@ -909,14 +925,8 @@ func escassign(e *EscState, dst *Node, src *Node) {
}
if Debug['m'] > 1 {
- var tmp *Sym
- if Curfn != nil && Curfn.Nname != nil {
- tmp = Curfn.Nname.Sym
- } else {
- tmp = nil
- }
fmt.Printf("%v:[%d] %v escassign: %v(%v)[%v] = %v(%v)[%v]\n",
- Ctxt.Line(int(lineno)), e.loopdepth, tmp,
+ Ctxt.Line(int(lineno)), e.loopdepth, funcSym(Curfn),
Nconv(dst, obj.FmtShort), Jconv(dst, obj.FmtShort), Oconv(int(dst.Op), 0),
Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort), Oconv(int(src.Op), 0))
}
@@ -1038,12 +1048,15 @@ func escassign(e *EscState, dst *Node, src *Node) {
case OAPPEND:
// Append returns first argument.
+ // Subsequent arguments are already leaked because they are operands to append.
escassign(e, dst, src.List.N)
case OINDEX:
// Index of array preserves input value.
if Isfixedarray(src.Left.Type) {
escassign(e, dst, src.Left)
+ } else {
+ escflows(e, dst, src)
}
// Might be pointer arithmetic, in which case
@@ -1269,6 +1282,24 @@ func escNoteOutputParamFlow(e uint16, vargen int32, level Level) uint16 {
return (e &^ (bitsMaskForTag << shift)) | encodedFlow
}
+func initEscretval(e *EscState, n *Node, fntype *Type) {
+ i := 0
+ n.Escretval = nil // Suspect this is not nil for indirect calls.
+ for t := getoutargx(fntype).Type; t != nil; t = t.Down {
+ src := Nod(ONAME, nil, nil)
+ buf := fmt.Sprintf(".out%d", i)
+ i++
+ src.Sym = Lookup(buf)
+ src.Type = t.Type
+ src.Class = PAUTO
+ src.Curfn = Curfn
+ src.Escloopdepth = e.loopdepth
+ src.Used = true
+ src.Lineno = n.Lineno
+ n.Escretval = list(n.Escretval, src)
+ }
+}
+
// This is a bit messier than fortunate, pulled out of esc's big
// switch for clarity. We either have the paramnodes, which may be
// connected to other things through flows or we have the parameter type
@@ -1277,7 +1308,7 @@ func escNoteOutputParamFlow(e uint16, vargen int32, level Level) uint16 {
// this-package
func esccall(e *EscState, n *Node, up *Node) {
var fntype *Type
-
+ var indirect bool
var fn *Node
switch n.Op {
default:
@@ -1286,6 +1317,7 @@ func esccall(e *EscState, n *Node, up *Node) {
case OCALLFUNC:
fn = n.Left
fntype = fn.Type
+ indirect = fn.Op != ONAME || fn.Class != PFUNC
case OCALLMETH:
fn = n.Left.Right.Sym.Def
@@ -1297,6 +1329,7 @@ func esccall(e *EscState, n *Node, up *Node) {
case OCALLINTER:
fntype = n.Left.Type
+ indirect = true
}
ll := n.List
@@ -1307,8 +1340,30 @@ func esccall(e *EscState, n *Node, up *Node) {
}
}
+ if indirect {
+ // We know nothing!
+ // Leak all the parameters
+ for ; ll != nil; ll = ll.Next {
+ escassign(e, &e.theSink, ll.N)
+ if Debug['m'] > 2 {
+ fmt.Printf("%v::esccall:: indirect call <- %v, untracked\n", Ctxt.Line(int(lineno)), Nconv(ll.N, obj.FmtShort))
+ }
+ }
+ // Set up bogus outputs
+ initEscretval(e, n, fntype)
+ // If there is a receiver, it also leaks to heap.
+ if n.Op != OCALLFUNC {
+ t := getthisx(fntype).Type
+ src := n.Left.Left
+ if haspointers(t.Type) {
+ escassign(e, &e.theSink, src)
+ }
+ }
+ return
+ }
+
if fn != nil && fn.Op == ONAME && fn.Class == PFUNC &&
- fn.Defn != nil && fn.Defn.Nbody != nil && fn.Ntype != nil && fn.Defn.Esc < EscFuncTagged {
+ fn.Defn != nil && fn.Defn.Nbody != nil && fn.Param.Ntype != nil && fn.Defn.Esc < EscFuncTagged {
if Debug['m'] > 2 {
fmt.Printf("%v::esccall:: %v in recursive group\n", Ctxt.Line(int(lineno)), Nconv(n, obj.FmtShort))
}
@@ -1320,17 +1375,17 @@ func esccall(e *EscState, n *Node, up *Node) {
}
// set up out list on this call node
- for lr := fn.Ntype.Rlist; lr != nil; lr = lr.Next {
+ for lr := fn.Param.Ntype.Rlist; lr != nil; lr = lr.Next {
n.Escretval = list(n.Escretval, lr.N.Left) // type.rlist -> dclfield -> ONAME (PPARAMOUT)
}
// Receiver.
if n.Op != OCALLFUNC {
- escassign(e, fn.Ntype.Left.Left, n.Left.Left)
+ escassign(e, fn.Param.Ntype.Left.Left, n.Left.Left)
}
var src *Node
- for lr := fn.Ntype.List; ll != nil && lr != nil; ll, lr = ll.Next, lr.Next {
+ for lr := fn.Param.Ntype.List; ll != nil && lr != nil; ll, lr = ll.Next, lr.Next {
src = ll.N
if lr.N.Isddd && !n.Isddd {
// Introduce ODDDARG node to represent ... allocation.
@@ -1376,23 +1431,7 @@ func esccall(e *EscState, n *Node, up *Node) {
}
// set up out list on this call node with dummy auto ONAMES in the current (calling) function.
- i := 0
-
- var src *Node
- var buf string
- for t := getoutargx(fntype).Type; t != nil; t = t.Down {
- src = Nod(ONAME, nil, nil)
- buf = fmt.Sprintf(".out%d", i)
- i++
- src.Sym = Lookup(buf)
- src.Type = t.Type
- src.Class = PAUTO
- src.Curfn = Curfn
- src.Escloopdepth = e.loopdepth
- src.Used = true
- src.Lineno = n.Lineno
- n.Escretval = list(n.Escretval, src)
- }
+ initEscretval(e, n, fntype)
// print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval);
@@ -1405,9 +1444,8 @@ func esccall(e *EscState, n *Node, up *Node) {
}
}
- var a *Node
for t := getinargx(fntype).Type; ll != nil; ll = ll.Next {
- src = ll.N
+ src := ll.N
if t.Isddd && !n.Isddd {
// Introduce ODDDARG node to represent ... allocation.
src = Nod(ODDDARG, nil, nil)
@@ -1425,7 +1463,7 @@ func esccall(e *EscState, n *Node, up *Node) {
if haspointers(t.Type) {
if escassignfromtag(e, t.Note, n.Escretval, src) == EscNone && up.Op != ODEFER && up.Op != OPROC {
- a = src
+ a := src
for a.Op == OCONVNOP {
a = a.Left
}
@@ -1510,13 +1548,7 @@ func escflood(e *EscState, dst *Node) {
}
if Debug['m'] > 1 {
- var tmp *Sym
- if dst.Curfn != nil && dst.Curfn.Nname != nil {
- tmp = dst.Curfn.Nname.Sym
- } else {
- tmp = nil
- }
- fmt.Printf("\nescflood:%d: dst %v scope:%v[%d]\n", walkgen, Nconv(dst, obj.FmtShort), tmp, dst.Escloopdepth)
+ fmt.Printf("\nescflood:%d: dst %v scope:%v[%d]\n", walkgen, Nconv(dst, obj.FmtShort), curfnSym(dst), dst.Escloopdepth)
}
for l := dst.Escflowsrc; l != nil; l = l.Next {
@@ -1548,14 +1580,8 @@ func escwalk(e *EscState, level Level, dst *Node, src *Node) {
src.Esclevel = level
if Debug['m'] > 1 {
- var tmp *Sym
- if src.Curfn != nil && src.Curfn.Nname != nil {
- tmp = src.Curfn.Nname.Sym
- } else {
- tmp = nil
- }
fmt.Printf("escwalk: level:%d depth:%d %.*s op=%v %v(%v) scope:%v[%d]\n",
- level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", Oconv(int(src.Op), 0), Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort), tmp, src.Escloopdepth)
+ level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", Oconv(int(src.Op), 0), Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort), curfnSym(src), src.Escloopdepth)
}
e.pdepth++
@@ -1627,7 +1653,7 @@ func escwalk(e *EscState, level Level, dst *Node, src *Node) {
if leaks && Debug['m'] != 0 {
Warnl(int(src.Lineno), "leaking closure reference %v", Nconv(src, obj.FmtShort))
}
- escwalk(e, level, dst, src.Closure)
+ escwalk(e, level, dst, src.Param.Closure)
}
case OPTRLIT, OADDR:
@@ -1657,6 +1683,10 @@ func escwalk(e *EscState, level Level, dst *Node, src *Node) {
if Isfixedarray(src.Type) {
break
}
+ for ll := src.List; ll != nil; ll = ll.Next {
+ escwalk(e, level.dec(), dst, ll.N.Right)
+ }
+
fallthrough
case ODDDARG,
diff --git a/src/cmd/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go
similarity index 99%
rename from src/cmd/internal/gc/export.go
rename to src/cmd/compile/internal/gc/export.go
index 1efc8150c5..5117490ac8 100644
--- a/src/cmd/internal/gc/export.go
+++ b/src/cmd/compile/internal/gc/export.go
@@ -64,7 +64,7 @@ func autoexport(n *Node, ctxt uint8) {
if (ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN {
return
}
- if n.Ntype != nil && n.Ntype.Op == OTFUNC && n.Ntype.Left != nil { // method
+ if n.Param != nil && n.Param.Ntype != nil && n.Param.Ntype.Op == OTFUNC && n.Param.Ntype.Left != nil { // method
return
}
diff --git a/src/cmd/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go
similarity index 96%
rename from src/cmd/internal/gc/fmt.go
rename to src/cmd/compile/internal/gc/fmt.go
index 1a991a0a65..4b93363c73 100644
--- a/src/cmd/internal/gc/fmt.go
+++ b/src/cmd/compile/internal/gc/fmt.go
@@ -302,12 +302,12 @@ func Vconv(v *Val, flag int) string {
switch v.Ctype {
case CTINT:
if (flag&obj.FmtSharp != 0) || fmtmode == FExp {
- return Bconv(v.U.Xval, obj.FmtSharp)
+ return Bconv(v.U.(*Mpint), obj.FmtSharp)
}
- return Bconv(v.U.Xval, 0)
+ return Bconv(v.U.(*Mpint), 0)
case CTRUNE:
- x := Mpgetfix(v.U.Xval)
+ x := Mpgetfix(v.U.(*Mpint))
if ' ' <= x && x < 0x80 && x != '\\' && x != '\'' {
return fmt.Sprintf("'%c'", int(x))
}
@@ -317,34 +317,34 @@ func Vconv(v *Val, flag int) string {
if 0 <= x && x <= utf8.MaxRune {
return fmt.Sprintf("'\\U%08x'", uint64(x))
}
- return fmt.Sprintf("('\\x00' + %v)", v.U.Xval)
+ return fmt.Sprintf("('\\x00' + %v)", v.U.(*Mpint))
case CTFLT:
if (flag&obj.FmtSharp != 0) || fmtmode == FExp {
- return Fconv(v.U.Fval, 0)
+ return Fconv(v.U.(*Mpflt), 0)
}
- return Fconv(v.U.Fval, obj.FmtSharp)
+ return Fconv(v.U.(*Mpflt), obj.FmtSharp)
case CTCPLX:
if (flag&obj.FmtSharp != 0) || fmtmode == FExp {
- return fmt.Sprintf("(%v+%vi)", &v.U.Cval.Real, &v.U.Cval.Imag)
+ return fmt.Sprintf("(%v+%vi)", &v.U.(*Mpcplx).Real, &v.U.(*Mpcplx).Imag)
}
- if mpcmpfltc(&v.U.Cval.Real, 0) == 0 {
- return fmt.Sprintf("%vi", Fconv(&v.U.Cval.Imag, obj.FmtSharp))
+ if mpcmpfltc(&v.U.(*Mpcplx).Real, 0) == 0 {
+ return fmt.Sprintf("%vi", Fconv(&v.U.(*Mpcplx).Imag, obj.FmtSharp))
}
- if mpcmpfltc(&v.U.Cval.Imag, 0) == 0 {
- return Fconv(&v.U.Cval.Real, obj.FmtSharp)
+ if mpcmpfltc(&v.U.(*Mpcplx).Imag, 0) == 0 {
+ return Fconv(&v.U.(*Mpcplx).Real, obj.FmtSharp)
}
- if mpcmpfltc(&v.U.Cval.Imag, 0) < 0 {
- return fmt.Sprintf("(%v%vi)", Fconv(&v.U.Cval.Real, obj.FmtSharp), Fconv(&v.U.Cval.Imag, obj.FmtSharp))
+ if mpcmpfltc(&v.U.(*Mpcplx).Imag, 0) < 0 {
+ return fmt.Sprintf("(%v%vi)", Fconv(&v.U.(*Mpcplx).Real, obj.FmtSharp), Fconv(&v.U.(*Mpcplx).Imag, obj.FmtSharp))
}
- return fmt.Sprintf("(%v+%vi)", Fconv(&v.U.Cval.Real, obj.FmtSharp), Fconv(&v.U.Cval.Imag, obj.FmtSharp))
+ return fmt.Sprintf("(%v+%vi)", Fconv(&v.U.(*Mpcplx).Real, obj.FmtSharp), Fconv(&v.U.(*Mpcplx).Imag, obj.FmtSharp))
case CTSTR:
- return strconv.Quote(v.U.Sval)
+ return strconv.Quote(v.U.(string))
case CTBOOL:
- if v.U.Bval {
+ if v.U.(bool) {
return "true"
}
return "false"
@@ -1127,7 +1127,7 @@ func exprfmt(n *Node, prec int) string {
// Special case: name used as local variable in export.
// _ becomes ~b%d internally; print as _ for export
case ONAME:
- if fmtmode == FExp && n.Sym != nil && n.Sym.Name[0] == '~' && n.Sym.Name[1] == 'b' {
+ if (fmtmode == FExp || fmtmode == FErr) && n.Sym != nil && n.Sym.Name[0] == '~' && n.Sym.Name[1] == 'b' {
return "_"
}
if fmtmode == FExp && n.Sym != nil && !isblank(n) && n.Vargen > 0 {
@@ -1199,7 +1199,7 @@ func exprfmt(n *Node, prec int) string {
if n.Nbody != nil {
return fmt.Sprintf("%v { %v }", n.Type, n.Nbody)
}
- return fmt.Sprintf("%v { %v }", n.Type, n.Closure.Nbody)
+ return fmt.Sprintf("%v { %v }", n.Type, n.Param.Closure.Nbody)
case OCOMPLIT:
ptrlit := n.Right != nil && n.Right.Implicit && n.Right.Type != nil && Isptr[n.Right.Type.Etype]
@@ -1521,9 +1521,9 @@ func nodedump(n *Node, flag int) string {
} else {
fmt.Fprintf(&buf, "%v%v", Oconv(int(n.Op), 0), Jconv(n, 0))
}
- if recur && n.Type == nil && n.Ntype != nil {
+ if recur && n.Type == nil && n.Param.Ntype != nil {
indent(&buf)
- fmt.Fprintf(&buf, "%v-ntype%v", Oconv(int(n.Op), 0), n.Ntype)
+ fmt.Fprintf(&buf, "%v-ntype%v", Oconv(int(n.Op), 0), n.Param.Ntype)
}
case OASOP:
@@ -1531,9 +1531,9 @@ func nodedump(n *Node, flag int) string {
case OTYPE:
fmt.Fprintf(&buf, "%v %v%v type=%v", Oconv(int(n.Op), 0), n.Sym, Jconv(n, 0), n.Type)
- if recur && n.Type == nil && n.Ntype != nil {
+ if recur && n.Type == nil && n.Param.Ntype != nil {
indent(&buf)
- fmt.Fprintf(&buf, "%v-ntype%v", Oconv(int(n.Op), 0), n.Ntype)
+ fmt.Fprintf(&buf, "%v-ntype%v", Oconv(int(n.Op), 0), n.Param.Ntype)
}
}
diff --git a/src/cmd/internal/gc/gen.go b/src/cmd/compile/internal/gc/gen.go
similarity index 86%
rename from src/cmd/internal/gc/gen.go
rename to src/cmd/compile/internal/gc/gen.go
index e6af897033..c0dd9964ea 100644
--- a/src/cmd/internal/gc/gen.go
+++ b/src/cmd/compile/internal/gc/gen.go
@@ -57,14 +57,14 @@ func addrescapes(n *Node) {
// expression to refer to stack copy
case PPARAM, PPARAMOUT:
- n.Stackparam = Nod(OPARAM, n, nil)
+ n.Param.Stackparam = Nod(OPARAM, n, nil)
- n.Stackparam.Type = n.Type
- n.Stackparam.Addable = true
+ n.Param.Stackparam.Type = n.Type
+ n.Param.Stackparam.Addable = true
if n.Xoffset == BADWIDTH {
Fatal("addrescapes before param assignment")
}
- n.Stackparam.Xoffset = n.Xoffset
+ n.Param.Stackparam.Xoffset = n.Xoffset
fallthrough
case PAUTO:
@@ -78,10 +78,10 @@ func addrescapes(n *Node) {
oldfn := Curfn
Curfn = n.Curfn
- n.Heapaddr = temp(Ptrto(n.Type))
+ n.Name.Heapaddr = temp(Ptrto(n.Type))
buf := fmt.Sprintf("&%v", n.Sym)
- n.Heapaddr.Sym = Lookup(buf)
- n.Heapaddr.Orig.Sym = n.Heapaddr.Sym
+ n.Name.Heapaddr.Sym = Lookup(buf)
+ n.Name.Heapaddr.Orig.Sym = n.Name.Heapaddr.Sym
n.Esc = EscHeap
if Debug['m'] != 0 {
fmt.Printf("%v: moved to heap: %v\n", n.Line(), n)
@@ -262,7 +262,7 @@ func cgen_dcl(n *Node) {
if n.Alloc == nil {
n.Alloc = callnew(n.Type)
}
- Cgen_as(n.Heapaddr, n.Alloc)
+ Cgen_as(n.Name.Heapaddr, n.Alloc)
}
/*
@@ -333,21 +333,22 @@ func Clearslim(n *Node) {
switch Simtype[n.Type.Etype] {
case TCOMPLEX64, TCOMPLEX128:
- z.Val.U.Cval = new(Mpcplx)
- Mpmovecflt(&z.Val.U.Cval.Real, 0.0)
- Mpmovecflt(&z.Val.U.Cval.Imag, 0.0)
+ z.Val.U = new(Mpcplx)
+ Mpmovecflt(&z.Val.U.(*Mpcplx).Real, 0.0)
+ Mpmovecflt(&z.Val.U.(*Mpcplx).Imag, 0.0)
case TFLOAT32, TFLOAT64:
var zero Mpflt
Mpmovecflt(&zero, 0.0)
z.Val.Ctype = CTFLT
- z.Val.U.Fval = &zero
+ z.Val.U = &zero
case TPTR32, TPTR64, TCHAN, TMAP:
z.Val.Ctype = CTNIL
case TBOOL:
z.Val.Ctype = CTBOOL
+ z.Val.U = false
case TINT8,
TINT16,
@@ -358,8 +359,8 @@ func Clearslim(n *Node) {
TUINT32,
TUINT64:
z.Val.Ctype = CTINT
- z.Val.U.Xval = new(Mpint)
- Mpmovecfix(z.Val.U.Xval, 0)
+ z.Val.U = new(Mpint)
+ Mpmovecfix(z.Val.U.(*Mpint), 0)
default:
Fatal("clearslim called on type %v", n.Type)
@@ -428,8 +429,7 @@ func cgen_dottype(n *Node, res, resok *Node, wb bool) {
Cgen(&iface, &r1)
if !isnilinter(n.Left.Type) {
// Holding itab, want concrete type in second word.
- Thearch.Gins(Thearch.Optoas(OCMP, byteptr), &r1, Nodintconst(0))
- p := Gbranch(Thearch.Optoas(OEQ, byteptr), nil, -1)
+ p := Thearch.Ginscmp(OEQ, byteptr, &r1, Nodintconst(0), -1)
r2 = r1
r2.Op = OINDREG
r2.Xoffset = int64(Widthptr)
@@ -438,8 +438,7 @@ func cgen_dottype(n *Node, res, resok *Node, wb bool) {
}
Regalloc(&r2, byteptr, nil)
Cgen(typename(n.Type), &r2)
- Thearch.Gins(Thearch.Optoas(OCMP, byteptr), &r1, &r2)
- p := Gbranch(Thearch.Optoas(ONE, byteptr), nil, -1)
+ p := Thearch.Ginscmp(ONE, byteptr, &r1, &r2, -1)
Regfree(&r2) // not needed for success path; reclaimed on one failure path
iface.Xoffset += int64(Widthptr)
Cgen(&iface, &r1)
@@ -521,8 +520,7 @@ func Cgen_As2dottype(n, res, resok *Node) {
Cgen(&iface, &r1)
if !isnilinter(n.Left.Type) {
// Holding itab, want concrete type in second word.
- Thearch.Gins(Thearch.Optoas(OCMP, byteptr), &r1, Nodintconst(0))
- p := Gbranch(Thearch.Optoas(OEQ, byteptr), nil, -1)
+ p := Thearch.Ginscmp(OEQ, byteptr, &r1, Nodintconst(0), -1)
r2 = r1
r2.Op = OINDREG
r2.Xoffset = int64(Widthptr)
@@ -531,8 +529,7 @@ func Cgen_As2dottype(n, res, resok *Node) {
}
Regalloc(&r2, byteptr, nil)
Cgen(typename(n.Type), &r2)
- Thearch.Gins(Thearch.Optoas(OCMP, byteptr), &r1, &r2)
- p := Gbranch(Thearch.Optoas(ONE, byteptr), nil, -1)
+ p := Thearch.Ginscmp(ONE, byteptr, &r1, &r2, -1)
iface.Type = n.Type
iface.Xoffset += int64(Widthptr)
Cgen(&iface, &r1)
@@ -555,122 +552,6 @@ func Cgen_As2dottype(n, res, resok *Node) {
Patch(q, Pc)
}
-/*
- * generate:
- * res = s[lo, hi];
- * n->left is s
- * n->list is (cap(s)-lo(TUINT), hi-lo(TUINT)[, lo*width(TUINTPTR)])
- * caller (cgen) guarantees res is an addable ONAME.
- *
- * called for OSLICE, OSLICE3, OSLICEARR, OSLICE3ARR, OSLICESTR.
- */
-func Cgen_slice(n *Node, res *Node) {
- cap := n.List.N
- len := n.List.Next.N
- var offs *Node
- if n.List.Next.Next != nil {
- offs = n.List.Next.Next.N
- }
-
- // evaluate base pointer first, because it is the only
- // possibly complex expression. once that is evaluated
- // and stored, updating the len and cap can be done
- // without making any calls, so without doing anything that
- // might cause preemption or garbage collection.
- // this makes the whole slice update atomic as far as the
- // garbage collector can see.
- base := temp(Types[TUINTPTR])
-
- tmplen := temp(Types[TINT])
- var tmpcap *Node
- if n.Op != OSLICESTR {
- tmpcap = temp(Types[TINT])
- } else {
- tmpcap = tmplen
- }
-
- var src Node
- if isnil(n.Left) {
- Tempname(&src, n.Left.Type)
- Cgen(n.Left, &src)
- } else {
- src = *n.Left
- }
- if n.Op == OSLICE || n.Op == OSLICE3 || n.Op == OSLICESTR {
- src.Xoffset += int64(Array_array)
- }
-
- if n.Op == OSLICEARR || n.Op == OSLICE3ARR {
- if !Isptr[n.Left.Type.Etype] {
- Fatal("slicearr is supposed to work on pointer: %v\n", Nconv(n, obj.FmtSign))
- }
- Cgen(&src, base)
- Cgen_checknil(base)
- } else {
- src.Type = Types[Tptr]
- Cgen(&src, base)
- }
-
- // committed to the update
- Gvardef(res)
-
- // compute len and cap.
- // len = n-i, cap = m-i, and offs = i*width.
- // computing offs last lets the multiply overwrite i.
- Cgen((*Node)(len), tmplen)
-
- if n.Op != OSLICESTR {
- Cgen(cap, tmpcap)
- }
-
- // if new cap != 0 { base += add }
- // This avoids advancing base past the end of the underlying array/string,
- // so that it cannot point at the next object in memory.
- // If cap == 0, the base doesn't matter except insofar as it is 0 or non-zero.
- // In essence we are replacing x[i:j:k] where i == j == k
- // or x[i:j] where i == j == cap(x) with x[0:0:0].
- if offs != nil {
- p1 := gjmp(nil)
- p2 := gjmp(nil)
- Patch(p1, Pc)
-
- var con Node
- Nodconst(&con, tmpcap.Type, 0)
- cmp := Nod(OEQ, tmpcap, &con)
- typecheck(&cmp, Erv)
- Bgen(cmp, true, -1, p2)
-
- add := Nod(OADD, base, offs)
- typecheck(&add, Erv)
- Cgen(add, base)
-
- Patch(p2, Pc)
- }
-
- // dst.array = src.array [ + lo *width ]
- dst := *res
-
- dst.Xoffset += int64(Array_array)
- dst.Type = Types[Tptr]
- Cgen(base, &dst)
-
- // dst.len = hi [ - lo ]
- dst = *res
-
- dst.Xoffset += int64(Array_nel)
- dst.Type = Types[Simtype[TUINT]]
- Cgen(tmplen, &dst)
-
- if n.Op != OSLICESTR {
- // dst.cap = cap [ - lo ]
- dst = *res
-
- dst.Xoffset += int64(Array_cap)
- dst.Type = Types[Simtype[TUINT]]
- Cgen(tmpcap, &dst)
- }
-}
-
/*
* gather series of offsets
* >=0 is direct addressed field
@@ -1085,7 +966,7 @@ func cgen_callmeth(n *Node, proc int) {
l := n.Left
if l.Op != ODOTMETH {
- Fatal("cgen_callmeth: not dotmethod: %v")
+ Fatal("cgen_callmeth: not dotmethod: %v", l)
}
n2 := *n
@@ -1241,7 +1122,7 @@ func componentgen_wb(nr, nl *Node, wb bool) bool {
nodl.Type = Ptrto(Types[TUINT8])
Regalloc(&nodr, Types[Tptr], nil)
p := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &nodr)
- Datastring(nr.Val.U.Sval, &p.From)
+ Datastring(nr.Val.U.(string), &p.From)
p.From.Type = obj.TYPE_ADDR
Thearch.Gmove(&nodr, &nodl)
Regfree(&nodr)
@@ -1249,7 +1130,7 @@ func componentgen_wb(nr, nl *Node, wb bool) bool {
// length
nodl.Type = Types[Simtype[TUINT]]
nodl.Xoffset += int64(Array_nel) - int64(Array_array)
- Nodconst(&nodr, nodl.Type, int64(len(nr.Val.U.Sval)))
+ Nodconst(&nodr, nodl.Type, int64(len(nr.Val.U.(string))))
Thearch.Gmove(&nodr, &nodl)
return true
}
diff --git a/src/cmd/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go
similarity index 93%
rename from src/cmd/internal/gc/go.go
rename to src/cmd/compile/internal/gc/go.go
index 71bce0bf2c..dc33f62ba4 100644
--- a/src/cmd/internal/gc/go.go
+++ b/src/cmd/compile/internal/gc/go.go
@@ -6,7 +6,7 @@ package gc
import (
"bytes"
- "cmd/internal/gc/big"
+ "cmd/compile/internal/big"
"cmd/internal/obj"
)
@@ -83,13 +83,13 @@ type Mpcplx struct {
type Val struct {
Ctype int16
- U struct {
- Bval bool // bool value CTBOOL
- Xval *Mpint // int CTINT, rune CTRUNE
- Fval *Mpflt // float CTFLT
- Cval *Mpcplx // float CTCPLX
- Sval string // string CTSTR
- }
+ // U contains one of:
+ // bool bool when Ctype == CTBOOL
+ // *Mpint int when Ctype == CTINT, rune when Ctype == CTRUNE
+ // *Mpflt float when Ctype == CTFLT
+ // *Mpcplx pair of floats when Ctype == CTCPLX
+ // string string when Ctype == CTSTR
+ U interface{}
}
type Pkg struct {
@@ -448,7 +448,7 @@ var nsavederrors int
var nsyntaxerrors int
-var decldepth int
+var decldepth int32
var safemode int
@@ -778,13 +778,27 @@ type Arch struct {
Expandchecks func(*obj.Prog)
Getg func(*Node)
Gins func(int, *Node, *Node) *obj.Prog
+
+ // Ginscmp generates code comparing n1 to n2 and jumping away if op is satisfied.
+ // The returned prog should be Patch'ed with the jump target.
+ // If op is not satisfied, code falls through to the next emitted instruction.
+ // Likely is the branch prediction hint: +1 for likely, -1 for unlikely, 0 for no opinion.
+ //
+ // Ginscmp must be able to handle all kinds of arguments for n1 and n2,
+ // not just simple registers, although it can assume that there are no
+ // function calls needed during the evaluation, and on 32-bit systems
+ // the values are guaranteed not to be 64-bit values, so no in-memory
+ // temporaries are necessary.
+ Ginscmp func(op int, t *Type, n1, n2 *Node, likely int) *obj.Prog
+
// Ginsboolval inserts instructions to convert the result
// of a just-completed comparison to a boolean value.
// The first argument is the conditional jump instruction
// corresponding to the desired value.
// The second argument is the destination.
// If not present, Ginsboolval will be emulated with jumps.
- Ginsboolval func(int, *Node)
+ Ginsboolval func(int, *Node)
+
Ginscon func(int, int64, *Node)
Ginsnop func()
Gmove func(*Node, *Node)
diff --git a/src/cmd/internal/gc/go.y b/src/cmd/compile/internal/gc/go.y
similarity index 92%
rename from src/cmd/internal/gc/go.y
rename to src/cmd/compile/internal/gc/go.y
index f1904b0085..ae2e7613ab 100644
--- a/src/cmd/internal/gc/go.y
+++ b/src/cmd/compile/internal/gc/go.y
@@ -21,6 +21,7 @@
package gc
import (
+ "fmt"
"strings"
)
%}
@@ -116,7 +117,68 @@ import (
%left ')'
%left PreferToRightParen
-// TODO(rsc): Add %error-verbose
+%error loadsys package LIMPORT '(' LLITERAL import_package import_there ',':
+ "unexpected comma during import block"
+
+%error loadsys package LIMPORT LNAME ';':
+ "missing import path; require quoted string"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header ';':
+ "missing { after if clause"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LSWITCH if_header ';':
+ "missing { after switch clause"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LFOR for_header ';':
+ "missing { after for clause"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LFOR ';' LBODY:
+ "missing { after for clause"
+
+%error loadsys package imports LFUNC LNAME '(' ')' ';' '{':
+ "unexpected semicolon or newline before {"
+
+%error loadsys package imports LTYPE LNAME ';':
+ "unexpected semicolon or newline in type declaration"
+
+%error loadsys package imports LCHAN '}':
+ "unexpected } in channel type"
+
+%error loadsys package imports LCHAN ')':
+ "unexpected ) in channel type"
+
+%error loadsys package imports LCHAN ',':
+ "unexpected comma in channel type"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' if_stmt ';' LELSE:
+ "unexpected semicolon or newline before else"
+
+%error loadsys package imports LTYPE LNAME LINTERFACE '{' LNAME ',' LNAME:
+ "name list not allowed in interface type"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LFOR LVAR LNAME '=' LNAME:
+ "var declaration not allowed in for initializer"
+
+%error loadsys package imports LVAR LNAME '[' ']' LNAME '{':
+ "unexpected { at end of statement"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LVAR LNAME '[' ']' LNAME '{':
+ "unexpected { at end of statement"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LDEFER LNAME ';':
+ "argument to go/defer must be function call"
+
+%error loadsys package imports LVAR LNAME '=' LNAME '{' LNAME ';':
+ "need trailing comma before newline in composite literal"
+
+%error loadsys package imports LVAR LNAME '=' comptype '{' LNAME ';':
+ "need trailing comma before newline in composite literal"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LFUNC LNAME:
+ "nested func not allowed"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header loop_body LELSE ';':
+ "else must be followed by if or statement block"
%%
file:
@@ -1130,13 +1192,13 @@ hidden_importsym:
{
var p *Pkg
- if $2.U.Sval == "" {
+ if $2.U.(string) == "" {
p = importpkg;
} else {
- if isbadimport($2.U.Sval) {
+ if isbadimport($2.U.(string)) {
errorexit();
}
- p = mkpkg($2.U.Sval);
+ p = mkpkg($2.U.(string));
}
$$ = Pkglookup($4.Name, p);
}
@@ -1144,13 +1206,13 @@ hidden_importsym:
{
var p *Pkg
- if $2.U.Sval == "" {
+ if $2.U.(string) == "" {
p = importpkg;
} else {
- if isbadimport($2.U.Sval) {
+ if isbadimport($2.U.(string)) {
errorexit();
}
- p = mkpkg($2.U.Sval);
+ p = mkpkg($2.U.(string));
}
$$ = Pkglookup("?", p);
}
@@ -1360,7 +1422,7 @@ fndcl:
$$ = Nod(ODCLFUNC, nil, nil);
$$.Nname = newfuncname($1);
$$.Nname.Defn = $$;
- $$.Nname.Ntype = t; // TODO: check if nname already has an ntype
+ $$.Nname.Param.Ntype = t; // TODO: check if nname already has an ntype
declare($$.Nname, PFUNC);
funchdr($$);
@@ -1395,7 +1457,7 @@ fndcl:
$$.Func.Shortname = newfuncname($4);
$$.Nname = methodname1($$.Func.Shortname, rcvr.Right);
$$.Nname.Defn = $$;
- $$.Nname.Ntype = t;
+ $$.Nname.Param.Ntype = t;
$$.Nname.Nointerface = nointerface;
declare($$.Nname, PFUNC);
@@ -1944,7 +2006,7 @@ oliteral:
hidden_import:
LIMPORT LNAME LLITERAL ';'
{
- importimport($2, $3.U.Sval);
+ importimport($2, $3.U.(string));
}
| LVAR hidden_pkg_importsym hidden_type ';'
{
@@ -1975,9 +2037,9 @@ hidden_import:
importlist = list(importlist, $2);
if Debug['E'] > 0 {
- print("import [%q] func %lN \n", importpkg.Path, $2);
+ fmt.Printf("import [%q] func %v \n", importpkg.Path, $2)
if Debug['m'] > 2 && $2.Func.Inl != nil {
- print("inl body:%+H\n", $2.Func.Inl);
+ fmt.Printf("inl body:%v\n", $2.Func.Inl)
}
}
}
@@ -2171,14 +2233,14 @@ hidden_literal:
$$ = nodlit($2);
switch($$.Val.Ctype){
case CTINT, CTRUNE:
- mpnegfix($$.Val.U.Xval);
+ mpnegfix($$.Val.U.(*Mpint));
break;
case CTFLT:
- mpnegflt($$.Val.U.Fval);
+ mpnegflt($$.Val.U.(*Mpflt));
break;
case CTCPLX:
- mpnegflt(&$$.Val.U.Cval.Real);
- mpnegflt(&$$.Val.U.Cval.Imag);
+ mpnegflt(&$$.Val.U.(*Mpcplx).Real);
+ mpnegflt(&$$.Val.U.(*Mpcplx).Imag);
break;
default:
Yyerror("bad negated constant");
@@ -2198,11 +2260,11 @@ hidden_constant:
{
if $2.Val.Ctype == CTRUNE && $4.Val.Ctype == CTINT {
$$ = $2;
- mpaddfixfix($2.Val.U.Xval, $4.Val.U.Xval, 0);
+ mpaddfixfix($2.Val.U.(*Mpint), $4.Val.U.(*Mpint), 0);
break;
}
- $4.Val.U.Cval.Real = $4.Val.U.Cval.Imag;
- Mpmovecflt(&$4.Val.U.Cval.Imag, 0.0);
+ $4.Val.U.(*Mpcplx).Real = $4.Val.U.(*Mpcplx).Imag;
+ Mpmovecflt(&$4.Val.U.(*Mpcplx).Imag, 0.0);
$$ = nodcplxlit($2.Val, $4.Val);
}
diff --git a/src/cmd/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go
similarity index 98%
rename from src/cmd/internal/gc/gsubr.go
rename to src/cmd/compile/internal/gc/gsubr.go
index 53b3f6c41d..5ec4587e74 100644
--- a/src/cmd/internal/gc/gsubr.go
+++ b/src/cmd/compile/internal/gc/gsubr.go
@@ -90,6 +90,10 @@ func Gbranch(as int, t *Type, likely int) *obj.Prog {
p.From.Offset = int64(obj.Bool2int(likely > 0))
}
+ if Debug['g'] != 0 {
+ fmt.Printf("%v\n", p)
+ }
+
return p
}
@@ -210,7 +214,7 @@ func ggloblnod(nam *Node) {
p.To.Sym = nil
p.To.Type = obj.TYPE_CONST
p.To.Offset = nam.Type.Width
- if nam.Readonly {
+ if nam.Name.Readonly {
p.From3.Offset = obj.RODATA
}
if nam.Type != nil && !haspointers(nam.Type) {
@@ -365,7 +369,7 @@ func Naddr(a *obj.Addr, n *Node) {
if s == nil {
s = Lookup(".noname")
}
- if n.Method {
+ if n.Name.Method {
if n.Type != nil {
if n.Type.Sym != nil {
if n.Type.Sym.Pkg != nil {
@@ -408,20 +412,20 @@ func Naddr(a *obj.Addr, n *Node) {
case CTFLT:
a.Type = obj.TYPE_FCONST
- a.Val = mpgetflt(n.Val.U.Fval)
+ a.Val = mpgetflt(n.Val.U.(*Mpflt))
case CTINT, CTRUNE:
a.Sym = nil
a.Type = obj.TYPE_CONST
- a.Offset = Mpgetfix(n.Val.U.Xval)
+ a.Offset = Mpgetfix(n.Val.U.(*Mpint))
case CTSTR:
- datagostring(n.Val.U.Sval, a)
+ datagostring(n.Val.U.(string), a)
case CTBOOL:
a.Sym = nil
a.Type = obj.TYPE_CONST
- a.Offset = int64(obj.Bool2int(n.Val.U.Bval))
+ a.Offset = int64(obj.Bool2int(n.Val.U.(bool)))
case CTNIL:
a.Sym = nil
@@ -621,20 +625,20 @@ func gclean() {
for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ {
n := reg[r-Thearch.REGMIN]
if n != 0 {
- Yyerror("reg %v left allocated", obj.Rconv(r))
if Debug['v'] != 0 {
Regdump()
}
+ Yyerror("reg %v left allocated", obj.Rconv(r))
}
}
for r := Thearch.FREGMIN; r <= Thearch.FREGMAX; r++ {
n := reg[r-Thearch.REGMIN]
if n != 0 {
- Yyerror("reg %v left allocated", obj.Rconv(r))
if Debug['v'] != 0 {
Regdump()
}
+ Yyerror("reg %v left allocated", obj.Rconv(r))
}
}
}
diff --git a/src/cmd/internal/gc/init.go b/src/cmd/compile/internal/gc/init.go
similarity index 98%
rename from src/cmd/internal/gc/init.go
rename to src/cmd/compile/internal/gc/init.go
index b5d1e505a5..92bfeecdef 100644
--- a/src/cmd/internal/gc/init.go
+++ b/src/cmd/compile/internal/gc/init.go
@@ -116,7 +116,7 @@ func fninit(n *NodeList) {
initsym := Lookup("init")
fn.Nname = newname(initsym)
fn.Nname.Defn = fn
- fn.Nname.Ntype = Nod(OTFUNC, nil, nil)
+ fn.Nname.Param.Ntype = Nod(OTFUNC, nil, nil)
declare(fn.Nname, PFUNC)
funchdr(fn)
diff --git a/src/cmd/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go
similarity index 98%
rename from src/cmd/internal/gc/inl.go
rename to src/cmd/compile/internal/gc/inl.go
index dd2087dec3..22a5d3d9fe 100644
--- a/src/cmd/internal/gc/inl.go
+++ b/src/cmd/compile/internal/gc/inl.go
@@ -511,10 +511,10 @@ func mkinlcall(np **Node, fn *Node, isddd bool) {
func tinlvar(t *Type) *Node {
if t.Nname != nil && !isblank(t.Nname) {
- if t.Nname.Inlvar == nil {
+ if t.Nname.Name.Inlvar == nil {
Fatal("missing inlvar for %v\n", t.Nname)
}
- return t.Nname.Inlvar
+ return t.Nname.Name.Inlvar
}
typecheck(&nblank, Erv|Easgn)
@@ -577,13 +577,13 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) {
continue
}
if ll.N.Op == ONAME {
- ll.N.Inlvar = inlvar(ll.N)
+ ll.N.Name.Inlvar = inlvar(ll.N)
// Typecheck because inlvar is not necessarily a function parameter.
- typecheck(&ll.N.Inlvar, Erv)
+ typecheck(&ll.N.Name.Inlvar, Erv)
if ll.N.Class&^PHEAP != PAUTO {
- ninit = list(ninit, Nod(ODCL, ll.N.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs
+ ninit = list(ninit, Nod(ODCL, ll.N.Name.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs
}
}
}
@@ -594,7 +594,7 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) {
if t != nil && t.Nname != nil && !isblank(t.Nname) {
m = inlvar(t.Nname)
typecheck(&m, Erv)
- t.Nname.Inlvar = m
+ t.Nname.Name.Inlvar = m
} else {
// anonymous return values, synthesize names for use in assignment that replaces return
m = retvar(t, i)
@@ -611,7 +611,7 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) {
// method call with a receiver.
t := getthisx(fn.Type).Type
- if t != nil && t.Nname != nil && !isblank(t.Nname) && t.Nname.Inlvar == nil {
+ if t != nil && t.Nname != nil && !isblank(t.Nname) && t.Nname.Name.Inlvar == nil {
Fatal("missing inlvar for %v\n", t.Nname)
}
if n.Left.Left == nil {
@@ -680,7 +680,7 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) {
// append receiver inlvar to LHS.
t := getthisx(fn.Type).Type
- if t != nil && t.Nname != nil && !isblank(t.Nname) && t.Nname.Inlvar == nil {
+ if t != nil && t.Nname != nil && !isblank(t.Nname) && t.Nname.Name.Inlvar == nil {
Fatal("missing inlvar for %v\n", t.Nname)
}
if t == nil {
@@ -907,11 +907,11 @@ func inlsubst(n *Node) *Node {
switch n.Op {
case ONAME:
- if n.Inlvar != nil { // These will be set during inlnode
+ if n.Name.Inlvar != nil { // These will be set during inlnode
if Debug['m'] > 2 {
- fmt.Printf("substituting name %v -> %v\n", Nconv(n, obj.FmtSign), Nconv(n.Inlvar, obj.FmtSign))
+ fmt.Printf("substituting name %v -> %v\n", Nconv(n, obj.FmtSign), Nconv(n.Name.Inlvar, obj.FmtSign))
}
- return n.Inlvar
+ return n.Name.Inlvar
}
if Debug['m'] > 2 {
diff --git a/src/cmd/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go
similarity index 95%
rename from src/cmd/internal/gc/lex.go
rename to src/cmd/compile/internal/gc/lex.go
index 4bbda957a5..cf41c40964 100644
--- a/src/cmd/internal/gc/lex.go
+++ b/src/cmd/compile/internal/gc/lex.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:generate go tool yacc go.y
-//go:generate go run yaccerrors.go
//go:generate go run mkbuiltin.go runtime unsafe
package gc
@@ -35,7 +34,11 @@ var goarch string
var goroot string
-var Debug_wb int
+var (
+ Debug_wb int
+ Debug_append int
+ Debug_slice int
+)
// Debug arguments.
// These can be specified with the -d flag, as in "-d nil"
@@ -45,9 +48,12 @@ var debugtab = []struct {
name string
val *int
}{
- {"nil", &Debug_checknil}, // print information about nil checks
- {"typeassert", &Debug_typeassert}, // print information about type assertion inlining
+ {"append", &Debug_append}, // print information about append compilation
{"disablenil", &Disable_checknil}, // disable nil checks
+ {"gcprog", &Debug_gcprog}, // print dump of GC programs
+ {"nil", &Debug_checknil}, // print information about nil checks
+ {"slice", &Debug_slice}, // print information about slice compilation
+ {"typeassert", &Debug_typeassert}, // print information about type assertion inlining
{"wb", &Debug_wb}, // print information about write barriers
}
@@ -308,7 +314,7 @@ func Main() {
lexlineno = 1
for _, infile = range flag.Args() {
- linehist(infile, 0, 0)
+ linehistpush(infile)
curio.infile = infile
var err error
@@ -339,7 +345,7 @@ func Main() {
errorexit()
}
- linehist("", 0, 0)
+ linehistpop()
if curio.bin != nil {
obj.Bterm(curio.bin)
}
@@ -394,7 +400,7 @@ func Main() {
// This needs to run before escape analysis,
// because variables captured by value do not escape.
for l := xtop; l != nil; l = l.Next {
- if l.N.Op == ODCLFUNC && l.N.Closure != nil {
+ if l.N.Op == ODCLFUNC && l.N.Param.Closure != nil {
Curfn = l.N
capturevars(l.N)
}
@@ -440,17 +446,15 @@ func Main() {
// which stores the addresses of stack variables into the closure.
// If the closure does not escape, it needs to be on the stack
// or else the stack copier will not update it.
+ // Large values are also moved off stack in escape analysis;
+ // because large values may contain pointers, it must happen early.
escapes(xtop)
- // Escape analysis moved escaped values off stack.
- // Move large values off stack too.
- movelarge(xtop)
-
// Phase 7: Transform closure bodies to properly reference captured variables.
// This needs to happen before walk, because closures must be transformed
// before walk reaches a call of a closure.
for l := xtop; l != nil; l = l.Next {
- if l.N.Op == ODCLFUNC && l.N.Closure != nil {
+ if l.N.Op == ODCLFUNC && l.N.Param.Closure != nil {
Curfn = l.N
transformclosure(l.N)
}
@@ -577,7 +581,7 @@ func findpkg(name string) (file string, ok bool) {
if obj.Access(file, 0) >= 0 {
return file, true
}
- file = fmt.Sprintf("%s.%c", name, Thearch.Thechar)
+ file = fmt.Sprintf("%s.o", name)
if obj.Access(file, 0) >= 0 {
return file, true
}
@@ -599,7 +603,7 @@ func findpkg(name string) (file string, ok bool) {
if obj.Access(file, 0) >= 0 {
return file, true
}
- file = fmt.Sprintf("%s/%s.%c", p.dir, name, Thearch.Thechar)
+ file = fmt.Sprintf("%s/%s.o", p.dir, name)
if obj.Access(file, 0) >= 0 {
return file, true
}
@@ -620,7 +624,7 @@ func findpkg(name string) (file string, ok bool) {
if obj.Access(file, 0) >= 0 {
return file, true
}
- file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.%c", goroot, goos, goarch, suffixsep, suffix, name, Thearch.Thechar)
+ file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.o", goroot, goos, goarch, suffixsep, suffix, name)
if obj.Access(file, 0) >= 0 {
return file, true
}
@@ -631,7 +635,7 @@ func findpkg(name string) (file string, ok bool) {
func fakeimport() {
importpkg = mkpkg("fake")
- cannedimports("fake.6", "$$\n")
+ cannedimports("fake.o", "$$\n")
}
func importfile(f *Val, line int) {
@@ -641,13 +645,13 @@ func importfile(f *Val, line int) {
return
}
- if len(f.U.Sval) == 0 {
+ if len(f.U.(string)) == 0 {
Yyerror("import path is empty")
fakeimport()
return
}
- if isbadimport(f.U.Sval) {
+ if isbadimport(f.U.(string)) {
fakeimport()
return
}
@@ -656,29 +660,29 @@ func importfile(f *Val, line int) {
// but we reserve the import path "main" to identify
// the main package, just as we reserve the import
// path "math" to identify the standard math package.
- if f.U.Sval == "main" {
+ if f.U.(string) == "main" {
Yyerror("cannot import \"main\"")
errorexit()
}
- if myimportpath != "" && f.U.Sval == myimportpath {
- Yyerror("import %q while compiling that package (import cycle)", f.U.Sval)
+ if myimportpath != "" && f.U.(string) == myimportpath {
+ Yyerror("import %q while compiling that package (import cycle)", f.U.(string))
errorexit()
}
- if f.U.Sval == "unsafe" {
+ if f.U.(string) == "unsafe" {
if safemode != 0 {
Yyerror("cannot import package unsafe")
errorexit()
}
- importpkg = mkpkg(f.U.Sval)
- cannedimports("unsafe.6", unsafeimport)
+ importpkg = mkpkg(f.U.(string))
+ cannedimports("unsafe.o", unsafeimport)
imported_unsafe = 1
return
}
- path_ := f.U.Sval
+ path_ := f.U.(string)
if islocalname(path_) {
if path_[0] == '/' {
Yyerror("import path cannot be absolute path")
@@ -704,7 +708,7 @@ func importfile(f *Val, line int) {
file, found := findpkg(path_)
if !found {
- Yyerror("can't find import: %q", f.U.Sval)
+ Yyerror("can't find import: %q", f.U.(string))
errorexit()
}
@@ -729,7 +733,7 @@ func importfile(f *Val, line int) {
var imp *obj.Biobuf
imp, err = obj.Bopenr(file)
if err != nil {
- Yyerror("can't open import: %q: %v", f.U.Sval, err)
+ Yyerror("can't open import: %q: %v", f.U.(string), err)
errorexit()
}
@@ -758,7 +762,7 @@ func importfile(f *Val, line int) {
// assume files move (get installed)
// so don't record the full path.
- linehist(file[len(file)-len(path_)-2:], -1, 1) // acts as #pragma lib
+ linehistpragma(file[len(file)-len(path_)-2:]) // acts as #pragma lib
/*
* position the input right
@@ -792,7 +796,7 @@ func importfile(f *Val, line int) {
return
}
- Yyerror("no import in %q", f.U.Sval)
+ Yyerror("no import in %q", f.U.(string))
unimportfile()
}
@@ -1060,8 +1064,8 @@ l0:
ungetc(int(v))
}
- yylval.val.U.Xval = new(Mpint)
- Mpmovecfix(yylval.val.U.Xval, v)
+ yylval.val.U = new(Mpint)
+ Mpmovecfix(yylval.val.U.(*Mpint), v)
yylval.val.Ctype = CTRUNE
if Debug['x'] != 0 {
fmt.Printf("lex: codepoint literal\n")
@@ -1399,11 +1403,11 @@ ncu:
ungetc(c)
str = lexbuf.String()
- yylval.val.U.Xval = new(Mpint)
- mpatofix(yylval.val.U.Xval, str)
- if yylval.val.U.Xval.Ovf {
+ yylval.val.U = new(Mpint)
+ mpatofix(yylval.val.U.(*Mpint), str)
+ if yylval.val.U.(*Mpint).Ovf {
Yyerror("overflow in constant")
- Mpmovecfix(yylval.val.U.Xval, 0)
+ Mpmovecfix(yylval.val.U.(*Mpint), 0)
}
yylval.val.Ctype = CTINT
@@ -1430,6 +1434,11 @@ casedot:
}
caseep:
+ if importpkg == nil && (c == 'p' || c == 'P') {
+ // p is allowed in .a/.o imports,
+ // but not in .go sources. See #9036.
+ Yyerror("malformed floating point constant")
+ }
cp.WriteByte(byte(c))
c = getc()
if c == '+' || c == '-' {
@@ -1438,7 +1447,7 @@ caseep:
}
if !yy_isdigit(c) {
- Yyerror("malformed fp constant exponent")
+ Yyerror("malformed floating point constant exponent")
}
for yy_isdigit(c) {
cp.WriteByte(byte(c))
@@ -1455,12 +1464,12 @@ casei:
cp = nil
str = lexbuf.String()
- yylval.val.U.Cval = new(Mpcplx)
- Mpmovecflt(&yylval.val.U.Cval.Real, 0.0)
- mpatoflt(&yylval.val.U.Cval.Imag, str)
- if yylval.val.U.Cval.Imag.Val.IsInf() {
+ yylval.val.U = new(Mpcplx)
+ Mpmovecflt(&yylval.val.U.(*Mpcplx).Real, 0.0)
+ mpatoflt(&yylval.val.U.(*Mpcplx).Imag, str)
+ if yylval.val.U.(*Mpcplx).Imag.Val.IsInf() {
Yyerror("overflow in imaginary constant")
- Mpmovecflt(&yylval.val.U.Cval.Real, 0.0)
+ Mpmovecflt(&yylval.val.U.(*Mpcplx).Real, 0.0)
}
yylval.val.Ctype = CTCPLX
@@ -1475,11 +1484,11 @@ caseout:
ungetc(c)
str = lexbuf.String()
- yylval.val.U.Fval = newMpflt()
- mpatoflt(yylval.val.U.Fval, str)
- if yylval.val.U.Fval.Val.IsInf() {
+ yylval.val.U = newMpflt()
+ mpatoflt(yylval.val.U.(*Mpflt), str)
+ if yylval.val.U.(*Mpflt).Val.IsInf() {
Yyerror("overflow in float constant")
- Mpmovecflt(yylval.val.U.Fval, 0.0)
+ Mpmovecflt(yylval.val.U.(*Mpflt), 0.0)
}
yylval.val.Ctype = CTFLT
@@ -1490,7 +1499,7 @@ caseout:
return LLITERAL
strlit:
- yylval.val.U.Sval = internString(cp.Bytes())
+ yylval.val.U = internString(cp.Bytes())
yylval.val.Ctype = CTSTR
if Debug['x'] != 0 {
fmt.Printf("lex: string literal\n")
@@ -1649,7 +1658,7 @@ func getlinepragma() int {
}
name = text[:linep-1]
- linehist(name, int32(n), 0)
+ linehistupdate(name, n)
return c
out:
@@ -2590,6 +2599,6 @@ func mkpackage(pkgname string) {
if i := strings.LastIndex(p, "."); i >= 0 {
p = p[:i]
}
- outfile = fmt.Sprintf("%s.%c", p, Thearch.Thechar)
+ outfile = fmt.Sprintf("%s.o", p)
}
}
diff --git a/src/cmd/internal/gc/mkbuiltin.go b/src/cmd/compile/internal/gc/mkbuiltin.go
similarity index 83%
rename from src/cmd/internal/gc/mkbuiltin.go
rename to src/cmd/compile/internal/gc/mkbuiltin.go
index b2362a6f01..f4569b48c2 100644
--- a/src/cmd/internal/gc/mkbuiltin.go
+++ b/src/cmd/compile/internal/gc/mkbuiltin.go
@@ -13,21 +13,14 @@ package main
import (
"bufio"
"fmt"
- "go/build"
"io"
"log"
"os"
"os/exec"
- "runtime"
"strings"
)
func main() {
- gochar, err := build.ArchChar(runtime.GOARCH)
- if err != nil {
- log.Fatal(err)
- }
-
f, err := os.Create("builtin.go")
if err != nil {
log.Fatal(err)
@@ -40,7 +33,7 @@ func main() {
fmt.Fprintln(w, "package gc")
for _, name := range os.Args[1:] {
- mkbuiltin(w, gochar, name)
+ mkbuiltin(w, name)
}
if err := w.Flush(); err != nil {
@@ -49,11 +42,11 @@ func main() {
}
// Compile .go file, import data from .6 file, and write Go string version.
-func mkbuiltin(w io.Writer, gochar string, name string) {
- if err := exec.Command("go", "tool", gochar+"g", "-A", "builtin/"+name+".go").Run(); err != nil {
+func mkbuiltin(w io.Writer, name string) {
+ if err := exec.Command("go", "tool", "compile", "-A", "builtin/"+name+".go").Run(); err != nil {
log.Fatal(err)
}
- obj := fmt.Sprintf("%s.%s", name, gochar)
+ obj := "name.o"
defer os.Remove(obj)
r, err := os.Open(obj)
@@ -77,7 +70,7 @@ Begin:
fmt.Fprintf(w, "\nconst %simport = \"\" +\n", name)
// sys.go claims to be in package PACKAGE to avoid
- // conflicts during "6g sys.go". Rename PACKAGE to $2.
+ // conflicts during "go tool compile sys.go". Rename PACKAGE to $2.
replacer := strings.NewReplacer("PACKAGE", name)
// Process imports, stopping at $$ that closes them.
diff --git a/src/cmd/internal/gc/mparith2.go b/src/cmd/compile/internal/gc/mparith2.go
similarity index 98%
rename from src/cmd/internal/gc/mparith2.go
rename to src/cmd/compile/internal/gc/mparith2.go
index de96e97809..2c7e5176ac 100644
--- a/src/cmd/internal/gc/mparith2.go
+++ b/src/cmd/compile/internal/gc/mparith2.go
@@ -5,7 +5,7 @@
package gc
import (
- "cmd/internal/gc/big"
+ "cmd/compile/internal/big"
"cmd/internal/obj"
"fmt"
)
@@ -13,7 +13,7 @@ import (
/// implements fix arithmetic
func mpsetovf(a *Mpint) {
- a.Val.SetUint64(0)
+ a.Val.SetUint64(1) // avoid spurious div-zero errors
a.Ovf = true
}
diff --git a/src/cmd/internal/gc/mparith3.go b/src/cmd/compile/internal/gc/mparith3.go
similarity index 84%
rename from src/cmd/internal/gc/mparith3.go
rename to src/cmd/compile/internal/gc/mparith3.go
index 2700b64a89..0e0b626475 100644
--- a/src/cmd/internal/gc/mparith3.go
+++ b/src/cmd/compile/internal/gc/mparith3.go
@@ -5,7 +5,7 @@
package gc
import (
- "cmd/internal/gc/big"
+ "cmd/compile/internal/big"
"cmd/internal/obj"
"fmt"
"math"
@@ -105,24 +105,8 @@ func mpcmpfltc(b *Mpflt, c float64) int {
return mpcmpfltflt(b, &a)
}
-func mpgetfltN(a *Mpflt, prec int, bias int) float64 {
- var x float64
- switch prec {
- case 53:
- x, _ = a.Val.Float64()
- case 24:
- // We should be using a.Val.Float32() here but that seems incorrect
- // for certain denormal values (all.bash fails). The current code
- // appears to work for all existing test cases, though there ought
- // to be issues with denormal numbers that are incorrectly rounded.
- // TODO(gri) replace with a.Val.Float32() once correctly working
- // See also: https://github.com/golang/go/issues/10321
- var t Mpflt
- t.Val.SetPrec(24).Set(&a.Val)
- x, _ = t.Val.Float64()
- default:
- panic("unreachable")
- }
+func mpgetflt(a *Mpflt) float64 {
+ x, _ := a.Val.Float64()
// check for overflow
if math.IsInf(x, 0) && nsavederrors+nerrors == 0 {
@@ -132,12 +116,16 @@ func mpgetfltN(a *Mpflt, prec int, bias int) float64 {
return x
}
-func mpgetflt(a *Mpflt) float64 {
- return mpgetfltN(a, 53, -1023)
-}
-
func mpgetflt32(a *Mpflt) float64 {
- return mpgetfltN(a, 24, -127)
+ x32, _ := a.Val.Float32()
+ x := float64(x32)
+
+ // check for overflow
+ if math.IsInf(x, 0) && nsavederrors+nerrors == 0 {
+ Yyerror("mpgetflt32 ovf")
+ }
+
+ return x
}
func Mpmovecflt(a *Mpflt, c float64) {
diff --git a/src/cmd/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
similarity index 98%
rename from src/cmd/internal/gc/obj.go
rename to src/cmd/compile/internal/gc/obj.go
index 05c5b1a811..9bb334ca34 100644
--- a/src/cmd/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -102,8 +102,7 @@ func dumpobj() {
obj.Bputc(bout, 0)
}
obj.Bseek(bout, startobj-ArhdrSize, 0)
- name := fmt.Sprintf("_go_.%c", Thearch.Thechar)
- formathdr(arhdr[:], name, size)
+ formathdr(arhdr[:], "_go_.o", size)
bout.Write(arhdr[:])
}
@@ -382,11 +381,11 @@ func gdata(nam *Node, nr *Node, wid int) {
if nr.Op == OLITERAL {
switch nr.Val.Ctype {
case CTCPLX:
- gdatacomplex(nam, nr.Val.U.Cval)
+ gdatacomplex(nam, nr.Val.U.(*Mpcplx))
return
case CTSTR:
- gdatastring(nam, nr.Val.U.Sval)
+ gdatastring(nam, nr.Val.U.(string))
return
}
}
diff --git a/src/cmd/internal/gc/opnames.go b/src/cmd/compile/internal/gc/opnames.go
similarity index 100%
rename from src/cmd/internal/gc/opnames.go
rename to src/cmd/compile/internal/gc/opnames.go
diff --git a/src/cmd/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go
similarity index 89%
rename from src/cmd/internal/gc/order.go
rename to src/cmd/compile/internal/gc/order.go
index f08f5f20fe..ee0ec52e7b 100644
--- a/src/cmd/internal/gc/order.go
+++ b/src/cmd/compile/internal/gc/order.go
@@ -104,9 +104,23 @@ func ordercopyexpr(n *Node, t *Type, order *Order, clear int) *Node {
// If not, ordercheapexpr allocates a new tmp, emits tmp = n,
// and then returns tmp.
func ordercheapexpr(n *Node, order *Order) *Node {
+ if n == nil {
+ return nil
+ }
switch n.Op {
case ONAME, OLITERAL:
return n
+ case OLEN, OCAP:
+ l := ordercheapexpr(n.Left, order)
+ if l == n.Left {
+ return n
+ }
+ a := Nod(OXXX, nil, nil)
+ *a = *n
+ a.Orig = a
+ a.Left = l
+ typecheck(&a, Erv)
+ return a
}
return ordercopyexpr(n, n.Type, order, 0)
@@ -124,7 +138,7 @@ func ordersafeexpr(n *Node, order *Order) *Node {
case ONAME, OLITERAL:
return n
- case ODOT:
+ case ODOT, OLEN, OCAP:
l := ordersafeexpr(n.Left, order)
if l == n.Left {
return n
@@ -264,7 +278,7 @@ func orderblock(l **NodeList) {
func orderexprinplace(np **Node, outer *Order) {
n := *np
var order Order
- orderexpr(&n, &order)
+ orderexpr(&n, &order, nil)
addinit(&n, order.out)
// insert new temporaries from order
@@ -358,8 +372,8 @@ func ordercallargs(l **NodeList, order *Order) {
// Ordercall orders the call expression n.
// n->op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY.
func ordercall(n *Node, order *Order) {
- orderexpr(&n.Left, order)
- orderexpr(&n.Right, order) // ODDDARG temp
+ orderexpr(&n.Left, order, nil)
+ orderexpr(&n.Right, order, nil) // ODDDARG temp
ordercallargs(&n.List, order)
}
@@ -447,8 +461,14 @@ func orderstmt(n *Node, order *Order) {
case OVARKILL:
order.out = list(order.out, n)
- case OAS,
- OAS2,
+ case OAS:
+ t := marktemp(order)
+ orderexpr(&n.Left, order, nil)
+ orderexpr(&n.Right, order, n.Left)
+ ordermapassign(n, order)
+ cleantemp(t, order)
+
+ case OAS2,
OCLOSE,
OCOPY,
OPRINT,
@@ -456,38 +476,36 @@ func orderstmt(n *Node, order *Order) {
ORECOVER,
ORECV:
t := marktemp(order)
- orderexpr(&n.Left, order)
- orderexpr(&n.Right, order)
+ orderexpr(&n.Left, order, nil)
+ orderexpr(&n.Right, order, nil)
orderexprlist(n.List, order)
orderexprlist(n.Rlist, order)
switch n.Op {
- case OAS, OAS2, OAS2DOTTYPE:
+ case OAS2, OAS2DOTTYPE:
ordermapassign(n, order)
-
default:
order.out = list(order.out, n)
}
-
cleantemp(t, order)
- // Special: rewrite l op= r into l = l op r.
- // This simplies quite a few operations;
- // most important is that it lets us separate
- // out map read from map write when l is
- // a map index expression.
case OASOP:
+ // Special: rewrite l op= r into l = l op r.
+ // This simplies quite a few operations;
+ // most important is that it lets us separate
+ // out map read from map write when l is
+ // a map index expression.
t := marktemp(order)
- orderexpr(&n.Left, order)
+ orderexpr(&n.Left, order, nil)
n.Left = ordersafeexpr(n.Left, order)
- tmp1 := treecopy(n.Left)
+ tmp1 := treecopy(n.Left, 0)
if tmp1.Op == OINDEXMAP {
tmp1.Etype = 0 // now an rvalue not an lvalue
}
tmp1 = ordercopyexpr(tmp1, n.Left.Type, order, 0)
n.Right = Nod(int(n.Etype), tmp1, n.Right)
typecheck(&n.Right, Erv)
- orderexpr(&n.Right, order)
+ orderexpr(&n.Right, order, nil)
n.Etype = 0
n.Op = OAS
ordermapassign(n, order)
@@ -500,8 +518,8 @@ func orderstmt(n *Node, order *Order) {
orderexprlist(n.List, order)
r := n.Rlist.N
- orderexpr(&r.Left, order)
- orderexpr(&r.Right, order)
+ orderexpr(&r.Left, order, nil)
+ orderexpr(&r.Right, order, nil)
// See case OINDEXMAP below.
if r.Right.Op == OARRAYBYTESTR {
@@ -527,7 +545,7 @@ func orderstmt(n *Node, order *Order) {
t := marktemp(order)
orderexprlist(n.List, order)
- orderexpr(&n.Rlist.N.Left, order) // i in i.(T)
+ orderexpr(&n.Rlist.N.Left, order, nil) // i in i.(T)
if isblank(n.List.N) {
order.out = list(order.out, n)
} else {
@@ -548,7 +566,7 @@ func orderstmt(n *Node, order *Order) {
t := marktemp(order)
orderexprlist(n.List, order)
- orderexpr(&n.Rlist.N.Left, order) // arg to recv
+ orderexpr(&n.Rlist.N.Left, order, nil) // arg to recv
ch := n.Rlist.N.Left.Type
tmp1 := ordertemp(ch.Type, order, haspointers(ch.Type))
var tmp2 *Node
@@ -617,8 +635,8 @@ func orderstmt(n *Node, order *Order) {
case ODELETE:
t := marktemp(order)
- orderexpr(&n.List.N, order)
- orderexpr(&n.List.Next.N, order)
+ orderexpr(&n.List.N, order, nil)
+ orderexpr(&n.List.Next.N, order, nil)
orderaddrtemp(&n.List.Next.N, order) // map key
order.out = list(order.out, n)
cleantemp(t, order)
@@ -659,7 +677,7 @@ func orderstmt(n *Node, order *Order) {
case OPANIC:
t := marktemp(order)
- orderexpr(&n.Left, order)
+ orderexpr(&n.Left, order, nil)
if !Isinter(n.Left.Type) {
orderaddrtemp(&n.Left, order)
}
@@ -677,7 +695,7 @@ func orderstmt(n *Node, order *Order) {
case ORANGE:
t := marktemp(order)
- orderexpr(&n.Right, order)
+ orderexpr(&n.Right, order, nil)
switch n.Type.Etype {
default:
Fatal("orderstmt range %v", n.Type)
@@ -793,7 +811,7 @@ func orderstmt(n *Node, order *Order) {
// r->left is x, r->ntest is ok, r->right is ORECV, r->right->left is c.
// r->left == N means 'case <-c'.
// c is always evaluated; x and ok are only evaluated when assigned.
- orderexpr(&r.Right.Left, order)
+ orderexpr(&r.Right.Left, order, nil)
if r.Right.Left.Op != ONAME {
r.Right.Left = ordercopyexpr(r.Right.Left, r.Right.Left.Type, order, 0)
@@ -853,12 +871,12 @@ func orderstmt(n *Node, order *Order) {
// case c <- x
// r->left is c, r->right is x, both are always evaluated.
- orderexpr(&r.Left, order)
+ orderexpr(&r.Left, order, nil)
if !istemp(r.Left) {
r.Left = ordercopyexpr(r.Left, r.Left.Type, order, 0)
}
- orderexpr(&r.Right, order)
+ orderexpr(&r.Right, order, nil)
if !istemp(r.Right) {
r.Right = ordercopyexpr(r.Right, r.Right.Type, order, 0)
}
@@ -884,8 +902,8 @@ func orderstmt(n *Node, order *Order) {
case OSEND:
t := marktemp(order)
- orderexpr(&n.Left, order)
- orderexpr(&n.Right, order)
+ orderexpr(&n.Left, order, nil)
+ orderexpr(&n.Right, order, nil)
orderaddrtemp(&n.Right, order)
order.out = list(order.out, n)
cleantemp(t, order)
@@ -900,7 +918,7 @@ func orderstmt(n *Node, order *Order) {
case OSWITCH:
t := marktemp(order)
- orderexpr(&n.Ntest, order)
+ orderexpr(&n.Ntest, order, nil)
for l := n.List; l != nil; l = l.Next {
if l.N.Op != OXCASE {
Fatal("order switch case %v", Oconv(int(l.N.Op), 0))
@@ -919,7 +937,7 @@ func orderstmt(n *Node, order *Order) {
// Orderexprlist orders the expression list l into order.
func orderexprlist(l *NodeList, order *Order) {
for ; l != nil; l = l.Next {
- orderexpr(&l.N, order)
+ orderexpr(&l.N, order, nil)
}
}
@@ -933,7 +951,10 @@ func orderexprlistinplace(l *NodeList, order *Order) {
// Orderexpr orders a single expression, appending side
// effects to order->out as needed.
-func orderexpr(np **Node, order *Order) {
+// If this is part of an assignment lhs = *np, lhs is given.
+// Otherwise lhs == nil. (When lhs != nil it may be possible
+// to avoid copying the result of the expression to a temporary.)
+func orderexpr(np **Node, order *Order, lhs *Node) {
n := *np
if n == nil {
return
@@ -944,8 +965,8 @@ func orderexpr(np **Node, order *Order) {
switch n.Op {
default:
- orderexpr(&n.Left, order)
- orderexpr(&n.Right, order)
+ orderexpr(&n.Left, order, nil)
+ orderexpr(&n.Right, order, nil)
orderexprlist(n.List, order)
orderexprlist(n.Rlist, order)
@@ -974,7 +995,7 @@ func orderexpr(np **Node, order *Order) {
haslit := false
for l := n.List; l != nil; l = l.Next {
hasbyte = hasbyte || l.N.Op == OARRAYBYTESTR
- haslit = haslit || l.N.Op == OLITERAL && len(l.N.Val.U.Sval) != 0
+ haslit = haslit || l.N.Op == OLITERAL && len(l.N.Val.U.(string)) != 0
}
if haslit && hasbyte {
@@ -986,8 +1007,8 @@ func orderexpr(np **Node, order *Order) {
}
case OCMPSTR:
- orderexpr(&n.Left, order)
- orderexpr(&n.Right, order)
+ orderexpr(&n.Left, order, nil)
+ orderexpr(&n.Right, order, nil)
// Mark string(byteSlice) arguments to reuse byteSlice backing
// buffer during conversion. String comparison does not
@@ -1001,9 +1022,9 @@ func orderexpr(np **Node, order *Order) {
// key must be addressable
case OINDEXMAP:
- orderexpr(&n.Left, order)
+ orderexpr(&n.Left, order, nil)
- orderexpr(&n.Right, order)
+ orderexpr(&n.Right, order, nil)
// For x = m[string(k)] where k is []byte, the allocation of
// backing bytes for the string can be avoided by reusing
@@ -1029,7 +1050,7 @@ func orderexpr(np **Node, order *Order) {
// concrete type (not interface) argument must be addressable
// temporary to pass to runtime.
case OCONVIFACE:
- orderexpr(&n.Left, order)
+ orderexpr(&n.Left, order, nil)
if !Isinter(n.Left.Type) {
orderaddrtemp(&n.Left, order)
@@ -1037,7 +1058,7 @@ func orderexpr(np **Node, order *Order) {
case OANDAND, OOROR:
mark := marktemp(order)
- orderexpr(&n.Left, order)
+ orderexpr(&n.Left, order, nil)
// Clean temporaries from first branch at beginning of second.
// Leave them on the stack so that they can be killed in the outer
@@ -1048,8 +1069,7 @@ func orderexpr(np **Node, order *Order) {
n.Right.Ninit = concat(l, n.Right.Ninit)
orderexprinplace(&n.Right, order)
- case OAPPEND,
- OCALLFUNC,
+ case OCALLFUNC,
OCALLINTER,
OCALLMETH,
OCAP,
@@ -1064,7 +1084,37 @@ func orderexpr(np **Node, order *Order) {
OREAL,
ORECOVER:
ordercall(n, order)
- n = ordercopyexpr(n, n.Type, order, 0)
+ if lhs == nil || lhs.Op != ONAME || flag_race != 0 {
+ n = ordercopyexpr(n, n.Type, order, 0)
+ }
+
+ case OAPPEND:
+ ordercallargs(&n.List, order)
+ if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.List.N) {
+ n = ordercopyexpr(n, n.Type, order, 0)
+ }
+
+ case OSLICE, OSLICEARR, OSLICESTR:
+ orderexpr(&n.Left, order, nil)
+ orderexpr(&n.Right.Left, order, nil)
+ n.Right.Left = ordercheapexpr(n.Right.Left, order)
+ orderexpr(&n.Right.Right, order, nil)
+ n.Right.Right = ordercheapexpr(n.Right.Right, order)
+ if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.Left) {
+ n = ordercopyexpr(n, n.Type, order, 0)
+ }
+
+ case OSLICE3, OSLICE3ARR:
+ orderexpr(&n.Left, order, nil)
+ orderexpr(&n.Right.Left, order, nil)
+ n.Right.Left = ordercheapexpr(n.Right.Left, order)
+ orderexpr(&n.Right.Right.Left, order, nil)
+ n.Right.Right.Left = ordercheapexpr(n.Right.Right.Left, order)
+ orderexpr(&n.Right.Right.Right, order, nil)
+ n.Right.Right.Right = ordercheapexpr(n.Right.Right.Right, order)
+ if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.Left) {
+ n = ordercopyexpr(n, n.Type, order, 0)
+ }
case OCLOSURE:
if n.Noescape && n.Func.Cvars != nil {
@@ -1072,8 +1122,8 @@ func orderexpr(np **Node, order *Order) {
}
case OARRAYLIT, OCALLPART:
- orderexpr(&n.Left, order)
- orderexpr(&n.Right, order)
+ orderexpr(&n.Left, order, nil)
+ orderexpr(&n.Right, order, nil)
orderexprlist(n.List, order)
orderexprlist(n.Rlist, order)
if n.Noescape {
@@ -1090,7 +1140,7 @@ func orderexpr(np **Node, order *Order) {
}
case ODOTTYPE, ODOTTYPE2:
- orderexpr(&n.Left, order)
+ orderexpr(&n.Left, order, nil)
// TODO(rsc): The Isfat is for consistency with componentgen and walkexpr.
// It needs to be removed in all three places.
// That would allow inlining x.(struct{*int}) the same as x.(*int).
@@ -1099,18 +1149,17 @@ func orderexpr(np **Node, order *Order) {
}
case ORECV:
- orderexpr(&n.Left, order)
+ orderexpr(&n.Left, order, nil)
n = ordercopyexpr(n, n.Type, order, 1)
case OEQ, ONE:
- orderexpr(&n.Left, order)
- orderexpr(&n.Right, order)
+ orderexpr(&n.Left, order, nil)
+ orderexpr(&n.Right, order, nil)
t := n.Left.Type
if t.Etype == TSTRUCT || Isfixedarray(t) {
// for complex comparisons, we need both args to be
// addressable so we can pass them to the runtime.
orderaddrtemp(&n.Left, order)
-
orderaddrtemp(&n.Right, order)
}
}
diff --git a/src/cmd/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go
similarity index 96%
rename from src/cmd/internal/gc/pgen.go
rename to src/cmd/compile/internal/gc/pgen.go
index 2c225c8778..c170060896 100644
--- a/src/cmd/internal/gc/pgen.go
+++ b/src/cmd/compile/internal/gc/pgen.go
@@ -5,8 +5,8 @@
package gc
import (
+ "cmd/compile/internal/ssa"
"cmd/internal/obj"
- "cmd/internal/ssa"
"crypto/md5"
"fmt"
"strings"
@@ -201,8 +201,8 @@ func cmpstackvar(a *Node, b *Node) int {
return bp - ap
}
- ap = obj.Bool2int(a.Needzero)
- bp = obj.Bool2int(b.Needzero)
+ ap = obj.Bool2int(a.Name.Needzero)
+ bp = obj.Bool2int(b.Name.Needzero)
if ap != bp {
return bp - ap
}
@@ -302,25 +302,6 @@ func allocauto(ptxt *obj.Prog) {
}
}
-func movelarge(l *NodeList) {
- for ; l != nil; l = l.Next {
- if l.N.Op == ODCLFUNC {
- movelargefn(l.N)
- }
- }
-}
-
-func movelargefn(fn *Node) {
- var n *Node
-
- for l := fn.Func.Dcl; l != nil; l = l.Next {
- n = l.N
- if n.Class == PAUTO && n.Type != nil && n.Type.Width > MaxStackVarSize {
- addrescapes(n)
- }
- }
-}
-
func Cgen_checknil(n *Node) {
if Disable_checknil != 0 {
return
diff --git a/src/cmd/internal/gc/plive.go b/src/cmd/compile/internal/gc/plive.go
similarity index 99%
rename from src/cmd/internal/gc/plive.go
rename to src/cmd/compile/internal/gc/plive.go
index 040a77814e..b4d0699d1f 100644
--- a/src/cmd/internal/gc/plive.go
+++ b/src/cmd/compile/internal/gc/plive.go
@@ -944,7 +944,7 @@ func onebitwalktype1(t *Type, xoffset *int64, bv Bvec) {
*xoffset += t.Width
case TARRAY:
- // The value of t->bound is -1 for slices types and >0 for
+ // The value of t->bound is -1 for slices types and >=0 for
// for fixed array types. All other values are invalid.
if t.Bound < -1 {
Fatal("onebitwalktype1: invalid bound, %v", t)
@@ -1281,8 +1281,8 @@ func livenessepilogue(lv *Liveness) {
}
bvset(all, pos) // silence future warnings in this block
n = lv.vars[pos]
- if !n.Needzero {
- n.Needzero = true
+ if !n.Name.Needzero {
+ n.Name.Needzero = true
if debuglive >= 1 {
Warnl(int(p.Lineno), "%v: %v is ambiguously live", Curfn.Nname, Nconv(n, obj.FmtLong))
}
diff --git a/src/cmd/internal/gc/popt.go b/src/cmd/compile/internal/gc/popt.go
similarity index 100%
rename from src/cmd/internal/gc/popt.go
rename to src/cmd/compile/internal/gc/popt.go
diff --git a/src/cmd/internal/gc/racewalk.go b/src/cmd/compile/internal/gc/racewalk.go
similarity index 94%
rename from src/cmd/internal/gc/racewalk.go
rename to src/cmd/compile/internal/gc/racewalk.go
index e7f35006dc..05a902e8c1 100644
--- a/src/cmd/internal/gc/racewalk.go
+++ b/src/cmd/compile/internal/gc/racewalk.go
@@ -186,31 +186,6 @@ func racewalknode(np **Node, init **NodeList, wr int, skip int) {
// as we do not instrument runtime code.
// typedslicecopy is instrumented in runtime.
case OCALLFUNC:
- if n.Left.Sym != nil && n.Left.Sym.Pkg == Runtimepkg && (strings.HasPrefix(n.Left.Sym.Name, "writebarrier") || n.Left.Sym.Name == "typedmemmove") {
- // Find the dst argument.
- // The list can be reordered, so it's not necessary just the first or the second element.
- var l *NodeList
- for l = n.List; l != nil; l = l.Next {
- if n.Left.Sym.Name == "typedmemmove" {
- if l.N.Left.Xoffset == int64(Widthptr) {
- break
- }
- } else {
- if l.N.Left.Xoffset == 0 {
- break
- }
- }
- }
-
- if l == nil {
- Fatal("racewalk: writebarrier no arg")
- }
- if l.N.Right.Op != OADDR {
- Fatal("racewalk: writebarrier bad arg")
- }
- callinstr(&l.N.Right.Left, init, 1, 0)
- }
-
racewalknode(&n.Left, init, 0, 0)
goto ret
@@ -324,9 +299,8 @@ func racewalknode(np **Node, init **NodeList, wr int, skip int) {
}
goto ret
- // Seems to only lead to double instrumentation.
- //racewalknode(&n->left, init, 0, 0);
case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR:
+ racewalknode(&n.Left, init, 0, 0)
goto ret
case OADDR:
@@ -509,7 +483,7 @@ func callinstr(np **Node, init **NodeList, wr int, skip int) bool {
*np = n
}
- n = treecopy(n)
+ n = treecopy(n, 0)
makeaddable(n)
var f *Node
if t.Etype == TSTRUCT || Isfixedarray(t) {
diff --git a/src/cmd/internal/gc/range.go b/src/cmd/compile/internal/gc/range.go
similarity index 100%
rename from src/cmd/internal/gc/range.go
rename to src/cmd/compile/internal/gc/range.go
diff --git a/src/cmd/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go
similarity index 81%
rename from src/cmd/internal/gc/reflect.go
rename to src/cmd/compile/internal/gc/reflect.go
index 9979fe85fd..6c0962f258 100644
--- a/src/cmd/internal/gc/reflect.go
+++ b/src/cmd/compile/internal/gc/reflect.go
@@ -5,8 +5,10 @@
package gc
import (
+ "cmd/internal/gcprog"
"cmd/internal/obj"
"fmt"
+ "os"
)
/*
@@ -687,7 +689,7 @@ func haspointers(t *Type) bool {
// typeptrdata returns the length in bytes of the prefix of t
// containing pointer data. Anything after this offset is scalar data.
-func typeptrdata(t *Type) uint64 {
+func typeptrdata(t *Type) int64 {
if !haspointers(t) {
return 0
}
@@ -699,24 +701,24 @@ func typeptrdata(t *Type) uint64 {
TFUNC,
TCHAN,
TMAP:
- return uint64(Widthptr)
+ return int64(Widthptr)
case TSTRING:
// struct { byte *str; intgo len; }
- return uint64(Widthptr)
+ return int64(Widthptr)
case TINTER:
// struct { Itab *tab; void *data; } or
// struct { Type *type; void *data; }
- return 2 * uint64(Widthptr)
+ return 2 * int64(Widthptr)
case TARRAY:
if Isslice(t) {
// struct { byte *array; uintgo len; uintgo cap; }
- return uint64(Widthptr)
+ return int64(Widthptr)
}
// haspointers already eliminated t.Bound == 0.
- return uint64(t.Bound-1)*uint64(t.Type.Width) + typeptrdata(t.Type)
+ return (t.Bound-1)*t.Type.Width + typeptrdata(t.Type)
case TSTRUCT:
// Find the last field that has pointers.
@@ -726,7 +728,7 @@ func typeptrdata(t *Type) uint64 {
lastPtrField = t1
}
}
- return uint64(lastPtrField.Width) + typeptrdata(lastPtrField.Type)
+ return lastPtrField.Width + typeptrdata(lastPtrField.Type)
default:
Fatal("typeptrdata: unexpected type, %v", t)
@@ -771,6 +773,8 @@ func dcommontype(s *Sym, ot int, t *Type) int {
// The linker magically takes the max of all the sizes.
zero := Pkglookup("zerovalue", Runtimepkg)
+ gcsym, useGCProg, ptrdata := dgcsym(t)
+
// We use size 0 here so we get the pointer to the zero value,
// but don't allocate space for the zero value unless we need it.
// TODO: how do we get this symbol into bss? We really want
@@ -787,14 +791,14 @@ func dcommontype(s *Sym, ot int, t *Type) int {
// fieldAlign uint8
// kind uint8
// alg unsafe.Pointer
- // gc unsafe.Pointer
+ // gcdata unsafe.Pointer
// string *string
// *extraType
// ptrToThis *Type
// zero unsafe.Pointer
// }
ot = duintptr(s, ot, uint64(t.Width))
- ot = duintptr(s, ot, typeptrdata(t))
+ ot = duintptr(s, ot, uint64(ptrdata))
ot = duint32(s, ot, typehash(t))
ot = duint8(s, ot, 0) // unused
@@ -811,8 +815,6 @@ func dcommontype(s *Sym, ot int, t *Type) int {
ot = duint8(s, ot, t.Align) // align
ot = duint8(s, ot, t.Align) // fieldAlign
- gcprog := usegcprog(t)
-
i = kinds[t.Etype]
if t.Etype == TARRAY && t.Bound < 0 {
i = obj.KindSlice
@@ -823,7 +825,7 @@ func dcommontype(s *Sym, ot int, t *Type) int {
if isdirectiface(t) {
i |= obj.KindDirectIface
}
- if gcprog {
+ if useGCProg {
i |= obj.KindGCProg
}
ot = duint8(s, ot, uint8(i)) // kind
@@ -832,48 +834,7 @@ func dcommontype(s *Sym, ot int, t *Type) int {
} else {
ot = dsymptr(s, ot, algsym, 0)
}
-
- // gc
- if gcprog {
- var gcprog1 *Sym
- var gcprog0 *Sym
- gengcprog(t, &gcprog0, &gcprog1)
- if gcprog0 != nil {
- ot = dsymptr(s, ot, gcprog0, 0)
- } else {
- ot = duintptr(s, ot, 0)
- }
- ot = dsymptr(s, ot, gcprog1, 0)
- } else {
- var gcmask [16]uint8
- gengcmask(t, gcmask[:])
- x1 := uint64(0)
- for i := 0; i < 8; i++ {
- x1 = x1<<8 | uint64(gcmask[i])
- }
- var p string
- if Widthptr == 4 {
- p = fmt.Sprintf("gcbits.0x%016x", x1)
- } else {
- x2 := uint64(0)
- for i := 0; i < 8; i++ {
- x2 = x2<<8 | uint64(gcmask[i+8])
- }
- p = fmt.Sprintf("gcbits.0x%016x%016x", x1, x2)
- }
-
- sbits := Pkglookup(p, Runtimepkg)
- if sbits.Flags&SymUniq == 0 {
- sbits.Flags |= SymUniq
- for i := 0; i < 2*Widthptr; i++ {
- duint8(sbits, i, gcmask[i])
- }
- ggloblsym(sbits, 2*int32(Widthptr), obj.DUPOK|obj.RODATA|obj.LOCAL)
- }
-
- ot = dsymptr(s, ot, sbits, 0)
- ot = duintptr(s, ot, 0)
- }
+ ot = dsymptr(s, ot, gcsym, 0)
p := Tconv(t, obj.FmtLeft|obj.FmtUnsigned)
@@ -1419,269 +1380,193 @@ func dalgsym(t *Type) *Sym {
return s
}
-func usegcprog(t *Type) bool {
- if !haspointers(t) {
- return false
- }
- if t.Width == BADWIDTH {
- dowidth(t)
+// maxPtrmaskBytes is the maximum length of a GC ptrmask bitmap,
+// which holds 1-bit entries describing where pointers are in a given type.
+// 16 bytes is enough to describe 128 pointer-sized words, 512 or 1024 bytes
+// depending on the system. Above this length, the GC information is
+// recorded as a GC program, which can express repetition compactly.
+// In either form, the information is used by the runtime to initialize the
+// heap bitmap, and for large types (like 128 or more words), they are
+// roughly the same speed. GC programs are never much larger and often
+// more compact. (If large arrays are involved, they can be arbitrarily more
+// compact.)
+//
+// The cutoff must be large enough that any allocation large enough to
+// use a GC program is large enough that it does not share heap bitmap
+// bytes with any other objects, allowing the GC program execution to
+// assume an aligned start and not use atomic operations. In the current
+// runtime, this means all malloc size classes larger than the cutoff must
+// 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
+// is 32 pointers, the bits for which fit in 4 bytes. So maxPtrmaskBytes
+// must be >= 4.
+//
+// We use 16 because the GC programs do have some constant overhead
+// to get started, and processing 128 pointers seems to be enough to
+// amortize that overhead well.
+const maxPtrmaskBytes = 16
+
+// dgcsym emits and returns a data symbol containing GC information for type t,
+// along with a boolean reporting whether the UseGCProg bit should be set in
+// the type kind, and the ptrdata field to record in the reflect type information.
+func dgcsym(t *Type) (sym *Sym, useGCProg bool, ptrdata int64) {
+ ptrdata = typeptrdata(t)
+ if ptrdata/int64(Widthptr) <= maxPtrmaskBytes*8 {
+ sym = dgcptrmask(t)
+ return
}
- // Calculate size of the unrolled GC mask.
- nptr := (t.Width + int64(Widthptr) - 1) / int64(Widthptr)
-
- size := nptr
- if size%2 != 0 {
- size *= 2 // repeated
- }
- size = size * obj.GcBits / 8 // 4 bits per word
-
- // Decide whether to use unrolled GC mask or GC program.
- // We could use a more elaborate condition, but this seems to work well in practice.
- // For small objects GC program can't give significant reduction.
- // While large objects usually contain arrays; and even if it don't
- // the program uses 2-bits per word while mask uses 4-bits per word,
- // so the program is still smaller.
- return size > int64(2*Widthptr)
+ useGCProg = true
+ sym, ptrdata = dgcprog(t)
+ return
}
-// Generates sparse GC bitmask (4 bits per word).
-func gengcmask(t *Type, gcmask []byte) {
- for i := int64(0); i < 16; i++ {
- gcmask[i] = 0
+// dgcptrmask emits and returns the symbol containing a pointer mask for type t.
+func dgcptrmask(t *Type) *Sym {
+ ptrmask := make([]byte, (typeptrdata(t)/int64(Widthptr)+7)/8)
+ fillptrmask(t, ptrmask)
+ p := fmt.Sprintf("gcbits.%x", ptrmask)
+
+ sym := Pkglookup(p, Runtimepkg)
+ if sym.Flags&SymUniq == 0 {
+ sym.Flags |= SymUniq
+ for i, x := range ptrmask {
+ duint8(sym, i, x)
+ }
+ ggloblsym(sym, int32(len(ptrmask)), obj.DUPOK|obj.RODATA|obj.LOCAL)
+ }
+ return sym
+}
+
+// fillptrmask fills in ptrmask with 1s corresponding to the
+// word offsets in t that hold pointers.
+// ptrmask is assumed to fit at least typeptrdata(t)/Widthptr bits.
+func fillptrmask(t *Type, ptrmask []byte) {
+ for i := range ptrmask {
+ ptrmask[i] = 0
}
if !haspointers(t) {
return
}
- // Generate compact mask as stacks use.
+ vec := bvalloc(8 * int32(len(ptrmask)))
xoffset := int64(0)
-
- vec := bvalloc(2 * int32(Widthptr) * 8)
onebitwalktype1(t, &xoffset, vec)
- // Unfold the mask for the GC bitmap format:
- // 4 bits per word, 2 high bits encode pointer info.
- pos := gcmask
-
- nptr := (t.Width + int64(Widthptr) - 1) / int64(Widthptr)
- half := false
-
- // If number of words is odd, repeat the mask.
- // This makes simpler handling of arrays in runtime.
- var i int64
- var bits uint8
- for j := int64(0); j <= (nptr % 2); j++ {
- for i = 0; i < nptr; i++ {
- // convert 0=scalar / 1=pointer to GC bit encoding
- if bvget(vec, int32(i)) == 0 {
- bits = obj.BitsScalar
- } else {
- bits = obj.BitsPointer
- }
- bits <<= 2
- if half {
- bits <<= 4
- }
- pos[0] |= byte(bits)
- half = !half
- if !half {
- pos = pos[1:]
- }
+ nptr := typeptrdata(t) / int64(Widthptr)
+ for i := int64(0); i < nptr; i++ {
+ if bvget(vec, int32(i)) == 1 {
+ ptrmask[i/8] |= 1 << (uint(i) % 8)
}
}
}
-// Helper object for generation of GC programs.
-type ProgGen struct {
- s *Sym
- datasize int32
- data [256 / obj.PointersPerByte]uint8
- ot int64
+// dgcprog emits and returns the symbol containing a GC program for type t
+// along with the size of the data described by the program (in the range [typeptrdata(t), t.Width]).
+// In practice, the size is typeptrdata(t) except for non-trivial arrays.
+// For non-trivial arrays, the program describes the full t.Width size.
+func dgcprog(t *Type) (*Sym, int64) {
+ dowidth(t)
+ if t.Width == BADWIDTH {
+ Fatal("dgcprog: %v badwidth", t)
+ }
+ sym := typesymprefix(".gcprog", t)
+ var p GCProg
+ p.init(sym)
+ p.emit(t, 0)
+ offset := p.w.BitIndex() * int64(Widthptr)
+ p.end()
+ if ptrdata := typeptrdata(t); offset < ptrdata || offset > t.Width {
+ Fatal("dgcprog: %v: offset=%d but ptrdata=%d size=%d", t, offset, ptrdata, t.Width)
+ }
+ return sym, offset
}
-func proggeninit(g *ProgGen, s *Sym) {
- g.s = s
- g.datasize = 0
- g.ot = 0
- g.data = [256 / obj.PointersPerByte]uint8{}
+type GCProg struct {
+ sym *Sym
+ symoff int
+ w gcprog.Writer
}
-func proggenemit(g *ProgGen, v uint8) {
- g.ot = int64(duint8(g.s, int(g.ot), v))
+var Debug_gcprog int // set by -d gcprog
+
+func (p *GCProg) init(sym *Sym) {
+ p.sym = sym
+ p.symoff = 4 // first 4 bytes hold program length
+ p.w.Init(p.writeByte)
+ if Debug_gcprog > 0 {
+ fmt.Fprintf(os.Stderr, "compile: start GCProg for %v\n", sym)
+ p.w.Debug(os.Stderr)
+ }
}
-// Emits insData block from g->data.
-func proggendataflush(g *ProgGen) {
- if g.datasize == 0 {
+func (p *GCProg) writeByte(x byte) {
+ p.symoff = duint8(p.sym, p.symoff, x)
+}
+
+func (p *GCProg) end() {
+ p.w.End()
+ duint32(p.sym, 0, uint32(p.symoff-4))
+ ggloblsym(p.sym, int32(p.symoff), obj.DUPOK|obj.RODATA|obj.LOCAL)
+ if Debug_gcprog > 0 {
+ fmt.Fprintf(os.Stderr, "compile: end GCProg for %v\n", p.sym)
+ }
+}
+
+func (p *GCProg) emit(t *Type, offset int64) {
+ dowidth(t)
+ if !haspointers(t) {
return
}
- proggenemit(g, obj.InsData)
- proggenemit(g, uint8(g.datasize))
- s := (g.datasize + obj.PointersPerByte - 1) / obj.PointersPerByte
- for i := int32(0); i < s; i++ {
- proggenemit(g, g.data[i])
+ if t.Width == int64(Widthptr) {
+ p.w.Ptr(offset / int64(Widthptr))
+ return
}
- g.datasize = 0
- g.data = [256 / obj.PointersPerByte]uint8{}
-}
-
-func proggendata(g *ProgGen, d uint8) {
- g.data[g.datasize/obj.PointersPerByte] |= d << uint((g.datasize%obj.PointersPerByte)*obj.BitsPerPointer)
- g.datasize++
- if g.datasize == 255 {
- proggendataflush(g)
- }
-}
-
-// Skip v bytes due to alignment, etc.
-func proggenskip(g *ProgGen, off int64, v int64) {
- for i := off; i < off+v; i++ {
- if (i % int64(Widthptr)) == 0 {
- proggendata(g, obj.BitsScalar)
- }
- }
-}
-
-// Emit insArray instruction.
-func proggenarray(g *ProgGen, len int64) {
- proggendataflush(g)
- proggenemit(g, obj.InsArray)
- for i := int32(0); i < int32(Widthptr); i, len = i+1, len>>8 {
- proggenemit(g, uint8(len))
- }
-}
-
-func proggenarrayend(g *ProgGen) {
- proggendataflush(g)
- proggenemit(g, obj.InsArrayEnd)
-}
-
-func proggenfini(g *ProgGen) int64 {
- proggendataflush(g)
- proggenemit(g, obj.InsEnd)
- return g.ot
-}
-
-// Generates GC program for large types.
-func gengcprog(t *Type, pgc0 **Sym, pgc1 **Sym) {
- nptr := (t.Width + int64(Widthptr) - 1) / int64(Widthptr)
- size := nptr
- if size%2 != 0 {
- size *= 2 // repeated twice
- }
- size = size * obj.PointersPerByte / 8 // 4 bits per word
- size++ // unroll flag in the beginning, used by runtime (see runtime.markallocated)
-
- // emity space in BSS for unrolled program
- *pgc0 = nil
-
- // Don't generate it if it's too large, runtime will unroll directly into GC bitmap.
- if size <= obj.MaxGCMask {
- gc0 := typesymprefix(".gc", t)
- ggloblsym(gc0, int32(size), obj.DUPOK|obj.NOPTR)
- *pgc0 = gc0
- }
-
- // program in RODATA
- gc1 := typesymprefix(".gcprog", t)
-
- var g ProgGen
- proggeninit(&g, gc1)
- xoffset := int64(0)
- gengcprog1(&g, t, &xoffset)
- ot := proggenfini(&g)
- ggloblsym(gc1, int32(ot), obj.DUPOK|obj.RODATA)
- *pgc1 = gc1
-}
-
-// Recursively walks type t and writes GC program into g.
-func gengcprog1(g *ProgGen, t *Type, xoffset *int64) {
switch t.Etype {
- case TINT8,
- TUINT8,
- TINT16,
- TUINT16,
- TINT32,
- TUINT32,
- TINT64,
- TUINT64,
- TINT,
- TUINT,
- TUINTPTR,
- TBOOL,
- TFLOAT32,
- TFLOAT64,
- TCOMPLEX64,
- TCOMPLEX128:
- proggenskip(g, *xoffset, t.Width)
- *xoffset += t.Width
-
- case TPTR32,
- TPTR64,
- TUNSAFEPTR,
- TFUNC,
- TCHAN,
- TMAP:
- proggendata(g, obj.BitsPointer)
- *xoffset += t.Width
+ default:
+ Fatal("GCProg.emit: unexpected type %v", t)
case TSTRING:
- proggendata(g, obj.BitsPointer)
- proggendata(g, obj.BitsScalar)
- *xoffset += t.Width
+ p.w.Ptr(offset / int64(Widthptr))
- // Assuming IfacePointerOnly=1.
case TINTER:
- proggendata(g, obj.BitsPointer)
-
- proggendata(g, obj.BitsPointer)
- *xoffset += t.Width
+ p.w.Ptr(offset / int64(Widthptr))
+ p.w.Ptr(offset/int64(Widthptr) + 1)
case TARRAY:
if Isslice(t) {
- proggendata(g, obj.BitsPointer)
- proggendata(g, obj.BitsScalar)
- proggendata(g, obj.BitsScalar)
- } else {
- t1 := t.Type
- if t1.Width == 0 {
- }
- // ignore
- if t.Bound <= 1 || t.Bound*t1.Width < int64(32*Widthptr) {
- for i := int64(0); i < t.Bound; i++ {
- gengcprog1(g, t1, xoffset)
- }
- } else if !haspointers(t1) {
- n := t.Width
- n -= -*xoffset & (int64(Widthptr) - 1) // skip to next ptr boundary
- proggenarray(g, (n+int64(Widthptr)-1)/int64(Widthptr))
- proggendata(g, obj.BitsScalar)
- proggenarrayend(g)
- *xoffset -= (n+int64(Widthptr)-1)/int64(Widthptr)*int64(Widthptr) - t.Width
- } else {
- proggenarray(g, t.Bound)
- gengcprog1(g, t1, xoffset)
- *xoffset += (t.Bound - 1) * t1.Width
- proggenarrayend(g)
- }
+ p.w.Ptr(offset / int64(Widthptr))
+ return
}
+ if t.Bound == 0 {
+ // should have been handled by haspointers check above
+ Fatal("GCProg.emit: empty array")
+ }
+
+ // Flatten array-of-array-of-array to just a big array by multiplying counts.
+ count := t.Bound
+ elem := t.Type
+ for Isfixedarray(elem) {
+ count *= elem.Bound
+ elem = elem.Type
+ }
+
+ if !p.w.ShouldRepeat(elem.Width/int64(Widthptr), count) {
+ // Cheaper to just emit the bits.
+ for i := int64(0); i < count; i++ {
+ p.emit(elem, offset+i*elem.Width)
+ }
+ return
+ }
+ p.emit(elem, offset)
+ p.w.ZeroUntil((offset + elem.Width) / int64(Widthptr))
+ p.w.Repeat(elem.Width/int64(Widthptr), count-1)
case TSTRUCT:
- o := int64(0)
- var fieldoffset int64
for t1 := t.Type; t1 != nil; t1 = t1.Down {
- fieldoffset = t1.Width
- proggenskip(g, *xoffset, fieldoffset-o)
- *xoffset += fieldoffset - o
- gengcprog1(g, t1.Type, xoffset)
- o = fieldoffset + t1.Type.Width
+ p.emit(t1.Type, offset+t1.Width)
}
-
- proggenskip(g, *xoffset, t.Width-o)
- *xoffset += t.Width - o
-
- default:
- Fatal("gengcprog1: unexpected type, %v", t)
}
}
diff --git a/src/cmd/internal/gc/reg.go b/src/cmd/compile/internal/gc/reg.go
similarity index 100%
rename from src/cmd/internal/gc/reg.go
rename to src/cmd/compile/internal/gc/reg.go
diff --git a/src/cmd/internal/gc/select.go b/src/cmd/compile/internal/gc/select.go
similarity index 100%
rename from src/cmd/internal/gc/select.go
rename to src/cmd/compile/internal/gc/select.go
diff --git a/src/cmd/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go
similarity index 97%
rename from src/cmd/internal/gc/sinit.go
rename to src/cmd/compile/internal/gc/sinit.go
index a9af9450ae..b5427a338c 100644
--- a/src/cmd/internal/gc/sinit.go
+++ b/src/cmd/compile/internal/gc/sinit.go
@@ -221,7 +221,7 @@ func init2(n *Node, out **NodeList) {
init2list(n.Nelse, out)
if n.Op == OCLOSURE {
- init2list(n.Closure.Nbody, out)
+ init2list(n.Param.Closure.Nbody, out)
}
if n.Op == ODOTMETH || n.Op == OCALLPART {
init2(n.Type.Nname, out)
@@ -437,7 +437,7 @@ func staticassign(l *Node, r *Node, out **NodeList) bool {
case OSTRARRAYBYTE:
if l.Class == PEXTERN && r.Left.Op == OLITERAL {
- sval := r.Left.Val.U.Sval
+ sval := r.Left.Val.U.(string)
slicebytes(l, sval, len(sval))
return true
}
@@ -449,7 +449,7 @@ func staticassign(l *Node, r *Node, out **NodeList) bool {
ta := typ(TARRAY)
ta.Type = r.Type.Type
- ta.Bound = Mpgetfix(r.Right.Val.U.Xval)
+ ta.Bound = Mpgetfix(r.Right.Val.U.(*Mpint))
a := staticname(ta, 1)
r.Nname = a
n1 = *l
@@ -510,7 +510,7 @@ func staticname(t *Type, ctxt int) *Node {
n := newname(Lookupf("statictmp_%.4d", statuniqgen))
statuniqgen++
if ctxt == 0 {
- n.Readonly = true
+ n.Name.Readonly = true
}
addvar(n, t, PEXTERN)
return n
@@ -722,7 +722,7 @@ func slicelit(ctxt int, n *Node, var_ *Node, init **NodeList) {
// make an array type
t := shallow(n.Type)
- t.Bound = Mpgetfix(n.Right.Val.U.Xval)
+ t.Bound = Mpgetfix(n.Right.Val.U.(*Mpint))
t.Width = 0
t.Sym = nil
t.Haspointers = 0
@@ -1226,7 +1226,7 @@ func oaslit(n *Node, init **NodeList) bool {
func getlit(lit *Node) int {
if Smallintconst(lit) {
- return int(Mpgetfix(lit.Val.U.Xval))
+ return int(Mpgetfix(lit.Val.U.(*Mpint)))
}
return -1
}
@@ -1290,7 +1290,7 @@ func initplan(n *Node) {
if a.Op != OKEY || !Smallintconst(a.Left) {
Fatal("initplan arraylit")
}
- addvalue(p, n.Type.Type.Width*Mpgetfix(a.Left.Val.U.Xval), nil, a.Right)
+ addvalue(p, n.Type.Type.Width*Mpgetfix(a.Left.Val.U.(*Mpint)), nil, a.Right)
}
case OSTRUCTLIT:
@@ -1360,19 +1360,19 @@ func iszero(n *Node) bool {
return true
case CTSTR:
- return n.Val.U.Sval == ""
+ return n.Val.U.(string) == ""
case CTBOOL:
- return !n.Val.U.Bval
+ return !n.Val.U.(bool)
case CTINT, CTRUNE:
- return mpcmpfixc(n.Val.U.Xval, 0) == 0
+ return mpcmpfixc(n.Val.U.(*Mpint), 0) == 0
case CTFLT:
- return mpcmpfltc(n.Val.U.Fval, 0) == 0
+ return mpcmpfltc(n.Val.U.(*Mpflt), 0) == 0
case CTCPLX:
- return mpcmpfltc(&n.Val.U.Cval.Real, 0) == 0 && mpcmpfltc(&n.Val.U.Cval.Imag, 0) == 0
+ return mpcmpfltc(&n.Val.U.(*Mpcplx).Real, 0) == 0 && mpcmpfltc(&n.Val.U.(*Mpcplx).Imag, 0) == 0
}
case OARRAYLIT:
@@ -1510,10 +1510,10 @@ func gen_as_init(n *Node) bool {
gdata(&nam, nr, int(nr.Type.Width))
case TCOMPLEX64, TCOMPLEX128:
- gdatacomplex(&nam, nr.Val.U.Cval)
+ gdatacomplex(&nam, nr.Val.U.(*Mpcplx))
case TSTRING:
- gdatastring(&nam, nr.Val.U.Sval)
+ gdatastring(&nam, nr.Val.U.(string))
}
return true
diff --git a/src/cmd/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
similarity index 99%
rename from src/cmd/internal/gc/ssa.go
rename to src/cmd/compile/internal/gc/ssa.go
index bb4d278383..7f78fce17e 100644
--- a/src/cmd/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -7,9 +7,9 @@ package gc
import (
"log"
+ "cmd/compile/internal/ssa"
"cmd/internal/obj"
"cmd/internal/obj/x86" // TODO: remove
- "cmd/internal/ssa"
)
func buildssa(fn *Node) *ssa.Func {
@@ -267,7 +267,7 @@ func (s *state) expr(n *Node) *ssa.Value {
case OLITERAL:
switch n.Val.Ctype {
case CTINT:
- return s.f.ConstInt(n.Type, Mpgetfix(n.Val.U.Xval))
+ return s.f.ConstInt(n.Type, Mpgetfix(n.Val.U.(*Mpint)))
default:
log.Fatalf("unhandled OLITERAL %v", n.Val.Ctype)
return nil
diff --git a/src/cmd/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
similarity index 97%
rename from src/cmd/internal/gc/subr.go
rename to src/cmd/compile/internal/gc/subr.go
index 06ceff5844..ed5001a983 100644
--- a/src/cmd/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -125,13 +125,6 @@ func Yyerror(format string, args ...interface{}) {
if strings.HasPrefix(msg, "syntax error") {
nsyntaxerrors++
- yystate := theparser.(*yyParserImpl).state()
- yychar := theparser.Lookahead()
-
- if Debug['x'] != 0 {
- fmt.Printf("yyerror: yystate=%d yychar=%d\n", yystate, yychar)
- }
-
// An unexpected EOF caused a syntax error. Use the previous
// line number since getc generated a fake newline character.
if curio.eofnl != 0 {
@@ -144,14 +137,6 @@ func Yyerror(format string, args ...interface{}) {
}
yyerror_lastsyntax = int(lexlineno)
- // look for parse state-specific errors in list (see go.errors).
- for i := range yymsg {
- if yymsg[i].yystate == yystate && yymsg[i].yychar == yychar {
- yyerrorl(int(lexlineno), "syntax error: %s", yymsg[i].msg)
- return
- }
- }
-
// plain "syntax error" gets "near foo" added
if msg == "syntax error" {
yyerrorl(int(lexlineno), "syntax error near %s", lexbuf.String())
@@ -214,26 +199,32 @@ func Fatal(fmt_ string, args ...interface{}) {
errorexit()
}
-func linehist(file string, off int32, relative int) {
+func linehistpragma(file string) {
if Debug['i'] != 0 {
- if file != "" {
- if off < 0 {
- fmt.Printf("pragma %s", file)
- } else if off > 0 {
- fmt.Printf("line %s", file)
- } else {
- fmt.Printf("import %s", file)
- }
- } else {
- fmt.Printf("end of import")
- }
- fmt.Printf(" at line %v\n", Ctxt.Line(int(lexlineno)))
+ fmt.Printf("pragma %s at line %v\n", file, Ctxt.Line(int(lexlineno)))
}
+ Ctxt.AddImport(file)
+}
- if off < 0 && file[0] != '/' && relative == 0 {
- file = fmt.Sprintf("%s/%s", Ctxt.Pathname, file)
+func linehistpush(file string) {
+ if Debug['i'] != 0 {
+ fmt.Printf("import %s at line %v\n", file, Ctxt.Line(int(lexlineno)))
}
- obj.Linklinehist(Ctxt, int(lexlineno), file, int(off))
+ Ctxt.LineHist.Push(int(lexlineno), file)
+}
+
+func linehistpop() {
+ if Debug['i'] != 0 {
+ fmt.Printf("end of import at line %v\n", Ctxt.Line(int(lexlineno)))
+ }
+ Ctxt.LineHist.Pop(int(lexlineno))
+}
+
+func linehistupdate(file string, off int) {
+ if Debug['i'] != 0 {
+ fmt.Printf("line %s at line %v\n", file, Ctxt.Line(int(lexlineno)))
+ }
+ Ctxt.LineHist.Update(int(lexlineno), file, off)
}
func setlineno(n *Node) int32 {
@@ -380,6 +371,12 @@ func Nod(op int, nleft *Node, nright *Node) *Node {
switch op {
case OCLOSURE, ODCLFUNC:
n.Func = new(Func)
+ n.Param = new(Param)
+ case ONAME:
+ n.Name = new(Name)
+ n.Param = new(Param)
+ case ODCLFIELD:
+ n.Param = new(Param)
}
return n
}
@@ -666,8 +663,8 @@ func sortinter(t *Type) *Type {
func Nodintconst(v int64) *Node {
c := Nod(OLITERAL, nil, nil)
c.Addable = true
- c.Val.U.Xval = new(Mpint)
- Mpmovecfix(c.Val.U.Xval, v)
+ c.Val.U = new(Mpint)
+ Mpmovecfix(c.Val.U.(*Mpint), v)
c.Val.Ctype = CTINT
c.Type = Types[TIDEAL]
ullmancalc(c)
@@ -677,8 +674,8 @@ func Nodintconst(v int64) *Node {
func nodfltconst(v *Mpflt) *Node {
c := Nod(OLITERAL, nil, nil)
c.Addable = true
- c.Val.U.Fval = newMpflt()
- mpmovefltflt(c.Val.U.Fval, v)
+ c.Val.U = newMpflt()
+ mpmovefltflt(c.Val.U.(*Mpflt), v)
c.Val.Ctype = CTFLT
c.Type = Types[TIDEAL]
ullmancalc(c)
@@ -690,8 +687,8 @@ func Nodconst(n *Node, t *Type, v int64) {
n.Op = OLITERAL
n.Addable = true
ullmancalc(n)
- n.Val.U.Xval = new(Mpint)
- Mpmovecfix(n.Val.U.Xval, v)
+ n.Val.U = new(Mpint)
+ Mpmovecfix(n.Val.U.(*Mpint), v)
n.Val.Ctype = CTINT
n.Type = t
@@ -710,7 +707,7 @@ func nodnil() *Node {
func Nodbool(b bool) *Node {
c := Nodintconst(0)
c.Val.Ctype = CTBOOL
- c.Val.U.Bval = b
+ c.Val.U = b
c.Type = idealbool
return c
}
@@ -724,7 +721,7 @@ func aindex(b *Node, t *Type) *Type {
Yyerror("array bound must be an integer expression")
case CTINT, CTRUNE:
- bound = Mpgetfix(b.Val.U.Xval)
+ bound = Mpgetfix(b.Val.U.(*Mpint))
if bound < 0 {
Yyerror("array bound must be non negative")
}
@@ -739,7 +736,12 @@ func aindex(b *Node, t *Type) *Type {
return r
}
-func treecopy(n *Node) *Node {
+// treecopy recursively copies n, with the exception of
+// ONAME, OLITERAL, OTYPE, and non-iota ONONAME leaves.
+// Copies of iota ONONAME nodes are assigned the current
+// value of iota_. If lineno != 0, it sets the line number
+// of newly allocated nodes to lineno.
+func treecopy(n *Node, lineno int32) *Node {
if n == nil {
return nil
}
@@ -750,9 +752,12 @@ func treecopy(n *Node) *Node {
m = Nod(OXXX, nil, nil)
*m = *n
m.Orig = m
- m.Left = treecopy(n.Left)
- m.Right = treecopy(n.Right)
- m.List = listtreecopy(n.List)
+ m.Left = treecopy(n.Left, lineno)
+ m.Right = treecopy(n.Right, lineno)
+ m.List = listtreecopy(n.List, lineno)
+ if lineno != -1 {
+ m.Lineno = lineno
+ }
if m.Defn != nil {
panic("abort")
}
@@ -767,11 +772,13 @@ func treecopy(n *Node) *Node {
*m = *n
m.Iota = iota_
+ if lineno != 0 {
+ m.Lineno = lineno
+ }
break
}
fallthrough
- // fall through
case ONAME, OLITERAL, OTYPE:
m = n
}
@@ -1898,7 +1905,7 @@ func safeexpr(n *Node, init **NodeList) *Node {
case ONAME, OLITERAL:
return n
- case ODOT:
+ case ODOT, OLEN, OCAP:
l := safeexpr(n.Left, init)
if l == n.Left {
return n
@@ -2359,7 +2366,7 @@ func genwrapper(rcvr *Type, method *Type, newnam *Sym, iface int) {
lineno = lexlineno
if genwrapper_linehistdone == 0 {
// All the wrappers can share the same linehist entry.
- linehist("", 0, 0)
+ linehistpush("")
genwrapper_linehistdone = 1
}
@@ -2368,7 +2375,7 @@ func genwrapper(rcvr *Type, method *Type, newnam *Sym, iface int) {
markdcl()
this := Nod(ODCLFIELD, newname(Lookup(".this")), typenod(rcvr))
- this.Left.Ntype = this.Right
+ this.Left.Param.Ntype = this.Right
in := structargs(getinarg(method.Type), 1)
out := structargs(Getoutarg(method.Type), 0)
@@ -2394,7 +2401,7 @@ func genwrapper(rcvr *Type, method *Type, newnam *Sym, iface int) {
fn := Nod(ODCLFUNC, nil, nil)
fn.Nname = newname(newnam)
fn.Nname.Defn = fn
- fn.Nname.Ntype = t
+ fn.Nname.Param.Ntype = t
declare(fn.Nname, PFUNC)
funchdr(fn)
@@ -2422,11 +2429,11 @@ func genwrapper(rcvr *Type, method *Type, newnam *Sym, iface int) {
var v Val
v.Ctype = CTSTR
- v.U.Sval = rcvr.Type.Sym.Pkg.Name // package name
+ v.U = rcvr.Type.Sym.Pkg.Name // package name
l = list(l, nodlit(v))
- v.U.Sval = rcvr.Type.Sym.Name // type name
+ v.U = rcvr.Type.Sym.Name // type name
l = list(l, nodlit(v))
- v.U.Sval = method.Sym.Name
+ v.U = method.Sym.Name
l = list(l, nodlit(v)) // method name
call := Nod(OCALL, syslook("panicwrap", 0), nil)
call.List = l
@@ -2568,7 +2575,7 @@ func genhash(sym *Sym, t *Type) {
fn.Nname = newname(sym)
fn.Nname.Class = PFUNC
tfn := Nod(OTFUNC, nil, nil)
- fn.Nname.Ntype = tfn
+ fn.Nname.Param.Ntype = tfn
n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
tfn.List = list(tfn.List, n)
@@ -2580,7 +2587,7 @@ func genhash(sym *Sym, t *Type) {
tfn.Rlist = list(tfn.Rlist, n)
funchdr(fn)
- typecheck(&fn.Nname.Ntype, Etype)
+ typecheck(&fn.Nname.Param.Ntype, Etype)
// genhash is only called for types that have equality but
// cannot be handled by the standard algorithms,
@@ -2820,7 +2827,7 @@ func geneq(sym *Sym, t *Type) {
fn.Nname = newname(sym)
fn.Nname.Class = PFUNC
tfn := Nod(OTFUNC, nil, nil)
- fn.Nname.Ntype = tfn
+ fn.Nname.Param.Ntype = tfn
n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
tfn.List = list(tfn.List, n)
@@ -3096,10 +3103,10 @@ func Simsimtype(t *Type) int {
return et
}
-func listtreecopy(l *NodeList) *NodeList {
+func listtreecopy(l *NodeList, lineno int32) *NodeList {
var out *NodeList
for ; l != nil; l = l.Next {
- out = list(out, treecopy(l.N))
+ out = list(out, treecopy(l.N, lineno))
}
return out
}
@@ -3139,7 +3146,7 @@ func powtwo(n *Node) int {
return -1
}
- v := uint64(Mpgetfix(n.Val.U.Xval))
+ v := uint64(Mpgetfix(n.Val.U.(*Mpint)))
b := uint64(1)
for i := 0; i < 64; i++ {
if b == v {
diff --git a/src/cmd/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go
similarity index 98%
rename from src/cmd/internal/gc/swt.go
rename to src/cmd/compile/internal/gc/swt.go
index 7cb632cebe..221b1f43eb 100644
--- a/src/cmd/internal/gc/swt.go
+++ b/src/cmd/compile/internal/gc/swt.go
@@ -169,10 +169,10 @@ func typecheckswitch(n *Node) {
if nvar != nil {
if ll != nil && ll.Next == nil && ll.N.Type != nil && !Istype(ll.N.Type, TNIL) {
// single entry type switch
- nvar.Ntype = typenod(ll.N.Type)
+ nvar.Param.Ntype = typenod(ll.N.Type)
} else {
// multiple entry type switch or default
- nvar.Ntype = typenod(n.Type)
+ nvar.Param.Ntype = typenod(n.Type)
}
typecheck(&nvar, Erv|Easgn)
@@ -218,7 +218,7 @@ func (s *exprSwitch) walk(sw *Node) {
s.kind = switchKindExpr
if Isconst(sw.Ntest, CTBOOL) {
s.kind = switchKindTrue
- if !sw.Ntest.Val.U.Bval {
+ if !sw.Ntest.Val.U.(bool) {
s.kind = switchKindFalse
}
}
@@ -755,16 +755,16 @@ func exprcmp(c1, c2 *caseClause) int {
// sort by constant value to enable binary search
switch ct {
case CTFLT:
- return mpcmpfltflt(n1.Val.U.Fval, n2.Val.U.Fval)
+ return mpcmpfltflt(n1.Val.U.(*Mpflt), n2.Val.U.(*Mpflt))
case CTINT, CTRUNE:
- return Mpcmpfixfix(n1.Val.U.Xval, n2.Val.U.Xval)
+ return Mpcmpfixfix(n1.Val.U.(*Mpint), n2.Val.U.(*Mpint))
case CTSTR:
// Sort strings by length and then by value.
// It is much cheaper to compare lengths than values,
// and all we need here is consistency.
// We respect this sorting in exprSwitch.walkCases.
- a := n1.Val.U.Sval
- b := n2.Val.U.Sval
+ a := n1.Val.U.(string)
+ b := n2.Val.U.(string)
if len(a) < len(b) {
return -1
}
diff --git a/src/cmd/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
similarity index 87%
rename from src/cmd/internal/gc/syntax.go
rename to src/cmd/compile/internal/gc/syntax.go
index 7c9fb8d2b8..be4307690d 100644
--- a/src/cmd/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -23,14 +23,67 @@ type Node struct {
List *NodeList
Rlist *NodeList
+ // most nodes
+ Type *Type
+ Orig *Node // original form, for printing, and tracking copies of ONAMEs
+ Nname *Node
+
+ // func
+ Func *Func
+
+ // ONAME
+ Name *Name
+ Defn *Node // ONAME: initializing assignment; OLABEL: labeled statement
+ Pack *Node // real package for import . names
+ Curfn *Node // function for local variables
+ Paramfld *Type // TFIELD for this PPARAM; also for ODOT, curfn
+ Alloc *Node // allocation call
+ Param *Param
+
+ // OPACK
+ Pkg *Pkg
+
+ // OARRAYLIT, OMAPLIT, OSTRUCTLIT.
+ Initplan *InitPlan
+
+ // Escape analysis.
+ Escflowsrc *NodeList // flow(this, src)
+ Escretval *NodeList // on OCALLxxx, list of dummy return values
+
+ Sym *Sym // various
+
+ Opt interface{} // for optimization passes
+
+ // OLITERAL
+ Val Val
+
+ Xoffset int64
+ Stkdelta int64 // offset added by stack frame compaction phase.
+
+ // Escape analysis.
+ Escloopdepth int32 // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
+
+ Vargen int32 // unique name for OTYPE/ONAME within a function. Function outputs are numbered starting at one.
+ Lineno int32
+ Iota int32
+ Walkgen uint32
+
+ Funcdepth int32
+
+ // OREGISTER, OINDREG
+ Reg int16
+
+ // most nodes - smaller fields
+ Esclevel Level
+ Esc uint16 // EscXXX
+
Op uint8
Nointerface bool
Ullman uint8 // sethi/ullman number
Addable bool // addressable
- Etype uint8 // op for OASOP, etype for OTYPE, exclam for export
+ Etype uint8 // op for OASOP, etype for OTYPE, exclam for export, 6g saved reg
Bounded bool // bounds check unnecessary
Class uint8 // PPARAM, PAUTO, PEXTERN, etc
- Method bool // OCALLMETH is direct method call
Embedded uint8 // ODCLFIELD embedded type
Colas bool // OAS resulting from :=
Diag uint8 // already printed error about this
@@ -42,76 +95,36 @@ type Node struct {
Initorder uint8
Used bool
Isddd bool // is the argument variadic
- Readonly bool
Implicit bool
- Addrtaken bool // address taken, even if not moved to heap
- Assigned bool // is the variable ever assigned to
- Captured bool // is the variable captured by a closure
- Byval bool // is the variable captured by value or by reference
- Reslice bool // this is a reslice x = x[0:y] or x = append(x, ...)
- Likely int8 // likeliness of if statement
- Hasbreak bool // has break statement
- Needzero bool // if it contains pointers, needs to be zeroed on function entry
- Esc uint16 // EscXXX
- Funcdepth int32
+ Addrtaken bool // address taken, even if not moved to heap
+ Assigned bool // is the variable ever assigned to
+ Likely int8 // likeliness of if statement
+ Hasbreak bool // has break statement
+}
- // most nodes
- Type *Type
- Orig *Node // original form, for printing, and tracking copies of ONAMEs
- Nname *Node
+// Name holds Node fields used only by ONAME nodes.
+type Name struct {
+ Heapaddr *Node // temp holding heap address of param
+ Inlvar *Node // ONAME substitute while inlining
+ Decldepth int32 // declaration loop depth, increased for every loop or label
+ Method bool // OCALLMETH name
+ Readonly bool
+ Captured bool // is the variable captured by a closure
+ Byval bool // is the variable captured by value or by reference
+ Needzero bool // if it contains pointers, needs to be zeroed on function entry
+}
- // func
- Func *Func
-
- // OLITERAL
- Val Val
-
- // OREGISTER, OINDREG
- Reg int16
-
- // ONAME
- Ntype *Node
- Defn *Node // ONAME: initializing assignment; OLABEL: labeled statement
- Pack *Node // real package for import . names
- Curfn *Node // function for local variables
- Paramfld *Type // TFIELD for this PPARAM; also for ODOT, curfn
- Decldepth int // declaration loop depth, increased for every loop or label
+type Param struct {
+ Ntype *Node
// ONAME func param with PHEAP
- Heapaddr *Node // temp holding heap address of param
Outerexpr *Node // expression copied into closure for variable
Stackparam *Node // OPARAM node referring to stack copy of param
- Alloc *Node // allocation call
// ONAME closure param with PPARAMREF
Outer *Node // outer PPARAMREF in nested closure
Closure *Node // ONAME/PHEAP <-> ONAME/PPARAMREF
Top int // top context (Ecall, Eproc, etc)
-
- // ONAME substitute while inlining
- Inlvar *Node
-
- // OPACK
- Pkg *Pkg
-
- // OARRAYLIT, OMAPLIT, OSTRUCTLIT.
- Initplan *InitPlan
-
- // Escape analysis.
- Escflowsrc *NodeList // flow(this, src)
- Escretval *NodeList // on OCALLxxx, list of dummy return values
- Escloopdepth int // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
-
- Sym *Sym // various
- Vargen int32 // unique name for OTYPE/ONAME within a function. Function outputs are numbered starting at one.
- Lineno int32
- Xoffset int64
- Stkdelta int64 // offset added by stack frame compaction phase.
- Ostk int32 // 6g only
- Iota int32
- Walkgen uint32
- Esclevel Level
- Opt interface{} // for optimization passes
}
// Func holds Node fields used only with function-like nodes.
diff --git a/src/cmd/internal/gc/type.go b/src/cmd/compile/internal/gc/type.go
similarity index 97%
rename from src/cmd/internal/gc/type.go
rename to src/cmd/compile/internal/gc/type.go
index 6f7830d70a..cf1589eb03 100644
--- a/src/cmd/internal/gc/type.go
+++ b/src/cmd/compile/internal/gc/type.go
@@ -10,7 +10,7 @@
package gc
import (
- "cmd/internal/ssa"
+ "cmd/compile/internal/ssa"
)
func (t *Type) Size() int64 {
diff --git a/src/cmd/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
similarity index 95%
rename from src/cmd/internal/gc/typecheck.go
rename to src/cmd/compile/internal/gc/typecheck.go
index 6daf842474..2900da8be7 100644
--- a/src/cmd/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -311,8 +311,8 @@ OpSwitch:
break OpSwitch
case ONAME:
- if n.Decldepth == 0 {
- n.Decldepth = decldepth
+ if n.Name.Decldepth == 0 {
+ n.Name.Decldepth = decldepth
}
if n.Etype != 0 {
ok |= Ecall
@@ -392,7 +392,7 @@ OpSwitch:
return
}
- t.Bound = Mpgetfix(v.U.Xval)
+ t.Bound = Mpgetfix(v.U.(*Mpint))
if doesoverflow(v, Types[TINT]) {
Yyerror("array bound is too large")
n.Type = nil
@@ -770,7 +770,7 @@ OpSwitch:
}
if (op == ODIV || op == OMOD) && Isconst(r, CTINT) {
- if mpcmpfixc(r.Val.U.Xval, 0) == 0 {
+ if mpcmpfixc(r.Val.U.(*Mpint), 0) == 0 {
Yyerror("division by zero")
n.Type = nil
return
@@ -813,8 +813,8 @@ OpSwitch:
var l *Node
for l = n.Left; l != r; l = l.Left {
l.Addrtaken = true
- if l.Closure != nil {
- l.Closure.Addrtaken = true
+ if l.Param != nil && l.Param.Closure != nil {
+ l.Param.Closure.Addrtaken = true
}
}
@@ -822,8 +822,8 @@ OpSwitch:
Fatal("found non-orig name node %v", l)
}
l.Addrtaken = true
- if l.Closure != nil {
- l.Closure.Addrtaken = true
+ if l.Param != nil && l.Param.Closure != nil {
+ l.Param.Closure.Addrtaken = true
}
defaultlit(&n.Left, nil)
l = n.Left
@@ -891,6 +891,9 @@ OpSwitch:
}
n.Op = ONAME
+ if n.Name == nil {
+ n.Name = new(Name)
+ }
n.Sym = n.Right.Sym
n.Type = methodfunc(n.Type, n.Left.Type)
n.Xoffset = 0
@@ -1024,7 +1027,7 @@ OpSwitch:
case TSTRING, TARRAY:
indexlit(&n.Right)
if t.Etype == TSTRING {
- n.Type = Types[TUINT8]
+ n.Type = bytetype
} else {
n.Type = t.Type
}
@@ -1043,14 +1046,14 @@ OpSwitch:
}
if Isconst(n.Right, CTINT) {
- x := Mpgetfix(n.Right.Val.U.Xval)
+ x := Mpgetfix(n.Right.Val.U.(*Mpint))
if x < 0 {
Yyerror("invalid %s index %v (index must be non-negative)", why, n.Right)
} else if Isfixedarray(t) && t.Bound > 0 && x >= t.Bound {
Yyerror("invalid array index %v (out of bounds for %d-element array)", n.Right, t.Bound)
- } else if Isconst(n.Left, CTSTR) && x >= int64(len(n.Left.Val.U.Sval)) {
- Yyerror("invalid string index %v (out of bounds for %d-byte string)", n.Right, len(n.Left.Val.U.Sval))
- } else if Mpcmpfixfix(n.Right.Val.U.Xval, Maxintval[TINT]) > 0 {
+ } else if Isconst(n.Left, CTSTR) && x >= int64(len(n.Left.Val.U.(string))) {
+ Yyerror("invalid string index %v (out of bounds for %d-byte string)", n.Right, len(n.Left.Val.U.(string)))
+ } else if Mpcmpfixfix(n.Right.Val.U.(*Mpint), Maxintval[TINT]) > 0 {
Yyerror("invalid %s index %v (index too large)", why, n.Right)
}
}
@@ -1303,7 +1306,7 @@ OpSwitch:
if l.Op == OTYPE {
if n.Isddd || l.Type.Bound == -100 {
if l.Type.Broke == 0 {
- Yyerror("invalid use of ... in type conversion", l)
+ Yyerror("invalid use of ... in type conversion to %v", l.Type)
}
n.Diag = 1
}
@@ -1435,9 +1438,9 @@ OpSwitch:
if Isconst(l, CTCPLX) {
r := n
if n.Op == OREAL {
- n = nodfltconst(&l.Val.U.Cval.Real)
+ n = nodfltconst(&l.Val.U.(*Mpcplx).Real)
} else {
- n = nodfltconst(&l.Val.U.Cval.Imag)
+ n = nodfltconst(&l.Val.U.(*Mpcplx).Imag)
}
n.Orig = r
}
@@ -1451,7 +1454,7 @@ OpSwitch:
case TSTRING:
if Isconst(l, CTSTR) {
r := Nod(OXXX, nil, nil)
- Nodconst(r, Types[TINT], int64(len(l.Val.U.Sval)))
+ Nodconst(r, Types[TINT], int64(len(l.Val.U.(string))))
r.Orig = n
n = r
}
@@ -1528,7 +1531,7 @@ OpSwitch:
var t *Type
switch l.Type.Etype {
default:
- Yyerror("invalid operation: %v (arguments have type %v, expected floating-point)", n, l.Type, r.Type)
+ Yyerror("invalid operation: %v (arguments have type %v, expected floating-point)", n, l.Type)
n.Type = nil
return
@@ -1636,17 +1639,16 @@ OpSwitch:
}
// Unpack multiple-return result before type-checking.
+ var funarg *Type
if Istype(t, TSTRUCT) && t.Funarg != 0 {
- t = t.Type
- if Istype(t, TFIELD) {
- t = t.Type
- }
+ funarg = t
+ t = t.Type.Type
}
n.Type = t
if !Isslice(t) {
if Isconst(args.N, CTNIL) {
- Yyerror("first argument to append must be typed slice; have untyped nil", t)
+ Yyerror("first argument to append must be typed slice; have untyped nil")
n.Type = nil
return
}
@@ -1678,11 +1680,19 @@ OpSwitch:
break OpSwitch
}
- for args = args.Next; args != nil; args = args.Next {
- if args.N.Type == nil {
- continue
+ if funarg != nil {
+ for t := funarg.Type.Down; t != nil; t = t.Down {
+ if assignop(t.Type, n.Type.Type, nil) == 0 {
+ Yyerror("cannot append %v value to []%v", t.Type, n.Type.Type)
+ }
+ }
+ } else {
+ for args = args.Next; args != nil; args = args.Next {
+ if args.N.Type == nil {
+ continue
+ }
+ args.N = assignconv(args.N, t.Type, "append")
}
- args.N = assignconv(args.N, t.Type, "append")
}
break OpSwitch
@@ -1852,7 +1862,7 @@ OpSwitch:
n.Type = nil
return
}
- if Isconst(l, CTINT) && r != nil && Isconst(r, CTINT) && Mpcmpfixfix(l.Val.U.Xval, r.Val.U.Xval) > 0 {
+ if Isconst(l, CTINT) && r != nil && Isconst(r, CTINT) && Mpcmpfixfix(l.Val.U.(*Mpint), r.Val.U.(*Mpint)) > 0 {
Yyerror("len larger than cap in make(%v)", t)
n.Type = nil
return
@@ -2248,16 +2258,16 @@ func checksliceindex(l *Node, r *Node, tp *Type) int {
}
if r.Op == OLITERAL {
- if Mpgetfix(r.Val.U.Xval) < 0 {
+ if Mpgetfix(r.Val.U.(*Mpint)) < 0 {
Yyerror("invalid slice index %v (index must be non-negative)", r)
return -1
- } else if tp != nil && tp.Bound > 0 && Mpgetfix(r.Val.U.Xval) > tp.Bound {
+ } else if tp != nil && tp.Bound > 0 && Mpgetfix(r.Val.U.(*Mpint)) > tp.Bound {
Yyerror("invalid slice index %v (out of bounds for %d-element array)", r, tp.Bound)
return -1
- } else if Isconst(l, CTSTR) && Mpgetfix(r.Val.U.Xval) > int64(len(l.Val.U.Sval)) {
- Yyerror("invalid slice index %v (out of bounds for %d-byte string)", r, len(l.Val.U.Sval))
+ } else if Isconst(l, CTSTR) && Mpgetfix(r.Val.U.(*Mpint)) > int64(len(l.Val.U.(string))) {
+ Yyerror("invalid slice index %v (out of bounds for %d-byte string)", r, len(l.Val.U.(string)))
return -1
- } else if Mpcmpfixfix(r.Val.U.Xval, Maxintval[TINT]) > 0 {
+ } else if Mpcmpfixfix(r.Val.U.(*Mpint), Maxintval[TINT]) > 0 {
Yyerror("invalid slice index %v (index too large)", r)
return -1
}
@@ -2267,7 +2277,7 @@ func checksliceindex(l *Node, r *Node, tp *Type) int {
}
func checksliceconst(lo *Node, hi *Node) int {
- if lo != nil && hi != nil && lo.Op == OLITERAL && hi.Op == OLITERAL && Mpcmpfixfix(lo.Val.U.Xval, hi.Val.U.Xval) > 0 {
+ if lo != nil && hi != nil && lo.Op == OLITERAL && hi.Op == OLITERAL && Mpcmpfixfix(lo.Val.U.(*Mpint), hi.Val.U.(*Mpint)) > 0 {
Yyerror("invalid slice index: %v > %v", lo, hi)
return -1
}
@@ -2817,10 +2827,10 @@ func keydup(n *Node, hash []*Node) {
b = 23
case CTINT, CTRUNE:
- b = uint32(Mpgetfix(n.Val.U.Xval))
+ b = uint32(Mpgetfix(n.Val.U.(*Mpint)))
case CTFLT:
- d := mpgetflt(n.Val.U.Fval)
+ d := mpgetflt(n.Val.U.(*Mpflt))
x := math.Float64bits(d)
for i := 0; i < 8; i++ {
b = b*PRIME1 + uint32(x&0xFF)
@@ -2829,8 +2839,8 @@ func keydup(n *Node, hash []*Node) {
case CTSTR:
b = 0
- s := n.Val.U.Sval
- for i := len(n.Val.U.Sval); i > 0; i-- {
+ s := n.Val.U.(string)
+ for i := len(n.Val.U.(string)); i > 0; i-- {
b = b*PRIME1 + uint32(s[0])
s = s[1:]
}
@@ -2846,12 +2856,12 @@ func keydup(n *Node, hash []*Node) {
if Eqtype(a.Left.Type, n.Type) {
cmp.Right = a.Left
evconst(&cmp)
- b = uint32(obj.Bool2int(cmp.Val.U.Bval))
+ b = uint32(obj.Bool2int(cmp.Val.U.(bool)))
}
} else if Eqtype(a.Type, n.Type) {
cmp.Right = a
evconst(&cmp)
- b = uint32(obj.Bool2int(cmp.Val.U.Bval))
+ b = uint32(obj.Bool2int(cmp.Val.U.(bool)))
}
if b != 0 {
@@ -2869,11 +2879,11 @@ func indexdup(n *Node, hash []*Node) {
Fatal("indexdup: not OLITERAL")
}
- b := uint32(Mpgetfix(n.Val.U.Xval))
+ b := uint32(Mpgetfix(n.Val.U.(*Mpint)))
h := uint(b % uint32(len(hash)))
var c uint32
for a := hash[h]; a != nil; a = a.Ntest {
- c = uint32(Mpgetfix(a.Val.U.Xval))
+ c = uint32(Mpgetfix(a.Val.U.(*Mpint)))
if b == c {
Yyerror("duplicate index in array literal: %d", b)
return
@@ -3263,14 +3273,14 @@ func checkassign(stmt *Node, n *Node) {
var l *Node
for l = n; l != r; l = l.Left {
l.Assigned = true
- if l.Closure != nil {
- l.Closure.Assigned = true
+ if l.Param != nil && l.Param.Closure != nil {
+ l.Param.Closure.Assigned = true
}
}
l.Assigned = true
- if l.Closure != nil {
- l.Closure.Assigned = true
+ if l.Param != nil && l.Param.Closure != nil {
+ l.Param.Closure.Assigned = true
}
}
@@ -3335,7 +3345,7 @@ func typecheckas(n *Node) {
// so that the conversion below happens).
n.Left = resolve(n.Left)
- if n.Left.Defn != n || n.Left.Ntype != nil {
+ if n.Left.Defn != n || n.Left.Param.Ntype != nil {
typecheck(&n.Left, Erv|Easgn)
}
@@ -3347,7 +3357,7 @@ func typecheckas(n *Node) {
}
}
- if n.Left.Defn == n && n.Left.Ntype == nil {
+ if n.Left.Defn == n && n.Left.Param.Ntype == nil {
defaultlit(&n.Right, nil)
n.Left.Type = n.Right.Type
}
@@ -3360,29 +3370,6 @@ func typecheckas(n *Node) {
if n.Left.Typecheck == 0 {
typecheck(&n.Left, Erv|Easgn)
}
-
- // Recognize slices being updated in place, for better code generation later.
- // Don't rewrite if using race detector, to avoid needing to teach race detector
- // about this optimization.
- if n.Left != nil && n.Left.Op != OINDEXMAP && n.Right != nil && flag_race == 0 {
- switch n.Right.Op {
- // For x = x[0:y], x can be updated in place, without touching pointer.
- // TODO(rsc): Reenable once it is actually updated in place without touching the pointer.
- case OSLICE, OSLICE3, OSLICESTR:
- if false && samesafeexpr(n.Left, n.Right.Left) && (n.Right.Right.Left == nil || iszero(n.Right.Right.Left)) {
- n.Right.Reslice = true
- }
-
- // For x = append(x, ...), x can be updated in place when there is capacity,
- // without touching the pointer; otherwise the emitted code to growslice
- // can take care of updating the pointer, and only in that case.
- // TODO(rsc): Reenable once the emitted code does update the pointer.
- case OAPPEND:
- if false && n.Right.List != nil && samesafeexpr(n.Left, n.Right.List.N) {
- n.Right.Reslice = true
- }
- }
- }
}
func checkassignto(src *Type, dst *Node) {
@@ -3399,7 +3386,7 @@ func typecheckas2(n *Node) {
// delicate little dance.
ll.N = resolve(ll.N)
- if ll.N.Defn != n || ll.N.Ntype != nil {
+ if ll.N.Defn != n || ll.N.Param.Ntype != nil {
typecheck(&ll.N, Erv|Easgn)
}
}
@@ -3423,7 +3410,7 @@ func typecheckas2(n *Node) {
if ll.N.Type != nil && lr.N.Type != nil {
lr.N = assignconv(lr.N, ll.N.Type, "assignment")
}
- if ll.N.Defn == n && ll.N.Ntype == nil {
+ if ll.N.Defn == n && ll.N.Param.Ntype == nil {
defaultlit(&lr.N, nil)
ll.N.Type = lr.N.Type
}
@@ -3456,7 +3443,7 @@ func typecheckas2(n *Node) {
if t.Type != nil && ll.N.Type != nil {
checkassignto(t.Type, ll.N)
}
- if ll.N.Defn == n && ll.N.Ntype == nil {
+ if ll.N.Defn == n && ll.N.Param.Ntype == nil {
ll.N.Type = t.Type
}
t = structnext(&s)
@@ -3495,7 +3482,7 @@ func typecheckas2(n *Node) {
if l.Type != nil && l.Type.Etype != TBOOL {
checkassignto(Types[TBOOL], l)
}
- if l.Defn == n && l.Ntype == nil {
+ if l.Defn == n && l.Param.Ntype == nil {
l.Type = Types[TBOOL]
}
goto out
@@ -3534,7 +3521,7 @@ func typecheckfunc(n *Node) {
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.Decldepth = 1
+ l.N.Name.Decldepth = 1
}
}
}
@@ -3542,10 +3529,10 @@ func typecheckfunc(n *Node) {
func stringtoarraylit(np **Node) {
n := *np
if n.Left.Op != OLITERAL || n.Left.Val.Ctype != CTSTR {
- Fatal("stringtoarraylit %N", n)
+ Fatal("stringtoarraylit %v", n)
}
- s := n.Left.Val.U.Sval
+ s := n.Left.Val.U.(string)
var l *NodeList
if n.Type.Type.Etype == TUINT8 {
// []byte
@@ -3659,8 +3646,8 @@ func typecheckdeftype(n *Node) {
setlineno(n)
n.Type.Sym = n.Sym
n.Typecheck = 1
- typecheck(&n.Ntype, Etype)
- t := n.Ntype.Type
+ typecheck(&n.Param.Ntype, Etype)
+ t := n.Param.Ntype.Type
if t == nil {
n.Diag = 1
n.Type = nil
@@ -3770,10 +3757,10 @@ func typecheckdef(n *Node) *Node {
break
case OLITERAL:
- if n.Ntype != nil {
- typecheck(&n.Ntype, Etype)
- n.Type = n.Ntype.Type
- n.Ntype = nil
+ if n.Param.Ntype != nil {
+ typecheck(&n.Param.Ntype, Etype)
+ n.Type = n.Param.Ntype.Type
+ n.Param.Ntype = nil
if n.Type == nil {
n.Diag = 1
goto ret
@@ -3822,9 +3809,9 @@ func typecheckdef(n *Node) *Node {
n.Type = e.Type
case ONAME:
- if n.Ntype != nil {
- typecheck(&n.Ntype, Etype)
- n.Type = n.Ntype.Type
+ if n.Param.Ntype != nil {
+ typecheck(&n.Param.Ntype, Etype)
+ n.Type = n.Param.Ntype.Type
if n.Type == nil {
n.Diag = 1
@@ -3902,12 +3889,12 @@ func checkmake(t *Type, arg string, n *Node) int {
switch n.Val.Ctype {
case CTINT, CTRUNE, CTFLT, CTCPLX:
n.Val = toint(n.Val)
- if mpcmpfixc(n.Val.U.Xval, 0) < 0 {
+ if mpcmpfixc(n.Val.U.(*Mpint), 0) < 0 {
Yyerror("negative %s argument in make(%v)", arg, t)
return -1
}
- if Mpcmpfixfix(n.Val.U.Xval, Maxintval[TINT]) > 0 {
+ if Mpcmpfixfix(n.Val.U.(*Mpint), Maxintval[TINT]) > 0 {
Yyerror("%s argument too large in make(%v)", arg, t)
return -1
}
diff --git a/src/cmd/internal/gc/unsafe.go b/src/cmd/compile/internal/gc/unsafe.go
similarity index 98%
rename from src/cmd/internal/gc/unsafe.go
rename to src/cmd/compile/internal/gc/unsafe.go
index aa90a19308..824ecd0339 100644
--- a/src/cmd/internal/gc/unsafe.go
+++ b/src/cmd/compile/internal/gc/unsafe.go
@@ -140,8 +140,8 @@ ret:
var val Val
val.Ctype = CTINT
- val.U.Xval = new(Mpint)
- Mpmovecfix(val.U.Xval, v)
+ val.U = new(Mpint)
+ Mpmovecfix(val.U.(*Mpint), v)
n := Nod(OLITERAL, nil, nil)
n.Orig = nn
n.Val = val
diff --git a/src/cmd/internal/gc/util.go b/src/cmd/compile/internal/gc/util.go
similarity index 95%
rename from src/cmd/internal/gc/util.go
rename to src/cmd/compile/internal/gc/util.go
index 5dc6561b48..c59af0665b 100644
--- a/src/cmd/internal/gc/util.go
+++ b/src/cmd/compile/internal/gc/util.go
@@ -1,7 +1,6 @@
package gc
import (
- "cmd/internal/obj"
"os"
"runtime"
"runtime/pprof"
@@ -10,7 +9,7 @@ import (
)
func (n *Node) Line() string {
- return obj.Linklinefmt(Ctxt, int(n.Lineno), false, false)
+ return Ctxt.LineHist.LineString(int(n.Lineno))
}
func atoi(s string) int {
diff --git a/src/cmd/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
similarity index 89%
rename from src/cmd/internal/gc/walk.go
rename to src/cmd/compile/internal/gc/walk.go
index c32a8137d6..d5eb44c0bb 100644
--- a/src/cmd/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -352,6 +352,20 @@ func walkstmt(np **Node) {
*np = n
}
+func isSmallMakeSlice(n *Node) bool {
+ if n.Op != OMAKESLICE {
+ return false
+ }
+ l := n.Left
+ r := n.Right
+ if r == nil {
+ r = l
+ }
+ t := n.Type
+
+ return Smallintconst(l) && Smallintconst(r) && (t.Type.Width == 0 || Mpgetfix(r.Val.U.(*Mpint)) < (1<<16)/t.Type.Width)
+}
+
/*
* walk the whole tree of the body of an
* expression or simple statement.
@@ -601,7 +615,7 @@ func walkexpr(np **Node, init **NodeList) {
n.Left.Func.Enter = nil
// Replace OCLOSURE with ONAME/PFUNC.
- n.Left = n.Left.Closure.Nname
+ n.Left = n.Left.Param.Closure.Nname
// Update type of OCALLFUNC node.
// Output arguments had not changed, but their offsets could.
@@ -711,6 +725,23 @@ func walkexpr(np **Node, init **NodeList) {
n = mkcall1(chanfn("chanrecv1", 2, r.Type), nil, init, typename(r.Type), r, n1)
walkexpr(&n, init)
goto ret
+
+ case OAPPEND:
+ // x = append(...)
+ r := n.Right
+ if r.Isddd {
+ r = appendslice(r, init) // also works for append(slice, string).
+ } else {
+ r = walkappend(r, init, n)
+ }
+ n.Right = r
+ if r.Op == OAPPEND {
+ // Left in place for back end.
+ // Do not add a new write barrier.
+ goto ret
+ }
+ // Otherwise, lowered for race detector.
+ // Treat as ordinary assignment.
}
if n.Left != nil && n.Right != nil {
@@ -1189,7 +1220,7 @@ func walkexpr(np **Node, init **NodeList) {
Yyerror("index out of bounds")
}
} else if Isconst(n.Left, CTSTR) {
- n.Bounded = bounded(r, int64(len(n.Left.Val.U.Sval)))
+ n.Bounded = bounded(r, int64(len(n.Left.Val.U.(string))))
if Debug['m'] != 0 && n.Bounded && !Isconst(n.Right, CTINT) {
Warn("index bounds check elided")
}
@@ -1200,16 +1231,16 @@ func walkexpr(np **Node, init **NodeList) {
// replace "abc"[1] with 'b'.
// delayed until now because "abc"[1] is not
// an ideal constant.
- v := Mpgetfix(n.Right.Val.U.Xval)
+ v := Mpgetfix(n.Right.Val.U.(*Mpint))
- Nodconst(n, n.Type, int64(n.Left.Val.U.Sval[v]))
+ Nodconst(n, n.Type, int64(n.Left.Val.U.(string)[v]))
n.Typecheck = 1
}
}
}
if Isconst(n.Right, CTINT) {
- if Mpcmpfixfix(n.Right.Val.U.Xval, &mpzero) < 0 || Mpcmpfixfix(n.Right.Val.U.Xval, Maxintval[TINT]) > 0 {
+ if Mpcmpfixfix(n.Right.Val.U.(*Mpint), &mpzero) < 0 || Mpcmpfixfix(n.Right.Val.U.(*Mpint), Maxintval[TINT]) > 0 {
Yyerror("index out of bounds")
}
}
@@ -1263,56 +1294,39 @@ func walkexpr(np **Node, init **NodeList) {
case ORECV:
Fatal("walkexpr ORECV") // should see inside OAS only
- case OSLICE:
- if n.Right != nil && n.Right.Left == nil && n.Right.Right == nil { // noop
- walkexpr(&n.Left, init)
- n = n.Left
- goto ret
- }
- fallthrough
-
- case OSLICEARR, OSLICESTR:
- if n.Right == nil { // already processed
- goto ret
- }
-
+ case OSLICE, OSLICEARR, OSLICESTR:
walkexpr(&n.Left, init)
-
- // cgen_slice can't handle string literals as source
- // TODO the OINDEX case is a bug elsewhere that needs to be traced. it causes a crash on ([2][]int{ ... })[1][lo:hi]
- if (n.Op == OSLICESTR && n.Left.Op == OLITERAL) || (n.Left.Op == OINDEX) {
- n.Left = copyexpr(n.Left, n.Left.Type, init)
- } else {
- n.Left = safeexpr(n.Left, init)
- }
walkexpr(&n.Right.Left, init)
- n.Right.Left = safeexpr(n.Right.Left, init)
+ if n.Right.Left != nil && iszero(n.Right.Left) {
+ // Reduce x[0:j] to x[:j].
+ n.Right.Left = nil
+ }
walkexpr(&n.Right.Right, init)
- n.Right.Right = safeexpr(n.Right.Right, init)
- n = sliceany(n, init) // chops n.Right, sets n.List
+ n = reduceSlice(n)
goto ret
case OSLICE3, OSLICE3ARR:
- if n.Right == nil { // already processed
+ walkexpr(&n.Left, init)
+ walkexpr(&n.Right.Left, init)
+ if n.Right.Left != nil && iszero(n.Right.Left) {
+ // Reduce x[0:j:k] to x[:j:k].
+ n.Right.Left = nil
+ }
+ walkexpr(&n.Right.Right.Left, init)
+ walkexpr(&n.Right.Right.Right, init)
+
+ r := n.Right.Right.Right
+ if r != nil && r.Op == OCAP && samesafeexpr(n.Left, r.Left) {
+ // Reduce x[i:j:cap(x)] to x[i:j].
+ n.Right.Right = n.Right.Right.Left
+ if n.Op == OSLICE3 {
+ n.Op = OSLICE
+ } else {
+ n.Op = OSLICEARR
+ }
+ n = reduceSlice(n)
goto ret
}
-
- walkexpr(&n.Left, init)
-
- // TODO the OINDEX case is a bug elsewhere that needs to be traced. it causes a crash on ([2][]int{ ... })[1][lo:hi]
- // TODO the comment on the previous line was copied from case OSLICE. it might not even be true.
- if n.Left.Op == OINDEX {
- n.Left = copyexpr(n.Left, n.Left.Type, init)
- } else {
- n.Left = safeexpr(n.Left, init)
- }
- walkexpr(&n.Right.Left, init)
- n.Right.Left = safeexpr(n.Right.Left, init)
- walkexpr(&n.Right.Right.Left, init)
- n.Right.Right.Left = safeexpr(n.Right.Right.Left, init)
- walkexpr(&n.Right.Right.Right, init)
- n.Right.Right.Right = safeexpr(n.Right.Right.Right, init)
- n = sliceany(n, init) // chops n.Right, sets n.List
goto ret
case OADDR:
@@ -1320,7 +1334,10 @@ func walkexpr(np **Node, init **NodeList) {
goto ret
case ONEW:
- if n.Esc == EscNone && n.Type.Type.Width < 1<<16 {
+ if n.Esc == EscNone {
+ if n.Type.Type.Width >= 1<<16 {
+ Fatal("Large ONEW with EscNone, %v", n)
+ }
r := temp(n.Type.Type)
r = Nod(OAS, r, nil) // zero temp
typecheck(&r, Etop)
@@ -1338,7 +1355,7 @@ func walkexpr(np **Node, init **NodeList) {
// comparing the lengths instead will yield the same result
// without the function call.
case OCMPSTR:
- if (Isconst(n.Left, CTSTR) && len(n.Left.Val.U.Sval) == 0) || (Isconst(n.Right, CTSTR) && len(n.Right.Val.U.Sval) == 0) {
+ if (Isconst(n.Left, CTSTR) && len(n.Left.Val.U.(string)) == 0) || (Isconst(n.Right, CTSTR) && len(n.Right.Val.U.(string)) == 0) {
r := Nod(int(n.Etype), Nod(OLEN, n.Left, nil), Nod(OLEN, n.Right, nil))
typecheck(&r, Erv)
walkexpr(&r, init)
@@ -1400,12 +1417,8 @@ func walkexpr(np **Node, init **NodeList) {
goto ret
case OAPPEND:
- if n.Isddd {
- n = appendslice(n, init) // also works for append(slice, string).
- } else {
- n = walkappend(n, init)
- }
- goto ret
+ // order should make sure we only see OAS(node, OAPPEND), which we handle above.
+ Fatal("append outside assignment")
case OCOPY:
n = copyany(n, init, flag_race)
@@ -1462,7 +1475,10 @@ func walkexpr(np **Node, init **NodeList) {
l = r
}
t := n.Type
- if n.Esc == EscNone && Smallintconst(l) && Smallintconst(r) && (t.Type.Width == 0 || Mpgetfix(r.Val.U.Xval) < (1<<16)/t.Type.Width) {
+ if n.Esc == EscNone {
+ if !isSmallMakeSlice(n) {
+ Fatal("Non-small OMAKESLICE with EscNone, %v", n)
+ }
// var arr [r]T
// n = arr[:l]
t = aindex(r, t.Type) // [r]T
@@ -1647,6 +1663,22 @@ ret:
*np = n
}
+func reduceSlice(n *Node) *Node {
+ r := n.Right.Right
+ if r != nil && r.Op == OLEN && samesafeexpr(n.Left, r.Left) {
+ // Reduce x[i:len(x)] to x[i:].
+ n.Right.Right = nil
+ }
+ if (n.Op == OSLICE || n.Op == OSLICESTR) && n.Right.Left == nil && n.Right.Right == nil {
+ // Reduce x[:] to x.
+ if Debug_slice > 0 {
+ Warn("slice: omit slice operation")
+ }
+ return n.Left
+ }
+ return n
+}
+
func ascompatee1(op int, l *Node, r *Node, init **NodeList) *Node {
// convas will turn map assigns into function calls,
// making it impossible for reorder3 to work.
@@ -2108,9 +2140,8 @@ func isstack(n *Node) bool {
}
switch n.Op {
- // OINDREG only ends up in walk if it's indirect of SP.
case OINDREG:
- return true
+ return n.Reg == int16(Thearch.REGSP)
case ONAME:
switch n.Class {
@@ -2185,26 +2216,6 @@ func needwritebarrier(l *Node, r *Node) bool {
return false
}
- // No write barrier for reslice: x = x[0:y] or x = append(x, ...).
- // Both are compiled to modify x directly.
- // In the case of append, a write barrier may still be needed
- // if the underlying array grows, but the append code can
- // generate the write barrier directly in that case.
- // (It does not yet, but the cost of the write barrier will be
- // small compared to the cost of the allocation.)
- if r.Reslice {
- switch r.Op {
- case OSLICE, OSLICE3, OSLICESTR, OAPPEND:
- break
-
- default:
- Dump("bad reslice-l", l)
- Dump("bad reslice-r", r)
- }
-
- return false
- }
-
// Otherwise, be conservative and use write barrier.
return true
}
@@ -2215,65 +2226,11 @@ var applywritebarrier_bv Bvec
func applywritebarrier(n *Node, init **NodeList) *Node {
if n.Left != nil && n.Right != nil && needwritebarrier(n.Left, n.Right) {
- if Curfn != nil && Curfn.Func.Nowritebarrier {
- Yyerror("write barrier prohibited")
- }
- if flag_race == 0 {
- if Debug_wb > 1 {
- Warnl(int(n.Lineno), "marking %v for barrier", Nconv(n.Left, 0))
- }
- n.Op = OASWB
- return n
- }
- // Use slow path always for race detector.
- if Debug_wb > 0 {
- Warnl(int(n.Lineno), "write barrier")
- }
- t := n.Left.Type
- l := Nod(OADDR, n.Left, nil)
- l.Etype = 1 // addr does not escape
- if t.Width == int64(Widthptr) {
- n = mkcall1(writebarrierfn("writebarrierptr", t, n.Right.Type), nil, init, l, n.Right)
- } else if t.Etype == TSTRING {
- n = mkcall1(writebarrierfn("writebarrierstring", t, n.Right.Type), nil, init, l, n.Right)
- } else if Isslice(t) {
- n = mkcall1(writebarrierfn("writebarrierslice", t, n.Right.Type), nil, init, l, n.Right)
- } else if Isinter(t) {
- n = mkcall1(writebarrierfn("writebarrieriface", t, n.Right.Type), nil, init, l, n.Right)
- } else if t.Width <= int64(4*Widthptr) {
- x := int64(0)
- if applywritebarrier_bv.b == nil {
- applywritebarrier_bv = bvalloc(4)
- }
- bvresetall(applywritebarrier_bv)
- onebitwalktype1(t, &x, applywritebarrier_bv)
- var name string
- switch t.Width / int64(Widthptr) {
- default:
- Fatal("found writebarrierfat for %d-byte object of type %v", int(t.Width), t)
-
- case 2:
- name = fmt.Sprintf("writebarrierfat%d%d", bvget(applywritebarrier_bv, 0), bvget(applywritebarrier_bv, 1))
-
- case 3:
- name = fmt.Sprintf("writebarrierfat%d%d%d", bvget(applywritebarrier_bv, 0), bvget(applywritebarrier_bv, 1), bvget(applywritebarrier_bv, 2))
-
- case 4:
- name = fmt.Sprintf("writebarrierfat%d%d%d%d", bvget(applywritebarrier_bv, 0), bvget(applywritebarrier_bv, 1), bvget(applywritebarrier_bv, 2), bvget(applywritebarrier_bv, 3))
- }
-
- n = mkcall1(writebarrierfn(name, t, n.Right.Type), nil, init, l, Nodintconst(0), n.Right)
- } else {
- r := n.Right
- for r.Op == OCONVNOP {
- r = r.Left
- }
- r = Nod(OADDR, r, nil)
- r.Etype = 1 // addr does not escape
-
- //warnl(n->lineno, "typedmemmove %T %N", t, r);
- n = mkcall1(writebarrierfn("typedmemmove", t, r.Left.Type), nil, init, typename(t), l, r)
+ if Debug_wb > 1 {
+ Warnl(int(n.Lineno), "marking %v for barrier", Nconv(n.Left, 0))
}
+ n.Op = OASWB
+ return n
}
return n
}
@@ -2728,10 +2685,10 @@ func paramstoheap(argin **Type, out int) *NodeList {
if v.Alloc == nil {
v.Alloc = callnew(v.Type)
}
- nn = list(nn, Nod(OAS, v.Heapaddr, v.Alloc))
+ nn = list(nn, Nod(OAS, v.Name.Heapaddr, v.Alloc))
if v.Class&^PHEAP != PPARAMOUT {
- as = Nod(OAS, v, v.Stackparam)
- v.Stackparam.Typecheck = 1
+ as = Nod(OAS, v, v.Param.Stackparam)
+ v.Param.Stackparam.Typecheck = 1
typecheck(&as, Etop)
as = applywritebarrier(as, &nn)
nn = list(nn, as)
@@ -2754,7 +2711,7 @@ func returnsfromheap(argin **Type) *NodeList {
if v == nil || v.Class != PHEAP|PPARAMOUT {
continue
}
- nn = list(nn, Nod(OAS, v.Stackparam, v))
+ nn = list(nn, Nod(OAS, v.Param.Stackparam, v))
}
return nn
@@ -2871,7 +2828,7 @@ func addstr(n *Node, init **NodeList) *Node {
sz := int64(0)
for l := n.List; l != nil; l = l.Next {
if n.Op == OLITERAL {
- sz += int64(len(n.Val.U.Sval))
+ sz += int64(len(n.Val.U.(string)))
}
}
@@ -3026,7 +2983,13 @@ func appendslice(n *Node, init **NodeList) *Node {
return s
}
-// expand append(src, a [, b]* ) to
+// Rewrite append(src, x, y, z) so that any side effects in
+// x, y, z (including runtime panics) are evaluated in
+// initialization statements before the append.
+// For normal code generation, stop there and leave the
+// rest to cgen_append.
+//
+// For race detector, expand append(src, a [, b]* ) to
//
// init {
// s := src
@@ -3041,13 +3004,21 @@ func appendslice(n *Node, init **NodeList) *Node {
// ...
// }
// s
-func walkappend(n *Node, init **NodeList) *Node {
- walkexprlistsafe(n.List, init)
+func walkappend(n *Node, init **NodeList, dst *Node) *Node {
+ if !samesafeexpr(dst, n.List.N) {
+ l := n.List
+ l.N = safeexpr(l.N, init)
+ walkexpr(&l.N, init)
+ }
+ walkexprlistsafe(n.List.Next, init)
// walkexprlistsafe will leave OINDEX (s[n]) alone if both s
// and n are name or literal, but those may index the slice we're
// modifying here. Fix explicitly.
- for l := n.List; l != nil; l = l.Next {
+ // Using cheapexpr also makes sure that the evaluation
+ // of all arguments (and especially any panics) happen
+ // before we begin to modify the slice in a visible way.
+ for l := n.List.Next; l != nil; l = l.Next {
l.N = cheapexpr(l.N, init)
}
@@ -3062,6 +3033,12 @@ func walkappend(n *Node, init **NodeList) *Node {
return nsrc
}
+ // General case, with no function calls left as arguments.
+ // Leave for gen, except that race detector requires old form
+ if flag_race == 0 {
+ return n
+ }
+
var l *NodeList
ns := temp(nsrc.Type)
@@ -3166,213 +3143,6 @@ func copyany(n *Node, init **NodeList, runtimecall int) *Node {
return nlen
}
-// Generate frontend part for OSLICE[3][ARR|STR]
-//
-func sliceany(n *Node, init **NodeList) *Node {
- var hb *Node
- var cb *Node
-
- // print("before sliceany: %+N\n", n);
-
- src := n.Left
-
- lb := n.Right.Left
- slice3 := n.Op == OSLICE3 || n.Op == OSLICE3ARR
- if slice3 {
- hb = n.Right.Right.Left
- cb = n.Right.Right.Right
- } else {
- hb = n.Right.Right
- cb = nil
- }
-
- bounded := int(n.Etype)
-
- var bound *Node
- if n.Op == OSLICESTR {
- bound = Nod(OLEN, src, nil)
- } else {
- bound = Nod(OCAP, src, nil)
- }
-
- typecheck(&bound, Erv)
- walkexpr(&bound, init) // if src is an array, bound will be a const now.
-
- // static checks if possible
- bv := int64(1 << 50)
-
- if Isconst(bound, CTINT) {
- if !Smallintconst(bound) {
- Yyerror("array len too large")
- } else {
- bv = Mpgetfix(bound.Val.U.Xval)
- }
- }
-
- if Isconst(cb, CTINT) {
- cbv := Mpgetfix(cb.Val.U.Xval)
- if cbv < 0 || cbv > bv {
- Yyerror("slice index out of bounds")
- }
- }
-
- if Isconst(hb, CTINT) {
- hbv := Mpgetfix(hb.Val.U.Xval)
- if hbv < 0 || hbv > bv {
- Yyerror("slice index out of bounds")
- }
- }
-
- if Isconst(lb, CTINT) {
- lbv := Mpgetfix(lb.Val.U.Xval)
- if lbv < 0 || lbv > bv {
- Yyerror("slice index out of bounds")
- lbv = -1
- }
-
- if lbv == 0 {
- lb = nil
- }
- }
-
- // Checking src[lb:hb:cb] or src[lb:hb].
- // if chk0 || chk1 || chk2 { panicslice() }
-
- // All comparisons are unsigned to avoid testing < 0.
- bt := Types[Simtype[TUINT]]
-
- if cb != nil && cb.Type.Width > 4 {
- bt = Types[TUINT64]
- }
- if hb != nil && hb.Type.Width > 4 {
- bt = Types[TUINT64]
- }
- if lb != nil && lb.Type.Width > 4 {
- bt = Types[TUINT64]
- }
-
- bound = cheapexpr(conv(bound, bt), init)
-
- var chk0 *Node // cap(src) < cb
- if cb != nil {
- cb = cheapexpr(conv(cb, bt), init)
- if bounded == 0 {
- chk0 = Nod(OLT, bound, cb)
- }
- } else if slice3 {
- // When we figure out what this means, implement it.
- Fatal("slice3 with cb == N") // rejected by parser
- }
-
- var chk1 *Node // cb < hb for src[lb:hb:cb]; cap(src) < hb for src[lb:hb]
- if hb != nil {
- hb = cheapexpr(conv(hb, bt), init)
- if bounded == 0 {
- if cb != nil {
- chk1 = Nod(OLT, cb, hb)
- } else {
- chk1 = Nod(OLT, bound, hb)
- }
- }
- } else if slice3 {
- // When we figure out what this means, implement it.
- Fatal("slice3 with hb == N") // rejected by parser
- } else if n.Op == OSLICEARR {
- hb = bound
- } else {
- hb = Nod(OLEN, src, nil)
- typecheck(&hb, Erv)
- walkexpr(&hb, init)
- hb = cheapexpr(conv(hb, bt), init)
- }
-
- var chk2 *Node // hb < lb
- if lb != nil {
- lb = cheapexpr(conv(lb, bt), init)
- if bounded == 0 {
- chk2 = Nod(OLT, hb, lb)
- }
- }
-
- if chk0 != nil || chk1 != nil || chk2 != nil {
- chk := Nod(OIF, nil, nil)
- chk.Nbody = list1(mkcall("panicslice", nil, init))
- chk.Likely = -1
- if chk0 != nil {
- chk.Ntest = chk0
- }
- if chk1 != nil {
- if chk.Ntest == nil {
- chk.Ntest = chk1
- } else {
- chk.Ntest = Nod(OOROR, chk.Ntest, chk1)
- }
- }
-
- if chk2 != nil {
- if chk.Ntest == nil {
- chk.Ntest = chk2
- } else {
- chk.Ntest = Nod(OOROR, chk.Ntest, chk2)
- }
- }
-
- typecheck(&chk, Etop)
- walkstmt(&chk)
- *init = concat(*init, chk.Ninit)
- chk.Ninit = nil
- *init = list(*init, chk)
- }
-
- // prepare new cap, len and offs for backend cgen_slice
- // cap = bound [ - lo ]
- n.Right = nil
-
- n.List = nil
- if !slice3 {
- cb = bound
- }
- if lb == nil {
- bound = conv(cb, Types[Simtype[TUINT]])
- } else {
- bound = Nod(OSUB, conv(cb, Types[Simtype[TUINT]]), conv(lb, Types[Simtype[TUINT]]))
- }
- typecheck(&bound, Erv)
- walkexpr(&bound, init)
- n.List = list(n.List, bound)
-
- // len = hi [ - lo]
- if lb == nil {
- hb = conv(hb, Types[Simtype[TUINT]])
- } else {
- hb = Nod(OSUB, conv(hb, Types[Simtype[TUINT]]), conv(lb, Types[Simtype[TUINT]]))
- }
- typecheck(&hb, Erv)
- walkexpr(&hb, init)
- n.List = list(n.List, hb)
-
- // offs = [width *] lo, but omit if zero
- if lb != nil {
- var w int64
- if n.Op == OSLICESTR {
- w = 1
- } else {
- w = n.Type.Type.Width
- }
- lb = conv(lb, Types[TUINTPTR])
- if w > 1 {
- lb = Nod(OMUL, Nodintconst(w), lb)
- }
- typecheck(&lb, Erv)
- walkexpr(&lb, init)
- n.List = list(n.List, lb)
- }
-
- // print("after sliceany: %+N\n", n);
-
- return n
-}
-
func eqfor(t *Type, needsize *int) *Node {
// Should only arrive here with large memory or
// a struct/array containing a non-memory field/element.
@@ -3611,7 +3381,7 @@ func samecheap(a *Node, b *Node) bool {
case OINDEX:
ar = a.Right
br = b.Right
- if !Isconst(ar, CTINT) || !Isconst(br, CTINT) || Mpcmpfixfix(ar.Val.U.Xval, br.Val.U.Xval) != 0 {
+ if !Isconst(ar, CTINT) || !Isconst(br, CTINT) || Mpcmpfixfix(ar.Val.U.(*Mpint), br.Val.U.(*Mpint)) != 0 {
return false
}
}
@@ -3647,9 +3417,9 @@ func walkrotate(np **Node) {
w := int(l.Type.Width * 8)
if Smallintconst(l.Right) && Smallintconst(r.Right) {
- sl := int(Mpgetfix(l.Right.Val.U.Xval))
+ sl := int(Mpgetfix(l.Right.Val.U.(*Mpint)))
if sl >= 0 {
- sr := int(Mpgetfix(r.Right.Val.U.Xval))
+ sr := int(Mpgetfix(r.Right.Val.U.(*Mpint)))
if sr >= 0 && sl+sr == w {
// Rewrite left shift half to left rotate.
if l.Op == OLSH {
@@ -3660,7 +3430,7 @@ func walkrotate(np **Node) {
n.Op = OLROT
// Remove rotate 0 and rotate w.
- s := int(Mpgetfix(n.Right.Val.U.Xval))
+ s := int(Mpgetfix(n.Right.Val.U.(*Mpint)))
if s == 0 || s == w {
n = n.Left
@@ -3703,7 +3473,7 @@ func walkmul(np **Node, init **NodeList) {
// x*0 is 0 (and side effects of x).
var pow int
var w int
- if Mpgetfix(nr.Val.U.Xval) == 0 {
+ if Mpgetfix(nr.Val.U.(*Mpint)) == 0 {
cheapexpr(nl, init)
Nodconst(n, n.Type, 0)
goto ret
@@ -3796,10 +3566,10 @@ func walkdiv(np **Node, init **NodeList) {
m.W = w
if Issigned[nl.Type.Etype] {
- m.Sd = Mpgetfix(nr.Val.U.Xval)
+ m.Sd = Mpgetfix(nr.Val.U.(*Mpint))
Smagic(&m)
} else {
- m.Ud = uint64(Mpgetfix(nr.Val.U.Xval))
+ m.Ud = uint64(Mpgetfix(nr.Val.U.(*Mpint)))
Umagic(&m)
}
@@ -3993,7 +3763,7 @@ func walkdiv(np **Node, init **NodeList) {
// n = nl & (nr-1)
n.Op = OAND
- Nodconst(nc, nl.Type, Mpgetfix(nr.Val.U.Xval)-1)
+ Nodconst(nc, nl.Type, Mpgetfix(nr.Val.U.(*Mpint))-1)
} else {
// n = nl >> pow
n.Op = ORSH
@@ -4023,7 +3793,7 @@ func bounded(n *Node, max int64) bool {
bits := int32(8 * n.Type.Width)
if Smallintconst(n) {
- v := Mpgetfix(n.Val.U.Xval)
+ v := Mpgetfix(n.Val.U.(*Mpint))
return 0 <= v && v < max
}
@@ -4031,9 +3801,9 @@ func bounded(n *Node, max int64) bool {
case OAND:
v := int64(-1)
if Smallintconst(n.Left) {
- v = Mpgetfix(n.Left.Val.U.Xval)
+ v = Mpgetfix(n.Left.Val.U.(*Mpint))
} else if Smallintconst(n.Right) {
- v = Mpgetfix(n.Right.Val.U.Xval)
+ v = Mpgetfix(n.Right.Val.U.(*Mpint))
}
if 0 <= v && v < max {
@@ -4042,7 +3812,7 @@ func bounded(n *Node, max int64) bool {
case OMOD:
if !sign && Smallintconst(n.Right) {
- v := Mpgetfix(n.Right.Val.U.Xval)
+ v := Mpgetfix(n.Right.Val.U.(*Mpint))
if 0 <= v && v <= max {
return true
}
@@ -4050,7 +3820,7 @@ func bounded(n *Node, max int64) bool {
case ODIV:
if !sign && Smallintconst(n.Right) {
- v := Mpgetfix(n.Right.Val.U.Xval)
+ v := Mpgetfix(n.Right.Val.U.(*Mpint))
for bits > 0 && v >= 2 {
bits--
v >>= 1
@@ -4059,7 +3829,7 @@ func bounded(n *Node, max int64) bool {
case ORSH:
if !sign && Smallintconst(n.Right) {
- v := Mpgetfix(n.Right.Val.U.Xval)
+ v := Mpgetfix(n.Right.Val.U.(*Mpint))
if v > int64(bits) {
return true
}
@@ -4192,17 +3962,17 @@ func candiscard(n *Node) bool {
// Discardable as long as we know it's not division by zero.
case ODIV, OMOD:
- if Isconst(n.Right, CTINT) && mpcmpfixc(n.Right.Val.U.Xval, 0) != 0 {
+ if Isconst(n.Right, CTINT) && mpcmpfixc(n.Right.Val.U.(*Mpint), 0) != 0 {
break
}
- if Isconst(n.Right, CTFLT) && mpcmpfltc(n.Right.Val.U.Fval, 0) != 0 {
+ if Isconst(n.Right, CTFLT) && mpcmpfltc(n.Right.Val.U.(*Mpflt), 0) != 0 {
break
}
return false
// Discardable as long as we know it won't fail because of a bad size.
case OMAKECHAN, OMAKEMAP:
- if Isconst(n.Left, CTINT) && mpcmpfixc(n.Left.Val.U.Xval, 0) == 0 {
+ if Isconst(n.Left, CTINT) && mpcmpfixc(n.Left.Val.U.(*Mpint), 0) == 0 {
break
}
return false
@@ -4256,7 +4026,7 @@ func walkprintfunc(np **Node, init **NodeList) {
buf = fmt.Sprintf("print·%d", walkprintfunc_prgen)
fn.Nname = newname(Lookup(buf))
fn.Nname.Defn = fn
- fn.Nname.Ntype = t
+ fn.Nname.Param.Ntype = t
declare(fn.Nname, PFUNC)
oldfn := Curfn
diff --git a/src/cmd/internal/gc/y.go b/src/cmd/compile/internal/gc/y.go
similarity index 92%
rename from src/cmd/internal/gc/y.go
rename to src/cmd/compile/internal/gc/y.go
index f2c8b96982..56b9d04ecb 100644
--- a/src/cmd/internal/gc/y.go
+++ b/src/cmd/compile/internal/gc/y.go
@@ -5,10 +5,11 @@ import __yyfmt__ "fmt"
//line go.y:21
import (
+ "fmt"
"strings"
)
-//line go.y:27
+//line go.y:28
type yySymType struct {
yys int
node *Node
@@ -153,7 +154,7 @@ const yyEofCode = 1
const yyErrCode = 2
const yyMaxDepth = 200
-//line go.y:2242
+//line go.y:2304
func fixlbrace(lbr int) {
// If the opening brace was an LBODY,
// set up for another one now that we're done.
@@ -856,6 +857,34 @@ var yyTok3 = [...]int{
0,
}
+var yyErrorMessages = [...]struct {
+ state int
+ token int
+ msg string
+}{
+ {332, 76, "unexpected comma during import block"},
+ {89, 63, "missing import path; require quoted string"},
+ {390, 63, "missing { after if clause"},
+ {387, 63, "missing { after switch clause"},
+ {279, 63, "missing { after for clause"},
+ {498, 36, "missing { after for clause"},
+ {17, 68, "unexpected semicolon or newline before {"},
+ {111, 63, "unexpected semicolon or newline in type declaration"},
+ {78, 69, "unexpected } in channel type"},
+ {78, 61, "unexpected ) in channel type"},
+ {78, 76, "unexpected comma in channel type"},
+ {416, 15, "unexpected semicolon or newline before else"},
+ {329, 76, "name list not allowed in interface type"},
+ {279, 33, "var declaration not allowed in for initializer"},
+ {25, 68, "unexpected { at end of statement"},
+ {371, 68, "unexpected { at end of statement"},
+ {122, 63, "argument to go/defer must be function call"},
+ {398, 63, "need trailing comma before newline in composite literal"},
+ {414, 63, "need trailing comma before newline in composite literal"},
+ {124, 25, "nested func not allowed"},
+ {650, 63, "else must be followed by if or statement block"},
+}
+
//line yaccpar:1
/* parser for yacc output */
@@ -877,7 +906,6 @@ type yyParser interface {
type yyParserImpl struct {
lookahead func() int
- state func() int
}
func (p *yyParserImpl) Lookahead() int {
@@ -887,7 +915,6 @@ func (p *yyParserImpl) Lookahead() int {
func yyNewParser() yyParser {
p := &yyParserImpl{
lookahead: func() int { return -1 },
- state: func() int { return -1 },
}
return p
}
@@ -918,6 +945,13 @@ func yyErrorMessage(state, lookAhead int) string {
if !yyErrorVerbose {
return "syntax error"
}
+
+ for _, e := range yyErrorMessages {
+ if e.state == state && e.token == lookAhead {
+ return "syntax error: " + e.msg
+ }
+ }
+
res := "syntax error: unexpected " + yyTokname(lookAhead)
// To match Bison, suggest at most four expected tokens.
@@ -1020,7 +1054,6 @@ func (yyrcvr *yyParserImpl) Parse(yylex yyLexer) int {
yystate := 0
yychar := -1
yytoken := -1 // yychar translated into internal numbering
- yyrcvr.state = func() int { return yystate }
yyrcvr.lookahead = func() int { return yychar }
defer func() {
// Make sure we report no lookahead when not parsing.
@@ -1187,13 +1220,13 @@ yydefault:
case 1:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:127
+ //line go.y:189
{
xtop = concat(xtop, yyDollar[4].list)
}
case 2:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:133
+ //line go.y:195
{
prevlineno = lineno
Yyerror("package statement must be first")
@@ -1201,13 +1234,13 @@ yydefault:
}
case 3:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:139
+ //line go.y:201
{
mkpackage(yyDollar[2].sym.Name)
}
case 4:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:149
+ //line go.y:211
{
importpkg = Runtimepkg
@@ -1220,13 +1253,13 @@ yydefault:
}
case 5:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:161
+ //line go.y:223
{
importpkg = nil
}
case 11:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:175
+ //line go.y:237
{
ipkg := importpkg
my := importmyname
@@ -1263,7 +1296,7 @@ yydefault:
}
case 12:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:210
+ //line go.y:272
{
// When an invalid import path is passed to importfile,
// it calls Yyerror and then sets up a fake import with
@@ -1275,7 +1308,7 @@ yydefault:
}
case 15:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:226
+ //line go.y:288
{
// import with original name
yyVAL.i = parserline()
@@ -1284,7 +1317,7 @@ yydefault:
}
case 16:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:233
+ //line go.y:295
{
// import with given name
yyVAL.i = parserline()
@@ -1293,7 +1326,7 @@ yydefault:
}
case 17:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:240
+ //line go.y:302
{
// import into my name space
yyVAL.i = parserline()
@@ -1302,7 +1335,7 @@ yydefault:
}
case 18:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:249
+ //line go.y:311
{
if importpkg.Name == "" {
importpkg.Name = yyDollar[2].sym.Name
@@ -1319,7 +1352,7 @@ yydefault:
}
case 20:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:266
+ //line go.y:328
{
if yyDollar[1].sym.Name == "safe" {
curio.importsafe = true
@@ -1327,64 +1360,64 @@ yydefault:
}
case 21:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:273
+ //line go.y:335
{
defercheckwidth()
}
case 22:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:277
+ //line go.y:339
{
resumecheckwidth()
unimportfile()
}
case 23:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:286
+ //line go.y:348
{
Yyerror("empty top-level declaration")
yyVAL.list = nil
}
case 25:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:292
+ //line go.y:354
{
yyVAL.list = list1(yyDollar[1].node)
}
case 26:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:296
+ //line go.y:358
{
Yyerror("non-declaration statement outside function body")
yyVAL.list = nil
}
case 27:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:301
+ //line go.y:363
{
yyVAL.list = nil
}
case 28:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:307
+ //line go.y:369
{
yyVAL.list = yyDollar[2].list
}
case 29:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:311
+ //line go.y:373
{
yyVAL.list = yyDollar[3].list
}
case 30:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:315
+ //line go.y:377
{
yyVAL.list = nil
}
case 31:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:319
+ //line go.y:381
{
yyVAL.list = yyDollar[2].list
iota_ = -100000
@@ -1392,7 +1425,7 @@ yydefault:
}
case 32:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:325
+ //line go.y:387
{
yyVAL.list = yyDollar[3].list
iota_ = -100000
@@ -1400,7 +1433,7 @@ yydefault:
}
case 33:
yyDollar = yyS[yypt-7 : yypt+1]
- //line go.y:331
+ //line go.y:393
{
yyVAL.list = concat(yyDollar[3].list, yyDollar[5].list)
iota_ = -100000
@@ -1408,80 +1441,80 @@ yydefault:
}
case 34:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:337
+ //line go.y:399
{
yyVAL.list = nil
iota_ = -100000
}
case 35:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:342
+ //line go.y:404
{
yyVAL.list = list1(yyDollar[2].node)
}
case 36:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:346
+ //line go.y:408
{
yyVAL.list = yyDollar[3].list
}
case 37:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:350
+ //line go.y:412
{
yyVAL.list = nil
}
case 38:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:356
+ //line go.y:418
{
iota_ = 0
}
case 39:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:362
+ //line go.y:424
{
yyVAL.list = variter(yyDollar[1].list, yyDollar[2].node, nil)
}
case 40:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:366
+ //line go.y:428
{
yyVAL.list = variter(yyDollar[1].list, yyDollar[2].node, yyDollar[4].list)
}
case 41:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:370
+ //line go.y:432
{
yyVAL.list = variter(yyDollar[1].list, nil, yyDollar[3].list)
}
case 42:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:376
+ //line go.y:438
{
yyVAL.list = constiter(yyDollar[1].list, yyDollar[2].node, yyDollar[4].list)
}
case 43:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:380
+ //line go.y:442
{
yyVAL.list = constiter(yyDollar[1].list, nil, yyDollar[3].list)
}
case 45:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:387
+ //line go.y:449
{
yyVAL.list = constiter(yyDollar[1].list, yyDollar[2].node, nil)
}
case 46:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:391
+ //line go.y:453
{
yyVAL.list = constiter(yyDollar[1].list, nil, nil)
}
case 47:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:397
+ //line go.y:459
{
// different from dclname because the name
// becomes visible right here, not at the end
@@ -1490,13 +1523,13 @@ yydefault:
}
case 48:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:406
+ //line go.y:468
{
yyVAL.node = typedcl1(yyDollar[1].node, yyDollar[2].node, true)
}
case 49:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:412
+ //line go.y:474
{
yyVAL.node = yyDollar[1].node
@@ -1512,14 +1545,14 @@ yydefault:
}
case 50:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:426
+ //line go.y:488
{
yyVAL.node = Nod(OASOP, yyDollar[1].node, yyDollar[3].node)
yyVAL.node.Etype = uint8(yyDollar[2].i) // rathole to pass opcode
}
case 51:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:431
+ //line go.y:493
{
if yyDollar[1].list.Next == nil && yyDollar[3].list.Next == nil {
// simple
@@ -1533,7 +1566,7 @@ yydefault:
}
case 52:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:443
+ //line go.y:505
{
if yyDollar[3].list.N.Op == OTYPESW {
yyVAL.node = Nod(OTYPESW, nil, yyDollar[3].list.N.Right)
@@ -1553,7 +1586,7 @@ yydefault:
}
case 53:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:461
+ //line go.y:523
{
yyVAL.node = Nod(OASOP, yyDollar[1].node, Nodintconst(1))
yyVAL.node.Implicit = true
@@ -1561,7 +1594,7 @@ yydefault:
}
case 54:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:467
+ //line go.y:529
{
yyVAL.node = Nod(OASOP, yyDollar[1].node, Nodintconst(1))
yyVAL.node.Implicit = true
@@ -1569,7 +1602,7 @@ yydefault:
}
case 55:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:475
+ //line go.y:537
{
var n, nn *Node
@@ -1594,7 +1627,7 @@ yydefault:
}
case 56:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:498
+ //line go.y:560
{
var n *Node
@@ -1614,7 +1647,7 @@ yydefault:
}
case 57:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:516
+ //line go.y:578
{
// will be converted to OCASE
// right will point to next case
@@ -1625,7 +1658,7 @@ yydefault:
}
case 58:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:525
+ //line go.y:587
{
var n, nn *Node
@@ -1646,13 +1679,13 @@ yydefault:
}
case 59:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:546
+ //line go.y:608
{
markdcl()
}
case 60:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:550
+ //line go.y:612
{
if yyDollar[3].list == nil {
yyVAL.node = Nod(OEMPTY, nil, nil)
@@ -1663,7 +1696,7 @@ yydefault:
}
case 61:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:561
+ //line go.y:623
{
// If the last token read by the lexer was consumed
// as part of the case, clear it (parser has cleared yychar).
@@ -1676,7 +1709,7 @@ yydefault:
}
case 62:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:572
+ //line go.y:634
{
// This is the only place in the language where a statement
// list is not allowed to drop the final semicolon, because
@@ -1696,32 +1729,32 @@ yydefault:
}
case 63:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:591
+ //line go.y:653
{
yyVAL.list = nil
}
case 64:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:595
+ //line go.y:657
{
yyVAL.list = list(yyDollar[1].list, yyDollar[2].node)
}
case 65:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:601
+ //line go.y:663
{
markdcl()
}
case 66:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:605
+ //line go.y:667
{
yyVAL.list = yyDollar[3].list
popdcl()
}
case 67:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:612
+ //line go.y:674
{
yyVAL.node = Nod(ORANGE, nil, yyDollar[4].node)
yyVAL.node.List = yyDollar[1].list
@@ -1729,7 +1762,7 @@ yydefault:
}
case 68:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:618
+ //line go.y:680
{
yyVAL.node = Nod(ORANGE, nil, yyDollar[4].node)
yyVAL.node.List = yyDollar[1].list
@@ -1738,14 +1771,14 @@ yydefault:
}
case 69:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:625
+ //line go.y:687
{
yyVAL.node = Nod(ORANGE, nil, yyDollar[2].node)
yyVAL.node.Etype = 0 // := flag
}
case 70:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:632
+ //line go.y:694
{
// init ; test ; incr
if yyDollar[5].node != nil && yyDollar[5].node.Colas {
@@ -1760,7 +1793,7 @@ yydefault:
}
case 71:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:645
+ //line go.y:707
{
// normal test
yyVAL.node = Nod(OFOR, nil, nil)
@@ -1768,27 +1801,27 @@ yydefault:
}
case 73:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:654
+ //line go.y:716
{
yyVAL.node = yyDollar[1].node
yyVAL.node.Nbody = concat(yyVAL.node.Nbody, yyDollar[2].list)
}
case 74:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:661
+ //line go.y:723
{
markdcl()
}
case 75:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:665
+ //line go.y:727
{
yyVAL.node = yyDollar[3].node
popdcl()
}
case 76:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:672
+ //line go.y:734
{
// test
yyVAL.node = Nod(OIF, nil, nil)
@@ -1796,7 +1829,7 @@ yydefault:
}
case 77:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:678
+ //line go.y:740
{
// init ; test
yyVAL.node = Nod(OIF, nil, nil)
@@ -1807,13 +1840,13 @@ yydefault:
}
case 78:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:690
+ //line go.y:752
{
markdcl()
}
case 79:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:694
+ //line go.y:756
{
if yyDollar[3].node.Ntest == nil {
Yyerror("missing condition in if statement")
@@ -1821,13 +1854,13 @@ yydefault:
}
case 80:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:700
+ //line go.y:762
{
yyDollar[3].node.Nbody = yyDollar[5].list
}
case 81:
yyDollar = yyS[yypt-8 : yypt+1]
- //line go.y:704
+ //line go.y:766
{
var n *Node
var nn *NodeList
@@ -1845,13 +1878,13 @@ yydefault:
}
case 82:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:722
+ //line go.y:784
{
markdcl()
}
case 83:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:726
+ //line go.y:788
{
if yyDollar[4].node.Ntest == nil {
Yyerror("missing condition in if statement")
@@ -1861,25 +1894,25 @@ yydefault:
}
case 84:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:735
+ //line go.y:797
{
yyVAL.list = nil
}
case 85:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:739
+ //line go.y:801
{
yyVAL.list = concat(yyDollar[1].list, yyDollar[2].list)
}
case 86:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:744
+ //line go.y:806
{
yyVAL.list = nil
}
case 87:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:748
+ //line go.y:810
{
l := &NodeList{N: yyDollar[2].node}
l.End = l
@@ -1887,13 +1920,13 @@ yydefault:
}
case 88:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:756
+ //line go.y:818
{
markdcl()
}
case 89:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:760
+ //line go.y:822
{
var n *Node
n = yyDollar[3].node.Ntest
@@ -1904,7 +1937,7 @@ yydefault:
}
case 90:
yyDollar = yyS[yypt-7 : yypt+1]
- //line go.y:769
+ //line go.y:831
{
yyVAL.node = yyDollar[3].node
yyVAL.node.Op = OSWITCH
@@ -1914,13 +1947,13 @@ yydefault:
}
case 91:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:779
+ //line go.y:841
{
typesw = Nod(OXXX, typesw, nil)
}
case 92:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:783
+ //line go.y:845
{
yyVAL.node = Nod(OSELECT, nil, nil)
yyVAL.node.Lineno = typesw.Lineno
@@ -1929,133 +1962,133 @@ yydefault:
}
case 94:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:796
+ //line go.y:858
{
yyVAL.node = Nod(OOROR, yyDollar[1].node, yyDollar[3].node)
}
case 95:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:800
+ //line go.y:862
{
yyVAL.node = Nod(OANDAND, yyDollar[1].node, yyDollar[3].node)
}
case 96:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:804
+ //line go.y:866
{
yyVAL.node = Nod(OEQ, yyDollar[1].node, yyDollar[3].node)
}
case 97:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:808
+ //line go.y:870
{
yyVAL.node = Nod(ONE, yyDollar[1].node, yyDollar[3].node)
}
case 98:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:812
+ //line go.y:874
{
yyVAL.node = Nod(OLT, yyDollar[1].node, yyDollar[3].node)
}
case 99:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:816
+ //line go.y:878
{
yyVAL.node = Nod(OLE, yyDollar[1].node, yyDollar[3].node)
}
case 100:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:820
+ //line go.y:882
{
yyVAL.node = Nod(OGE, yyDollar[1].node, yyDollar[3].node)
}
case 101:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:824
+ //line go.y:886
{
yyVAL.node = Nod(OGT, yyDollar[1].node, yyDollar[3].node)
}
case 102:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:828
+ //line go.y:890
{
yyVAL.node = Nod(OADD, yyDollar[1].node, yyDollar[3].node)
}
case 103:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:832
+ //line go.y:894
{
yyVAL.node = Nod(OSUB, yyDollar[1].node, yyDollar[3].node)
}
case 104:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:836
+ //line go.y:898
{
yyVAL.node = Nod(OOR, yyDollar[1].node, yyDollar[3].node)
}
case 105:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:840
+ //line go.y:902
{
yyVAL.node = Nod(OXOR, yyDollar[1].node, yyDollar[3].node)
}
case 106:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:844
+ //line go.y:906
{
yyVAL.node = Nod(OMUL, yyDollar[1].node, yyDollar[3].node)
}
case 107:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:848
+ //line go.y:910
{
yyVAL.node = Nod(ODIV, yyDollar[1].node, yyDollar[3].node)
}
case 108:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:852
+ //line go.y:914
{
yyVAL.node = Nod(OMOD, yyDollar[1].node, yyDollar[3].node)
}
case 109:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:856
+ //line go.y:918
{
yyVAL.node = Nod(OAND, yyDollar[1].node, yyDollar[3].node)
}
case 110:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:860
+ //line go.y:922
{
yyVAL.node = Nod(OANDNOT, yyDollar[1].node, yyDollar[3].node)
}
case 111:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:864
+ //line go.y:926
{
yyVAL.node = Nod(OLSH, yyDollar[1].node, yyDollar[3].node)
}
case 112:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:868
+ //line go.y:930
{
yyVAL.node = Nod(ORSH, yyDollar[1].node, yyDollar[3].node)
}
case 113:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:873
+ //line go.y:935
{
yyVAL.node = Nod(OSEND, yyDollar[1].node, yyDollar[3].node)
}
case 115:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:880
+ //line go.y:942
{
yyVAL.node = Nod(OIND, yyDollar[2].node, nil)
}
case 116:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:884
+ //line go.y:946
{
if yyDollar[2].node.Op == OCOMPLIT {
// Special case for &T{...}: turn into (*T){...}.
@@ -2068,57 +2101,57 @@ yydefault:
}
case 117:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:895
+ //line go.y:957
{
yyVAL.node = Nod(OPLUS, yyDollar[2].node, nil)
}
case 118:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:899
+ //line go.y:961
{
yyVAL.node = Nod(OMINUS, yyDollar[2].node, nil)
}
case 119:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:903
+ //line go.y:965
{
yyVAL.node = Nod(ONOT, yyDollar[2].node, nil)
}
case 120:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:907
+ //line go.y:969
{
Yyerror("the bitwise complement operator is ^")
yyVAL.node = Nod(OCOM, yyDollar[2].node, nil)
}
case 121:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:912
+ //line go.y:974
{
yyVAL.node = Nod(OCOM, yyDollar[2].node, nil)
}
case 122:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:916
+ //line go.y:978
{
yyVAL.node = Nod(ORECV, yyDollar[2].node, nil)
}
case 123:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:926
+ //line go.y:988
{
yyVAL.node = Nod(OCALL, yyDollar[1].node, nil)
}
case 124:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:930
+ //line go.y:992
{
yyVAL.node = Nod(OCALL, yyDollar[1].node, nil)
yyVAL.node.List = yyDollar[3].list
}
case 125:
yyDollar = yyS[yypt-6 : yypt+1]
- //line go.y:935
+ //line go.y:997
{
yyVAL.node = Nod(OCALL, yyDollar[1].node, nil)
yyVAL.node.List = yyDollar[3].list
@@ -2126,13 +2159,13 @@ yydefault:
}
case 126:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:943
+ //line go.y:1005
{
yyVAL.node = nodlit(yyDollar[1].val)
}
case 128:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:948
+ //line go.y:1010
{
if yyDollar[1].node.Op == OPACK {
var s *Sym
@@ -2145,31 +2178,31 @@ yydefault:
}
case 129:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:959
+ //line go.y:1021
{
yyVAL.node = Nod(ODOTTYPE, yyDollar[1].node, yyDollar[4].node)
}
case 130:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:963
+ //line go.y:1025
{
yyVAL.node = Nod(OTYPESW, nil, yyDollar[1].node)
}
case 131:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:967
+ //line go.y:1029
{
yyVAL.node = Nod(OINDEX, yyDollar[1].node, yyDollar[3].node)
}
case 132:
yyDollar = yyS[yypt-6 : yypt+1]
- //line go.y:971
+ //line go.y:1033
{
yyVAL.node = Nod(OSLICE, yyDollar[1].node, Nod(OKEY, yyDollar[3].node, yyDollar[5].node))
}
case 133:
yyDollar = yyS[yypt-8 : yypt+1]
- //line go.y:975
+ //line go.y:1037
{
if yyDollar[5].node == nil {
Yyerror("middle index required in 3-index slice")
@@ -2181,7 +2214,7 @@ yydefault:
}
case 135:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:986
+ //line go.y:1048
{
// conversion
yyVAL.node = Nod(OCALL, yyDollar[1].node, nil)
@@ -2189,7 +2222,7 @@ yydefault:
}
case 136:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:992
+ //line go.y:1054
{
yyVAL.node = yyDollar[3].node
yyVAL.node.Right = yyDollar[1].node
@@ -2198,7 +2231,7 @@ yydefault:
}
case 137:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:999
+ //line go.y:1061
{
yyVAL.node = yyDollar[3].node
yyVAL.node.Right = yyDollar[1].node
@@ -2206,7 +2239,7 @@ yydefault:
}
case 138:
yyDollar = yyS[yypt-7 : yypt+1]
- //line go.y:1005
+ //line go.y:1067
{
Yyerror("cannot parenthesize type in composite literal")
yyVAL.node = yyDollar[5].node
@@ -2215,7 +2248,7 @@ yydefault:
}
case 140:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:1014
+ //line go.y:1076
{
// composite expression.
// make node early so we get the right line number.
@@ -2223,13 +2256,13 @@ yydefault:
}
case 141:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1022
+ //line go.y:1084
{
yyVAL.node = Nod(OKEY, yyDollar[1].node, yyDollar[3].node)
}
case 142:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1028
+ //line go.y:1090
{
// These nodes do not carry line numbers.
// Since a composite literal commonly spans several lines,
@@ -2244,21 +2277,21 @@ yydefault:
}
case 143:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:1041
+ //line go.y:1103
{
yyVAL.node = yyDollar[2].node
yyVAL.node.List = yyDollar[3].list
}
case 145:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:1049
+ //line go.y:1111
{
yyVAL.node = yyDollar[2].node
yyVAL.node.List = yyDollar[3].list
}
case 147:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1057
+ //line go.y:1119
{
yyVAL.node = yyDollar[2].node
@@ -2272,19 +2305,19 @@ yydefault:
}
case 151:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1078
+ //line go.y:1140
{
yyVAL.i = LBODY
}
case 152:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1082
+ //line go.y:1144
{
yyVAL.i = '{'
}
case 153:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1093
+ //line go.y:1155
{
if yyDollar[1].sym == nil {
yyVAL.node = nil
@@ -2294,19 +2327,19 @@ yydefault:
}
case 154:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1103
+ //line go.y:1165
{
yyVAL.node = dclname(yyDollar[1].sym)
}
case 155:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:1108
+ //line go.y:1170
{
yyVAL.node = nil
}
case 157:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1115
+ //line go.y:1177
{
yyVAL.sym = yyDollar[1].sym
// during imports, unqualified non-exported identifiers are from builtinpkg
@@ -2316,45 +2349,45 @@ yydefault:
}
case 159:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1124
+ //line go.y:1186
{
yyVAL.sym = nil
}
case 160:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:1130
+ //line go.y:1192
{
var p *Pkg
- if yyDollar[2].val.U.Sval == "" {
+ if yyDollar[2].val.U.(string) == "" {
p = importpkg
} else {
- if isbadimport(yyDollar[2].val.U.Sval) {
+ if isbadimport(yyDollar[2].val.U.(string)) {
errorexit()
}
- p = mkpkg(yyDollar[2].val.U.Sval)
+ p = mkpkg(yyDollar[2].val.U.(string))
}
yyVAL.sym = Pkglookup(yyDollar[4].sym.Name, p)
}
case 161:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:1144
+ //line go.y:1206
{
var p *Pkg
- if yyDollar[2].val.U.Sval == "" {
+ if yyDollar[2].val.U.(string) == "" {
p = importpkg
} else {
- if isbadimport(yyDollar[2].val.U.Sval) {
+ if isbadimport(yyDollar[2].val.U.(string)) {
errorexit()
}
- p = mkpkg(yyDollar[2].val.U.Sval)
+ p = mkpkg(yyDollar[2].val.U.(string))
}
yyVAL.sym = Pkglookup("?", p)
}
case 162:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1160
+ //line go.y:1222
{
yyVAL.node = oldname(yyDollar[1].sym)
if yyVAL.node.Pack != nil {
@@ -2363,38 +2396,38 @@ yydefault:
}
case 164:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1181
+ //line go.y:1243
{
Yyerror("final argument in variadic function missing type")
yyVAL.node = Nod(ODDD, typenod(typ(TINTER)), nil)
}
case 165:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1186
+ //line go.y:1248
{
yyVAL.node = Nod(ODDD, yyDollar[2].node, nil)
}
case 171:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1197
+ //line go.y:1259
{
yyVAL.node = yyDollar[2].node
}
case 175:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1206
+ //line go.y:1268
{
yyVAL.node = Nod(OIND, yyDollar[2].node, nil)
}
case 180:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1216
+ //line go.y:1278
{
yyVAL.node = yyDollar[2].node
}
case 190:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1237
+ //line go.y:1299
{
if yyDollar[1].node.Op == OPACK {
var s *Sym
@@ -2407,53 +2440,53 @@ yydefault:
}
case 191:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:1250
+ //line go.y:1312
{
yyVAL.node = Nod(OTARRAY, yyDollar[2].node, yyDollar[4].node)
}
case 192:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:1254
+ //line go.y:1316
{
// array literal of nelem
yyVAL.node = Nod(OTARRAY, Nod(ODDD, nil, nil), yyDollar[4].node)
}
case 193:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1259
+ //line go.y:1321
{
yyVAL.node = Nod(OTCHAN, yyDollar[2].node, nil)
yyVAL.node.Etype = Cboth
}
case 194:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1264
+ //line go.y:1326
{
yyVAL.node = Nod(OTCHAN, yyDollar[3].node, nil)
yyVAL.node.Etype = Csend
}
case 195:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:1269
+ //line go.y:1331
{
yyVAL.node = Nod(OTMAP, yyDollar[3].node, yyDollar[5].node)
}
case 198:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1277
+ //line go.y:1339
{
yyVAL.node = Nod(OIND, yyDollar[2].node, nil)
}
case 199:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1283
+ //line go.y:1345
{
yyVAL.node = Nod(OTCHAN, yyDollar[3].node, nil)
yyVAL.node.Etype = Crecv
}
case 200:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:1290
+ //line go.y:1352
{
yyVAL.node = Nod(OTSTRUCT, nil, nil)
yyVAL.node.List = yyDollar[3].list
@@ -2461,14 +2494,14 @@ yydefault:
}
case 201:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1296
+ //line go.y:1358
{
yyVAL.node = Nod(OTSTRUCT, nil, nil)
fixlbrace(yyDollar[2].i)
}
case 202:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:1303
+ //line go.y:1365
{
yyVAL.node = Nod(OTINTER, nil, nil)
yyVAL.node.List = yyDollar[3].list
@@ -2476,14 +2509,14 @@ yydefault:
}
case 203:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1309
+ //line go.y:1371
{
yyVAL.node = Nod(OTINTER, nil, nil)
fixlbrace(yyDollar[2].i)
}
case 204:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1320
+ //line go.y:1382
{
yyVAL.node = yyDollar[2].node
if yyVAL.node == nil {
@@ -2501,7 +2534,7 @@ yydefault:
}
case 205:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:1338
+ //line go.y:1400
{
var t *Node
@@ -2527,14 +2560,14 @@ yydefault:
yyVAL.node = Nod(ODCLFUNC, nil, nil)
yyVAL.node.Nname = newfuncname(yyDollar[1].sym)
yyVAL.node.Nname.Defn = yyVAL.node
- yyVAL.node.Nname.Ntype = t // TODO: check if nname already has an ntype
+ yyVAL.node.Nname.Param.Ntype = t // TODO: check if nname already has an ntype
declare(yyVAL.node.Nname, PFUNC)
funchdr(yyVAL.node)
}
case 206:
yyDollar = yyS[yypt-8 : yypt+1]
- //line go.y:1369
+ //line go.y:1431
{
var rcvr, t *Node
@@ -2564,7 +2597,7 @@ yydefault:
yyVAL.node.Func.Shortname = newfuncname(yyDollar[4].sym)
yyVAL.node.Nname = methodname1(yyVAL.node.Func.Shortname, rcvr.Right)
yyVAL.node.Nname.Defn = yyVAL.node
- yyVAL.node.Nname.Ntype = t
+ yyVAL.node.Nname.Param.Ntype = t
yyVAL.node.Nname.Nointerface = nointerface
declare(yyVAL.node.Nname, PFUNC)
@@ -2572,7 +2605,7 @@ yydefault:
}
case 207:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:1407
+ //line go.y:1469
{
var s *Sym
var t *Type
@@ -2599,7 +2632,7 @@ yydefault:
}
case 208:
yyDollar = yyS[yypt-8 : yypt+1]
- //line go.y:1432
+ //line go.y:1494
{
yyVAL.node = methodname1(newname(yyDollar[4].sym), yyDollar[2].list.N.Right)
yyVAL.node.Type = functype(yyDollar[2].list.N, yyDollar[6].list, yyDollar[8].list)
@@ -2617,7 +2650,7 @@ yydefault:
}
case 209:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:1450
+ //line go.y:1512
{
yyDollar[3].list = checkarglist(yyDollar[3].list, 1)
yyVAL.node = Nod(OTFUNC, nil, nil)
@@ -2626,13 +2659,13 @@ yydefault:
}
case 210:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:1458
+ //line go.y:1520
{
yyVAL.list = nil
}
case 211:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1462
+ //line go.y:1524
{
yyVAL.list = yyDollar[2].list
if yyVAL.list == nil {
@@ -2641,51 +2674,51 @@ yydefault:
}
case 212:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:1471
+ //line go.y:1533
{
yyVAL.list = nil
}
case 213:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1475
+ //line go.y:1537
{
yyVAL.list = list1(Nod(ODCLFIELD, nil, yyDollar[1].node))
}
case 214:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1479
+ //line go.y:1541
{
yyDollar[2].list = checkarglist(yyDollar[2].list, 0)
yyVAL.list = yyDollar[2].list
}
case 215:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1486
+ //line go.y:1548
{
closurehdr(yyDollar[1].node)
}
case 216:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:1492
+ //line go.y:1554
{
yyVAL.node = closurebody(yyDollar[3].list)
fixlbrace(yyDollar[2].i)
}
case 217:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1497
+ //line go.y:1559
{
yyVAL.node = closurebody(nil)
}
case 218:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:1508
+ //line go.y:1570
{
yyVAL.list = nil
}
case 219:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1512
+ //line go.y:1574
{
yyVAL.list = concat(yyDollar[1].list, yyDollar[2].list)
if nsyntaxerrors == 0 {
@@ -2698,49 +2731,49 @@ yydefault:
}
case 221:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1526
+ //line go.y:1588
{
yyVAL.list = concat(yyDollar[1].list, yyDollar[3].list)
}
case 223:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1533
+ //line go.y:1595
{
yyVAL.list = concat(yyDollar[1].list, yyDollar[3].list)
}
case 224:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1539
+ //line go.y:1601
{
yyVAL.list = list1(yyDollar[1].node)
}
case 225:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1543
+ //line go.y:1605
{
yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
}
case 227:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1550
+ //line go.y:1612
{
yyVAL.list = concat(yyDollar[1].list, yyDollar[3].list)
}
case 228:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1556
+ //line go.y:1618
{
yyVAL.list = list1(yyDollar[1].node)
}
case 229:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1560
+ //line go.y:1622
{
yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
}
case 230:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1566
+ //line go.y:1628
{
var l *NodeList
@@ -2766,14 +2799,14 @@ yydefault:
}
case 231:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1590
+ //line go.y:1652
{
yyDollar[1].node.Val = yyDollar[2].val
yyVAL.list = list1(yyDollar[1].node)
}
case 232:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:1595
+ //line go.y:1657
{
yyDollar[2].node.Val = yyDollar[4].val
yyVAL.list = list1(yyDollar[2].node)
@@ -2781,7 +2814,7 @@ yydefault:
}
case 233:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1601
+ //line go.y:1663
{
yyDollar[2].node.Right = Nod(OIND, yyDollar[2].node.Right, nil)
yyDollar[2].node.Val = yyDollar[3].val
@@ -2789,7 +2822,7 @@ yydefault:
}
case 234:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:1607
+ //line go.y:1669
{
yyDollar[3].node.Right = Nod(OIND, yyDollar[3].node.Right, nil)
yyDollar[3].node.Val = yyDollar[5].val
@@ -2798,7 +2831,7 @@ yydefault:
}
case 235:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:1614
+ //line go.y:1676
{
yyDollar[3].node.Right = Nod(OIND, yyDollar[3].node.Right, nil)
yyDollar[3].node.Val = yyDollar[5].val
@@ -2807,7 +2840,7 @@ yydefault:
}
case 236:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1623
+ //line go.y:1685
{
var n *Node
@@ -2819,7 +2852,7 @@ yydefault:
}
case 237:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1633
+ //line go.y:1695
{
var pkg *Pkg
@@ -2834,33 +2867,33 @@ yydefault:
}
case 238:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1648
+ //line go.y:1710
{
yyVAL.node = embedded(yyDollar[1].sym, localpkg)
}
case 239:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1654
+ //line go.y:1716
{
yyVAL.node = Nod(ODCLFIELD, yyDollar[1].node, yyDollar[2].node)
ifacedcl(yyVAL.node)
}
case 240:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1659
+ //line go.y:1721
{
yyVAL.node = Nod(ODCLFIELD, nil, oldname(yyDollar[1].sym))
}
case 241:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1663
+ //line go.y:1725
{
yyVAL.node = Nod(ODCLFIELD, nil, oldname(yyDollar[2].sym))
Yyerror("cannot parenthesize embedded type")
}
case 242:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:1670
+ //line go.y:1732
{
// without func keyword
yyDollar[2].list = checkarglist(yyDollar[2].list, 1)
@@ -2870,7 +2903,7 @@ yydefault:
}
case 244:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1684
+ //line go.y:1746
{
yyVAL.node = Nod(ONONAME, nil, nil)
yyVAL.node.Sym = yyDollar[1].sym
@@ -2878,7 +2911,7 @@ yydefault:
}
case 245:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1690
+ //line go.y:1752
{
yyVAL.node = Nod(ONONAME, nil, nil)
yyVAL.node.Sym = yyDollar[1].sym
@@ -2886,56 +2919,56 @@ yydefault:
}
case 247:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1699
+ //line go.y:1761
{
yyVAL.list = list1(yyDollar[1].node)
}
case 248:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1703
+ //line go.y:1765
{
yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
}
case 249:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:1708
+ //line go.y:1770
{
yyVAL.list = nil
}
case 250:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1712
+ //line go.y:1774
{
yyVAL.list = yyDollar[1].list
}
case 251:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:1720
+ //line go.y:1782
{
yyVAL.node = nil
}
case 253:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1725
+ //line go.y:1787
{
yyVAL.node = liststmt(yyDollar[1].list)
}
case 255:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1730
+ //line go.y:1792
{
yyVAL.node = nil
}
case 261:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1741
+ //line go.y:1803
{
yyDollar[1].node = Nod(OLABEL, yyDollar[1].node, nil)
yyDollar[1].node.Sym = dclstack // context, for goto restrictions
}
case 262:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:1746
+ //line go.y:1808
{
var l *NodeList
@@ -2948,7 +2981,7 @@ yydefault:
}
case 263:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1757
+ //line go.y:1819
{
// will be converted to OFALL
yyVAL.node = Nod(OXFALL, nil, nil)
@@ -2956,38 +2989,38 @@ yydefault:
}
case 264:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1763
+ //line go.y:1825
{
yyVAL.node = Nod(OBREAK, yyDollar[2].node, nil)
}
case 265:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1767
+ //line go.y:1829
{
yyVAL.node = Nod(OCONTINUE, yyDollar[2].node, nil)
}
case 266:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1771
+ //line go.y:1833
{
yyVAL.node = Nod(OPROC, yyDollar[2].node, nil)
}
case 267:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1775
+ //line go.y:1837
{
yyVAL.node = Nod(ODEFER, yyDollar[2].node, nil)
}
case 268:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1779
+ //line go.y:1841
{
yyVAL.node = Nod(OGOTO, yyDollar[2].node, nil)
yyVAL.node.Sym = dclstack // context, for goto restrictions
}
case 269:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1784
+ //line go.y:1846
{
yyVAL.node = Nod(ORETURN, nil, nil)
yyVAL.node.List = yyDollar[2].list
@@ -3009,7 +3042,7 @@ yydefault:
}
case 270:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1806
+ //line go.y:1868
{
yyVAL.list = nil
if yyDollar[1].node != nil {
@@ -3018,7 +3051,7 @@ yydefault:
}
case 271:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1813
+ //line go.y:1875
{
yyVAL.list = yyDollar[1].list
if yyDollar[3].node != nil {
@@ -3027,163 +3060,163 @@ yydefault:
}
case 272:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1822
+ //line go.y:1884
{
yyVAL.list = list1(yyDollar[1].node)
}
case 273:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1826
+ //line go.y:1888
{
yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
}
case 274:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1832
+ //line go.y:1894
{
yyVAL.list = list1(yyDollar[1].node)
}
case 275:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1836
+ //line go.y:1898
{
yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
}
case 276:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1842
+ //line go.y:1904
{
yyVAL.list = list1(yyDollar[1].node)
}
case 277:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1846
+ //line go.y:1908
{
yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
}
case 278:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1852
+ //line go.y:1914
{
yyVAL.list = list1(yyDollar[1].node)
}
case 279:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1856
+ //line go.y:1918
{
yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
}
case 280:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1865
+ //line go.y:1927
{
yyVAL.list = list1(yyDollar[1].node)
}
case 281:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1869
+ //line go.y:1931
{
yyVAL.list = list1(yyDollar[1].node)
}
case 282:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1873
+ //line go.y:1935
{
yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
}
case 283:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1877
+ //line go.y:1939
{
yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
}
case 284:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:1882
+ //line go.y:1944
{
yyVAL.list = nil
}
case 285:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:1886
+ //line go.y:1948
{
yyVAL.list = yyDollar[1].list
}
case 290:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:1900
+ //line go.y:1962
{
yyVAL.node = nil
}
case 292:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:1906
+ //line go.y:1968
{
yyVAL.list = nil
}
case 294:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:1912
+ //line go.y:1974
{
yyVAL.node = nil
}
case 296:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:1918
+ //line go.y:1980
{
yyVAL.list = nil
}
case 298:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:1924
+ //line go.y:1986
{
yyVAL.list = nil
}
case 300:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:1930
+ //line go.y:1992
{
yyVAL.list = nil
}
case 302:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:1936
+ //line go.y:1998
{
yyVAL.val.Ctype = CTxxx
}
case 304:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:1946
+ //line go.y:2008
{
- importimport(yyDollar[2].sym, yyDollar[3].val.U.Sval)
+ importimport(yyDollar[2].sym, yyDollar[3].val.U.(string))
}
case 305:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:1950
+ //line go.y:2012
{
importvar(yyDollar[2].sym, yyDollar[3].typ)
}
case 306:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:1954
+ //line go.y:2016
{
importconst(yyDollar[2].sym, Types[TIDEAL], yyDollar[4].node)
}
case 307:
yyDollar = yyS[yypt-6 : yypt+1]
- //line go.y:1958
+ //line go.y:2020
{
importconst(yyDollar[2].sym, yyDollar[3].typ, yyDollar[5].node)
}
case 308:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:1962
+ //line go.y:2024
{
importtype(yyDollar[2].typ, yyDollar[3].typ)
}
case 309:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:1966
+ //line go.y:2028
{
if yyDollar[2].node == nil {
dclcontext = PEXTERN // since we skip the funcbody below
@@ -3196,35 +3229,35 @@ yydefault:
importlist = list(importlist, yyDollar[2].node)
if Debug['E'] > 0 {
- print("import [%q] func %lN \n", importpkg.Path, yyDollar[2].node)
+ fmt.Printf("import [%q] func %v \n", importpkg.Path, yyDollar[2].node)
if Debug['m'] > 2 && yyDollar[2].node.Func.Inl != nil {
- print("inl body:%+H\n", yyDollar[2].node.Func.Inl)
+ fmt.Printf("inl body:%v\n", yyDollar[2].node.Func.Inl)
}
}
}
case 310:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1987
+ //line go.y:2049
{
yyVAL.sym = yyDollar[1].sym
structpkg = yyVAL.sym.Pkg
}
case 311:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1994
+ //line go.y:2056
{
yyVAL.typ = pkgtype(yyDollar[1].sym)
importsym(yyDollar[1].sym, OTYPE)
}
case 317:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:2014
+ //line go.y:2076
{
yyVAL.typ = pkgtype(yyDollar[1].sym)
}
case 318:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:2018
+ //line go.y:2080
{
// predefined name like uint8
yyDollar[1].sym = Pkglookup(yyDollar[1].sym.Name, builtinpkg)
@@ -3237,43 +3270,43 @@ yydefault:
}
case 319:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:2029
+ //line go.y:2091
{
yyVAL.typ = aindex(nil, yyDollar[3].typ)
}
case 320:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:2033
+ //line go.y:2095
{
yyVAL.typ = aindex(nodlit(yyDollar[2].val), yyDollar[4].typ)
}
case 321:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:2037
+ //line go.y:2099
{
yyVAL.typ = maptype(yyDollar[3].typ, yyDollar[5].typ)
}
case 322:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:2041
+ //line go.y:2103
{
yyVAL.typ = tostruct(yyDollar[3].list)
}
case 323:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:2045
+ //line go.y:2107
{
yyVAL.typ = tointerface(yyDollar[3].list)
}
case 324:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:2049
+ //line go.y:2111
{
yyVAL.typ = Ptrto(yyDollar[2].typ)
}
case 325:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:2053
+ //line go.y:2115
{
yyVAL.typ = typ(TCHAN)
yyVAL.typ.Type = yyDollar[2].typ
@@ -3281,7 +3314,7 @@ yydefault:
}
case 326:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:2059
+ //line go.y:2121
{
yyVAL.typ = typ(TCHAN)
yyVAL.typ.Type = yyDollar[3].typ
@@ -3289,7 +3322,7 @@ yydefault:
}
case 327:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:2065
+ //line go.y:2127
{
yyVAL.typ = typ(TCHAN)
yyVAL.typ.Type = yyDollar[3].typ
@@ -3297,7 +3330,7 @@ yydefault:
}
case 328:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:2073
+ //line go.y:2135
{
yyVAL.typ = typ(TCHAN)
yyVAL.typ.Type = yyDollar[3].typ
@@ -3305,13 +3338,13 @@ yydefault:
}
case 329:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:2081
+ //line go.y:2143
{
yyVAL.typ = functype(nil, yyDollar[3].list, yyDollar[5].list)
}
case 330:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:2087
+ //line go.y:2149
{
yyVAL.node = Nod(ODCLFIELD, nil, typenod(yyDollar[2].typ))
if yyDollar[1].sym != nil {
@@ -3321,7 +3354,7 @@ yydefault:
}
case 331:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:2095
+ //line go.y:2157
{
var t *Type
@@ -3338,7 +3371,7 @@ yydefault:
}
case 332:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:2112
+ //line go.y:2174
{
var s *Sym
var p *Pkg
@@ -3362,55 +3395,55 @@ yydefault:
}
case 333:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:2136
+ //line go.y:2198
{
yyVAL.node = Nod(ODCLFIELD, newname(yyDollar[1].sym), typenod(functype(fakethis(), yyDollar[3].list, yyDollar[5].list)))
}
case 334:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:2140
+ //line go.y:2202
{
yyVAL.node = Nod(ODCLFIELD, nil, typenod(yyDollar[1].typ))
}
case 335:
yyDollar = yyS[yypt-0 : yypt+1]
- //line go.y:2145
+ //line go.y:2207
{
yyVAL.list = nil
}
case 337:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:2152
+ //line go.y:2214
{
yyVAL.list = yyDollar[2].list
}
case 338:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:2156
+ //line go.y:2218
{
yyVAL.list = list1(Nod(ODCLFIELD, nil, typenod(yyDollar[1].typ)))
}
case 339:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:2166
+ //line go.y:2228
{
yyVAL.node = nodlit(yyDollar[1].val)
}
case 340:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:2170
+ //line go.y:2232
{
yyVAL.node = nodlit(yyDollar[2].val)
switch yyVAL.node.Val.Ctype {
case CTINT, CTRUNE:
- mpnegfix(yyVAL.node.Val.U.Xval)
+ mpnegfix(yyVAL.node.Val.U.(*Mpint))
break
case CTFLT:
- mpnegflt(yyVAL.node.Val.U.Fval)
+ mpnegflt(yyVAL.node.Val.U.(*Mpflt))
break
case CTCPLX:
- mpnegflt(&yyVAL.node.Val.U.Cval.Real)
- mpnegflt(&yyVAL.node.Val.U.Cval.Imag)
+ mpnegflt(&yyVAL.node.Val.U.(*Mpcplx).Real)
+ mpnegflt(&yyVAL.node.Val.U.(*Mpcplx).Imag)
break
default:
Yyerror("bad negated constant")
@@ -3418,7 +3451,7 @@ yydefault:
}
case 341:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:2188
+ //line go.y:2250
{
yyVAL.node = oldname(Pkglookup(yyDollar[1].sym.Name, builtinpkg))
if yyVAL.node.Op != OLITERAL {
@@ -3427,50 +3460,50 @@ yydefault:
}
case 343:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:2198
+ //line go.y:2260
{
if yyDollar[2].node.Val.Ctype == CTRUNE && yyDollar[4].node.Val.Ctype == CTINT {
yyVAL.node = yyDollar[2].node
- mpaddfixfix(yyDollar[2].node.Val.U.Xval, yyDollar[4].node.Val.U.Xval, 0)
+ mpaddfixfix(yyDollar[2].node.Val.U.(*Mpint), yyDollar[4].node.Val.U.(*Mpint), 0)
break
}
- yyDollar[4].node.Val.U.Cval.Real = yyDollar[4].node.Val.U.Cval.Imag
- Mpmovecflt(&yyDollar[4].node.Val.U.Cval.Imag, 0.0)
+ yyDollar[4].node.Val.U.(*Mpcplx).Real = yyDollar[4].node.Val.U.(*Mpcplx).Imag
+ Mpmovecflt(&yyDollar[4].node.Val.U.(*Mpcplx).Imag, 0.0)
yyVAL.node = nodcplxlit(yyDollar[2].node.Val, yyDollar[4].node.Val)
}
case 346:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:2214
+ //line go.y:2276
{
yyVAL.list = list1(yyDollar[1].node)
}
case 347:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:2218
+ //line go.y:2280
{
yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
}
case 348:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:2224
+ //line go.y:2286
{
yyVAL.list = list1(yyDollar[1].node)
}
case 349:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:2228
+ //line go.y:2290
{
yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
}
case 350:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:2234
+ //line go.y:2296
{
yyVAL.list = list1(yyDollar[1].node)
}
case 351:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:2238
+ //line go.y:2300
{
yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
}
diff --git a/src/cmd/internal/gc/y.output b/src/cmd/compile/internal/gc/y.output
similarity index 91%
rename from src/cmd/internal/gc/y.output
rename to src/cmd/compile/internal/gc/y.output
index e4a5e5c212..2821702aea 100644
--- a/src/cmd/internal/gc/y.output
+++ b/src/cmd/compile/internal/gc/y.output
@@ -3,7 +3,7 @@ state 0
$accept: .file $end
$$4: . (4)
- . reduce 4 (src line 148)
+ . reduce 4 (src line 210)
file goto 1
loadsys goto 2
@@ -21,7 +21,7 @@ state 2
package: . (2)
LPACKAGE shift 5
- . reduce 2 (src line 131)
+ . reduce 2 (src line 193)
package goto 4
@@ -37,7 +37,7 @@ state 4
file: loadsys package.imports xdcl_list
imports: . (6)
- . reduce 6 (src line 165)
+ . reduce 6 (src line 227)
imports goto 8
@@ -56,7 +56,7 @@ state 6
loadsys: $$4 import_package.import_there
$$21: . (21)
- . reduce 21 (src line 272)
+ . reduce 21 (src line 334)
import_there goto 14
$$21 goto 15
@@ -74,7 +74,7 @@ state 8
xdcl_list: . (218)
LIMPORT shift 19
- . reduce 218 (src line 1507)
+ . reduce 218 (src line 1569)
xdcl_list goto 17
import goto 18
@@ -89,19 +89,19 @@ state 9
state 10
sym: LNAME. (157)
- . reduce 157 (src line 1113)
+ . reduce 157 (src line 1175)
state 11
sym: hidden_importsym. (158)
- . reduce 158 (src line 1122)
+ . reduce 158 (src line 1184)
state 12
sym: '?'. (159)
- . reduce 159 (src line 1123)
+ . reduce 159 (src line 1185)
state 13
@@ -115,14 +115,14 @@ state 13
state 14
loadsys: $$4 import_package import_there. (5)
- . reduce 5 (src line 159)
+ . reduce 5 (src line 221)
state 15
import_there: $$21.hidden_import_list '$' '$'
hidden_import_list: . (344)
- . reduce 344 (src line 2209)
+ . reduce 344 (src line 2271)
hidden_import_list goto 22
@@ -131,7 +131,7 @@ state 16
import_safety: . (19)
LNAME shift 24
- . reduce 19 (src line 264)
+ . reduce 19 (src line 326)
import_safety goto 23
@@ -140,7 +140,7 @@ state 17
xdcl_list: xdcl_list.xdcl ';'
xdcl: . (23)
- $end reduce 1 (src line 122)
+ $end reduce 1 (src line 184)
error shift 29
LLITERAL shift 68
LBREAK shift 41
@@ -170,7 +170,7 @@ state 17
'*' shift 58
'&' shift 59
'(' shift 67
- ';' reduce 23 (src line 285)
+ ';' reduce 23 (src line 347)
'!' shift 62
'~' shift 63
'[' shift 77
@@ -236,7 +236,7 @@ state 19
state 20
package: LPACKAGE sym ';'. (3)
- . reduce 3 (src line 138)
+ . reduce 3 (src line 200)
state 21
@@ -271,7 +271,7 @@ state 23
state 24
import_safety: LNAME. (20)
- . reduce 20 (src line 265)
+ . reduce 20 (src line 327)
state 25
@@ -284,25 +284,25 @@ state 25
state 26
xdcl: common_dcl. (24)
- . reduce 24 (src line 290)
+ . reduce 24 (src line 352)
state 27
xdcl: xfndcl. (25)
- . reduce 25 (src line 291)
+ . reduce 25 (src line 353)
state 28
xdcl: non_dcl_stmt. (26)
- . reduce 26 (src line 295)
+ . reduce 26 (src line 357)
state 29
xdcl: error. (27)
- . reduce 27 (src line 300)
+ . reduce 27 (src line 362)
state 30
@@ -373,31 +373,31 @@ state 33
state 34
non_dcl_stmt: simple_stmt. (256)
- . reduce 256 (src line 1734)
+ . reduce 256 (src line 1796)
state 35
non_dcl_stmt: for_stmt. (257)
- . reduce 257 (src line 1736)
+ . reduce 257 (src line 1798)
state 36
non_dcl_stmt: switch_stmt. (258)
- . reduce 258 (src line 1737)
+ . reduce 258 (src line 1799)
state 37
non_dcl_stmt: select_stmt. (259)
- . reduce 259 (src line 1738)
+ . reduce 259 (src line 1800)
state 38
non_dcl_stmt: if_stmt. (260)
- . reduce 260 (src line 1739)
+ . reduce 260 (src line 1801)
state 39
@@ -410,7 +410,7 @@ state 39
state 40
non_dcl_stmt: LFALL. (263)
- . reduce 263 (src line 1756)
+ . reduce 263 (src line 1818)
state 41
@@ -420,7 +420,7 @@ state 41
LNAME shift 10
'?' shift 12
'@' shift 13
- . reduce 155 (src line 1107)
+ . reduce 155 (src line 1169)
sym goto 119
new_name goto 118
@@ -434,7 +434,7 @@ state 42
LNAME shift 10
'?' shift 12
'@' shift 13
- . reduce 155 (src line 1107)
+ . reduce 155 (src line 1169)
sym goto 119
new_name goto 118
@@ -538,7 +538,7 @@ state 46
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 292 (src line 1905)
+ . reduce 292 (src line 1967)
sym goto 123
expr goto 129
@@ -562,7 +562,7 @@ state 46
state 47
lconst: LCONST. (38)
- . reduce 38 (src line 354)
+ . reduce 38 (src line 416)
state 48
@@ -593,7 +593,7 @@ state 48
expr_list: expr. (276)
LASOP shift 130
- LCOLAS reduce 276 (src line 1840)
+ LCOLAS reduce 276 (src line 1902)
LANDAND shift 134
LANDNOT shift 149
LCOMM shift 152
@@ -616,9 +616,9 @@ state 48
'/' shift 146
'%' shift 147
'&' shift 148
- '=' reduce 276 (src line 1840)
- ',' reduce 276 (src line 1840)
- . reduce 49 (src line 410)
+ '=' reduce 276 (src line 1902)
+ ',' reduce 276 (src line 1902)
+ . reduce 49 (src line 472)
state 49
@@ -636,7 +636,7 @@ state 50
for_stmt: LFOR.$$74 for_body
$$74: . (74)
- . reduce 74 (src line 659)
+ . reduce 74 (src line 721)
$$74 goto 156
@@ -644,7 +644,7 @@ state 51
switch_stmt: LSWITCH.$$88 if_header $$89 LBODY caseblock_list '}'
$$88: . (88)
- . reduce 88 (src line 754)
+ . reduce 88 (src line 816)
$$88 goto 157
@@ -652,7 +652,7 @@ state 52
select_stmt: LSELECT.$$91 LBODY caseblock_list '}'
$$91: . (91)
- . reduce 91 (src line 777)
+ . reduce 91 (src line 839)
$$91 goto 158
@@ -660,28 +660,28 @@ state 53
if_stmt: LIF.$$78 if_header $$79 loop_body $$80 elseif_list else
$$78: . (78)
- . reduce 78 (src line 688)
+ . reduce 78 (src line 750)
$$78 goto 159
state 54
labelname: new_name. (163)
- . reduce 163 (src line 1167)
+ . reduce 163 (src line 1229)
state 55
expr: uexpr. (93)
- . reduce 93 (src line 793)
+ . reduce 93 (src line 855)
state 56
new_name: sym. (153)
name: sym. (162)
- ':' reduce 153 (src line 1091)
- . reduce 162 (src line 1158)
+ ':' reduce 153 (src line 1153)
+ . reduce 162 (src line 1220)
state 57
@@ -699,7 +699,7 @@ state 57
'(' shift 160
'.' shift 161
'[' shift 162
- . reduce 114 (src line 877)
+ . reduce 114 (src line 939)
state 58
@@ -1027,7 +1027,7 @@ state 66
pexpr: pexpr_no_paren. (146)
'{' shift 171
- . reduce 146 (src line 1054)
+ . reduce 146 (src line 1116)
state 67
@@ -1078,19 +1078,19 @@ state 67
state 68
pexpr_no_paren: LLITERAL. (126)
- . reduce 126 (src line 941)
+ . reduce 126 (src line 1003)
state 69
pexpr_no_paren: name. (127)
- . reduce 127 (src line 946)
+ . reduce 127 (src line 1008)
state 70
pexpr_no_paren: pseudocall. (134)
- . reduce 134 (src line 984)
+ . reduce 134 (src line 1046)
state 71
@@ -1112,23 +1112,23 @@ state 72
state 73
pexpr_no_paren: fnliteral. (139)
- . reduce 139 (src line 1011)
+ . reduce 139 (src line 1073)
state 74
convtype: fntype. (181)
fnlitdcl: fntype. (215)
- '(' reduce 181 (src line 1220)
- . reduce 215 (src line 1484)
+ '(' reduce 181 (src line 1282)
+ . reduce 215 (src line 1546)
state 75
convtype: othertype. (182)
comptype: othertype. (183)
- '(' reduce 182 (src line 1222)
- . reduce 183 (src line 1224)
+ '(' reduce 182 (src line 1284)
+ . reduce 183 (src line 1286)
state 76
@@ -1167,7 +1167,7 @@ state 77
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 290 (src line 1899)
+ . reduce 290 (src line 1961)
sym goto 123
expr goto 188
@@ -1226,13 +1226,13 @@ state 79
state 80
othertype: structtype. (196)
- . reduce 196 (src line 1272)
+ . reduce 196 (src line 1334)
state 81
othertype: interfacetype. (197)
- . reduce 197 (src line 1273)
+ . reduce 197 (src line 1335)
state 82
@@ -1258,13 +1258,13 @@ state 83
state 84
imports: imports import ';'. (7)
- . reduce 7 (src line 166)
+ . reduce 7 (src line 228)
state 85
import: LIMPORT import_stmt. (8)
- . reduce 8 (src line 168)
+ . reduce 8 (src line 230)
state 86
@@ -1291,7 +1291,7 @@ state 87
$$21: . (21)
LPACKAGE shift 7
- . reduce 21 (src line 272)
+ . reduce 21 (src line 334)
import_package goto 204
import_there goto 205
@@ -1300,7 +1300,7 @@ state 87
state 88
import_here: LLITERAL. (15)
- . reduce 15 (src line 224)
+ . reduce 15 (src line 286)
state 89
@@ -1336,7 +1336,7 @@ state 92
state 93
hidden_import_list: hidden_import_list hidden_import. (345)
- . reduce 345 (src line 2210)
+ . reduce 345 (src line 2272)
state 94
@@ -1389,19 +1389,19 @@ state 98
state 99
import_package: LPACKAGE LNAME import_safety ';'. (18)
- . reduce 18 (src line 247)
+ . reduce 18 (src line 309)
state 100
xdcl_list: xdcl_list xdcl ';'. (219)
- . reduce 219 (src line 1511)
+ . reduce 219 (src line 1573)
state 101
common_dcl: LVAR vardcl. (28)
- . reduce 28 (src line 305)
+ . reduce 28 (src line 367)
state 102
@@ -1458,19 +1458,19 @@ state 103
state 104
dcl_name_list: dcl_name. (274)
- . reduce 274 (src line 1830)
+ . reduce 274 (src line 1892)
state 105
dcl_name: sym. (154)
- . reduce 154 (src line 1101)
+ . reduce 154 (src line 1163)
state 106
common_dcl: lconst constdcl. (31)
- . reduce 31 (src line 318)
+ . reduce 31 (src line 380)
state 107
@@ -1526,7 +1526,7 @@ state 108
state 109
common_dcl: LTYPE typedcl. (35)
- . reduce 35 (src line 341)
+ . reduce 35 (src line 403)
state 110
@@ -1577,7 +1577,7 @@ state 111
state 112
typedclname: sym. (47)
- . reduce 47 (src line 395)
+ . reduce 47 (src line 457)
state 113
@@ -1585,7 +1585,7 @@ state 113
fnbody: . (210)
'{' shift 242
- . reduce 210 (src line 1457)
+ . reduce 210 (src line 1519)
fnbody goto 241
@@ -1607,7 +1607,7 @@ state 114
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 249 (src line 1707)
+ . reduce 249 (src line 1769)
sym goto 247
ntype goto 249
@@ -1637,43 +1637,43 @@ state 116
non_dcl_stmt: labelname ':'.$$261 stmt
$$261: . (261)
- . reduce 261 (src line 1740)
+ . reduce 261 (src line 1802)
$$261 goto 252
state 117
non_dcl_stmt: LBREAK onew_name. (264)
- . reduce 264 (src line 1762)
+ . reduce 264 (src line 1824)
state 118
onew_name: new_name. (156)
- . reduce 156 (src line 1111)
+ . reduce 156 (src line 1173)
state 119
new_name: sym. (153)
- . reduce 153 (src line 1091)
+ . reduce 153 (src line 1153)
state 120
non_dcl_stmt: LCONTINUE onew_name. (265)
- . reduce 265 (src line 1766)
+ . reduce 265 (src line 1828)
state 121
pexpr_no_paren: pseudocall. (134)
non_dcl_stmt: LGO pseudocall. (266)
- '(' reduce 134 (src line 984)
- '.' reduce 134 (src line 984)
- '{' reduce 134 (src line 984)
- '[' reduce 134 (src line 984)
- . reduce 266 (src line 1770)
+ '(' reduce 134 (src line 1046)
+ '.' reduce 134 (src line 1046)
+ '{' reduce 134 (src line 1046)
+ '[' reduce 134 (src line 1046)
+ . reduce 266 (src line 1832)
state 122
@@ -1696,7 +1696,7 @@ state 122
state 123
name: sym. (162)
- . reduce 162 (src line 1158)
+ . reduce 162 (src line 1220)
state 124
@@ -1710,23 +1710,23 @@ state 125
pexpr_no_paren: pseudocall. (134)
non_dcl_stmt: LDEFER pseudocall. (267)
- '(' reduce 134 (src line 984)
- '.' reduce 134 (src line 984)
- '{' reduce 134 (src line 984)
- '[' reduce 134 (src line 984)
- . reduce 267 (src line 1774)
+ '(' reduce 134 (src line 1046)
+ '.' reduce 134 (src line 1046)
+ '{' reduce 134 (src line 1046)
+ '[' reduce 134 (src line 1046)
+ . reduce 267 (src line 1836)
state 126
non_dcl_stmt: LGOTO new_name. (268)
- . reduce 268 (src line 1778)
+ . reduce 268 (src line 1840)
state 127
non_dcl_stmt: LRETURN oexpr_list. (269)
- . reduce 269 (src line 1783)
+ . reduce 269 (src line 1845)
state 128
@@ -1734,7 +1734,7 @@ state 128
oexpr_list: expr_list. (293)
',' shift 155
- . reduce 293 (src line 1909)
+ . reduce 293 (src line 1971)
state 129
@@ -1780,7 +1780,7 @@ state 129
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 276 (src line 1840)
+ . reduce 276 (src line 1902)
state 130
@@ -1827,13 +1827,13 @@ state 130
state 131
simple_stmt: expr LINC. (53)
- . reduce 53 (src line 460)
+ . reduce 53 (src line 522)
state 132
simple_stmt: expr LDEC. (54)
- . reduce 54 (src line 466)
+ . reduce 54 (src line 528)
state 133
@@ -2805,7 +2805,7 @@ state 156
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 294 (src line 1911)
+ . reduce 294 (src line 1973)
sym goto 123
expr goto 48
@@ -2853,7 +2853,7 @@ state 157
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 294 (src line 1911)
+ . reduce 294 (src line 1973)
sym goto 123
expr goto 48
@@ -2906,7 +2906,7 @@ state 159
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 294 (src line 1911)
+ . reduce 294 (src line 1973)
sym goto 123
expr goto 48
@@ -3016,7 +3016,7 @@ state 162
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 290 (src line 1899)
+ . reduce 290 (src line 1961)
sym goto 123
expr goto 294
@@ -3039,56 +3039,56 @@ state 162
state 163
uexpr: '*' uexpr. (115)
- . reduce 115 (src line 879)
+ . reduce 115 (src line 941)
state 164
uexpr: '&' uexpr. (116)
- . reduce 116 (src line 883)
+ . reduce 116 (src line 945)
state 165
uexpr: '+' uexpr. (117)
- . reduce 117 (src line 894)
+ . reduce 117 (src line 956)
state 166
uexpr: '-' uexpr. (118)
- . reduce 118 (src line 898)
+ . reduce 118 (src line 960)
state 167
uexpr: '!' uexpr. (119)
- . reduce 119 (src line 902)
+ . reduce 119 (src line 964)
state 168
uexpr: '~' uexpr. (120)
- . reduce 120 (src line 906)
+ . reduce 120 (src line 968)
state 169
uexpr: '^' uexpr. (121)
- . reduce 121 (src line 911)
+ . reduce 121 (src line 973)
state 170
uexpr: LCOMM uexpr. (122)
- . reduce 122 (src line 915)
+ . reduce 122 (src line 977)
state 171
pexpr_no_paren: pexpr_no_paren '{'.start_complit braced_keyval_list '}'
start_complit: . (140)
- . reduce 140 (src line 1013)
+ . reduce 140 (src line 1075)
start_complit goto 296
@@ -3143,19 +3143,19 @@ state 173
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 148 (src line 1069)
+ . reduce 148 (src line 1131)
state 174
expr_or_type: non_expr_type. (149)
- . reduce 149 (src line 1071)
+ . reduce 149 (src line 1133)
state 175
non_expr_type: recvchantype. (172)
- . reduce 172 (src line 1201)
+ . reduce 172 (src line 1263)
state 176
@@ -3163,11 +3163,11 @@ state 176
convtype: fntype. (181)
fnlitdcl: fntype. (215)
- error reduce 215 (src line 1484)
- LBODY reduce 215 (src line 1484)
- '(' reduce 181 (src line 1220)
- '{' reduce 215 (src line 1484)
- . reduce 173 (src line 1203)
+ error reduce 215 (src line 1546)
+ LBODY reduce 215 (src line 1546)
+ '(' reduce 181 (src line 1282)
+ '{' reduce 215 (src line 1546)
+ . reduce 173 (src line 1265)
state 177
@@ -3175,10 +3175,10 @@ state 177
convtype: othertype. (182)
comptype: othertype. (183)
- LBODY reduce 183 (src line 1224)
- '(' reduce 182 (src line 1222)
- '{' reduce 183 (src line 1224)
- . reduce 174 (src line 1204)
+ LBODY reduce 183 (src line 1286)
+ '(' reduce 182 (src line 1284)
+ '{' reduce 183 (src line 1286)
+ . reduce 174 (src line 1266)
state 178
@@ -3310,20 +3310,20 @@ state 181
pexpr_no_paren: comptype lbrace.start_complit braced_keyval_list '}'
start_complit: . (140)
- . reduce 140 (src line 1013)
+ . reduce 140 (src line 1075)
start_complit goto 301
state 182
lbrace: LBODY. (151)
- . reduce 151 (src line 1076)
+ . reduce 151 (src line 1138)
state 183
lbrace: '{'. (152)
- . reduce 152 (src line 1081)
+ . reduce 152 (src line 1143)
state 184
@@ -3359,9 +3359,9 @@ state 184
'*' shift 58
'&' shift 59
'(' shift 67
- ';' reduce 251 (src line 1719)
+ ';' reduce 251 (src line 1781)
'{' shift 308
- '}' reduce 251 (src line 1719)
+ '}' reduce 251 (src line 1781)
'!' shift 62
'~' shift 63
'[' shift 77
@@ -3403,7 +3403,7 @@ state 184
state 185
fnliteral: fnlitdcl error. (217)
- . reduce 217 (src line 1496)
+ . reduce 217 (src line 1558)
state 186
@@ -3463,13 +3463,13 @@ state 188
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 291 (src line 1903)
+ . reduce 291 (src line 1965)
state 189
othertype: LCHAN non_recvchantype. (193)
- . reduce 193 (src line 1258)
+ . reduce 193 (src line 1320)
state 190
@@ -3504,25 +3504,25 @@ state 190
state 191
non_recvchantype: fntype. (176)
- . reduce 176 (src line 1210)
+ . reduce 176 (src line 1272)
state 192
non_recvchantype: othertype. (177)
- . reduce 177 (src line 1212)
+ . reduce 177 (src line 1274)
state 193
non_recvchantype: ptrtype. (178)
- . reduce 178 (src line 1213)
+ . reduce 178 (src line 1275)
state 194
non_recvchantype: dotname. (179)
- . reduce 179 (src line 1214)
+ . reduce 179 (src line 1276)
state 195
@@ -3588,7 +3588,7 @@ state 197
dotname: name.'.' sym
'.' shift 314
- . reduce 189 (src line 1234)
+ . reduce 189 (src line 1296)
state 198
@@ -3665,27 +3665,27 @@ state 201
osemi: . (286)
';' shift 333
- . reduce 286 (src line 1893)
+ . reduce 286 (src line 1955)
osemi goto 332
state 202
import: LIMPORT '(' ')'. (10)
- . reduce 10 (src line 171)
+ . reduce 10 (src line 233)
state 203
import_stmt_list: import_stmt. (13)
- . reduce 13 (src line 220)
+ . reduce 13 (src line 282)
state 204
import_stmt: import_here import_package.import_there
$$21: . (21)
- . reduce 21 (src line 272)
+ . reduce 21 (src line 334)
import_there goto 334
$$21 goto 15
@@ -3693,37 +3693,37 @@ state 204
state 205
import_stmt: import_here import_there. (12)
- . reduce 12 (src line 209)
+ . reduce 12 (src line 271)
state 206
import_here: sym LLITERAL. (16)
- . reduce 16 (src line 232)
+ . reduce 16 (src line 294)
state 207
import_here: '.' LLITERAL. (17)
- . reduce 17 (src line 239)
+ . reduce 17 (src line 301)
state 208
hidden_importsym: '@' LLITERAL '.' LNAME. (160)
- . reduce 160 (src line 1128)
+ . reduce 160 (src line 1190)
state 209
hidden_importsym: '@' LLITERAL '.' '?'. (161)
- . reduce 161 (src line 1143)
+ . reduce 161 (src line 1205)
state 210
import_there: $$21 hidden_import_list '$' '$'. (22)
- . reduce 22 (src line 276)
+ . reduce 22 (src line 338)
state 211
@@ -3757,7 +3757,7 @@ state 212
state 213
hidden_pkg_importsym: hidden_importsym. (310)
- . reduce 310 (src line 1985)
+ . reduce 310 (src line 2047)
state 214
@@ -3807,7 +3807,7 @@ state 215
state 216
hidden_pkgtype: hidden_pkg_importsym. (311)
- . reduce 311 (src line 1992)
+ . reduce 311 (src line 2054)
state 217
@@ -3815,7 +3815,7 @@ state 217
fnbody: . (210)
'{' shift 242
- . reduce 210 (src line 1457)
+ . reduce 210 (src line 1519)
fnbody goto 353
@@ -3845,20 +3845,20 @@ state 220
osemi: . (286)
';' shift 359
- . reduce 286 (src line 1893)
+ . reduce 286 (src line 1955)
osemi goto 358
state 221
common_dcl: LVAR '(' ')'. (30)
- . reduce 30 (src line 314)
+ . reduce 30 (src line 376)
state 222
vardcl_list: vardcl. (220)
- . reduce 220 (src line 1523)
+ . reduce 220 (src line 1585)
state 223
@@ -3866,7 +3866,7 @@ state 223
vardcl: dcl_name_list ntype.'=' expr_list
'=' shift 360
- . reduce 39 (src line 360)
+ . reduce 39 (src line 422)
state 224
@@ -3926,31 +3926,31 @@ state 225
state 226
ntype: recvchantype. (166)
- . reduce 166 (src line 1190)
+ . reduce 166 (src line 1252)
state 227
ntype: fntype. (167)
- . reduce 167 (src line 1192)
+ . reduce 167 (src line 1254)
state 228
ntype: othertype. (168)
- . reduce 168 (src line 1193)
+ . reduce 168 (src line 1255)
state 229
ntype: ptrtype. (169)
- . reduce 169 (src line 1194)
+ . reduce 169 (src line 1256)
state 230
ntype: dotname. (170)
- . reduce 170 (src line 1195)
+ . reduce 170 (src line 1257)
state 231
@@ -3995,14 +3995,14 @@ state 233
osemi: . (286)
';' shift 366
- . reduce 286 (src line 1893)
+ . reduce 286 (src line 1955)
osemi goto 365
state 234
common_dcl: lconst '(' ')'. (34)
- . reduce 34 (src line 336)
+ . reduce 34 (src line 398)
state 235
@@ -4060,32 +4060,32 @@ state 237
osemi: . (286)
';' shift 370
- . reduce 286 (src line 1893)
+ . reduce 286 (src line 1955)
osemi goto 369
state 238
common_dcl: LTYPE '(' ')'. (37)
- . reduce 37 (src line 349)
+ . reduce 37 (src line 411)
state 239
typedcl_list: typedcl. (224)
- . reduce 224 (src line 1537)
+ . reduce 224 (src line 1599)
state 240
typedcl: typedclname ntype. (48)
- . reduce 48 (src line 404)
+ . reduce 48 (src line 466)
state 241
xfndcl: LFUNC fndcl fnbody. (204)
- . reduce 204 (src line 1318)
+ . reduce 204 (src line 1380)
state 242
@@ -4121,9 +4121,9 @@ state 242
'*' shift 58
'&' shift 59
'(' shift 67
- ';' reduce 251 (src line 1719)
+ ';' reduce 251 (src line 1781)
'{' shift 308
- '}' reduce 251 (src line 1719)
+ '}' reduce 251 (src line 1781)
'!' shift 62
'~' shift 63
'[' shift 77
@@ -4176,20 +4176,20 @@ state 244
ocomma: . (288)
',' shift 373
- . reduce 288 (src line 1896)
+ . reduce 288 (src line 1958)
ocomma goto 374
state 245
arg_type_list: arg_type. (247)
- . reduce 247 (src line 1697)
+ . reduce 247 (src line 1759)
state 246
arg_type: name_or_type. (243)
- . reduce 243 (src line 1681)
+ . reduce 243 (src line 1743)
state 247
@@ -4210,7 +4210,7 @@ state 247
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 162 (src line 1158)
+ . reduce 162 (src line 1220)
sym goto 123
ntype goto 249
@@ -4229,13 +4229,13 @@ state 247
state 248
arg_type: dotdotdot. (246)
- . reduce 246 (src line 1695)
+ . reduce 246 (src line 1757)
state 249
name_or_type: ntype. (150)
- . reduce 150 (src line 1073)
+ . reduce 150 (src line 1135)
state 250
@@ -4254,7 +4254,7 @@ state 250
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 164 (src line 1179)
+ . reduce 164 (src line 1241)
sym goto 123
ntype goto 377
@@ -4285,7 +4285,7 @@ state 251
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 249 (src line 1707)
+ . reduce 249 (src line 1769)
sym goto 247
ntype goto 249
@@ -4311,11 +4311,11 @@ state 252
error shift 307
LLITERAL shift 68
LBREAK shift 41
- LCASE reduce 251 (src line 1719)
+ LCASE reduce 251 (src line 1781)
LCHAN shift 78
LCONST shift 47
LCONTINUE shift 42
- LDEFAULT reduce 251 (src line 1719)
+ LDEFAULT reduce 251 (src line 1781)
LDEFER shift 44
LFALL shift 40
LFOR shift 50
@@ -4339,9 +4339,9 @@ state 252
'*' shift 58
'&' shift 59
'(' shift 67
- ';' reduce 251 (src line 1719)
+ ';' reduce 251 (src line 1781)
'{' shift 308
- '}' reduce 251 (src line 1719)
+ '}' reduce 251 (src line 1781)
'!' shift 62
'~' shift 63
'[' shift 77
@@ -4396,7 +4396,7 @@ state 253
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 249 (src line 1707)
+ . reduce 249 (src line 1769)
sym goto 247
ntype goto 249
@@ -4458,7 +4458,7 @@ state 254
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 50 (src line 425)
+ . reduce 50 (src line 487)
state 255
@@ -4502,7 +4502,7 @@ state 255
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 94 (src line 795)
+ . reduce 94 (src line 857)
state 256
@@ -4545,7 +4545,7 @@ state 256
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 95 (src line 799)
+ . reduce 95 (src line 861)
state 257
@@ -4582,7 +4582,7 @@ state 257
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 96 (src line 803)
+ . reduce 96 (src line 865)
state 258
@@ -4619,7 +4619,7 @@ state 258
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 97 (src line 807)
+ . reduce 97 (src line 869)
state 259
@@ -4656,7 +4656,7 @@ state 259
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 98 (src line 811)
+ . reduce 98 (src line 873)
state 260
@@ -4693,7 +4693,7 @@ state 260
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 99 (src line 815)
+ . reduce 99 (src line 877)
state 261
@@ -4730,7 +4730,7 @@ state 261
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 100 (src line 819)
+ . reduce 100 (src line 881)
state 262
@@ -4767,7 +4767,7 @@ state 262
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 101 (src line 823)
+ . reduce 101 (src line 885)
state 263
@@ -4800,7 +4800,7 @@ state 263
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 102 (src line 827)
+ . reduce 102 (src line 889)
state 264
@@ -4833,7 +4833,7 @@ state 264
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 103 (src line 831)
+ . reduce 103 (src line 893)
state 265
@@ -4866,7 +4866,7 @@ state 265
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 104 (src line 835)
+ . reduce 104 (src line 897)
state 266
@@ -4899,7 +4899,7 @@ state 266
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 105 (src line 839)
+ . reduce 105 (src line 901)
state 267
@@ -4925,7 +4925,7 @@ state 267
expr: expr.LRSH expr
expr: expr.LCOMM expr
- . reduce 106 (src line 843)
+ . reduce 106 (src line 905)
state 268
@@ -4951,7 +4951,7 @@ state 268
expr: expr.LRSH expr
expr: expr.LCOMM expr
- . reduce 107 (src line 847)
+ . reduce 107 (src line 909)
state 269
@@ -4977,7 +4977,7 @@ state 269
expr: expr.LRSH expr
expr: expr.LCOMM expr
- . reduce 108 (src line 851)
+ . reduce 108 (src line 913)
state 270
@@ -5003,7 +5003,7 @@ state 270
expr: expr.LRSH expr
expr: expr.LCOMM expr
- . reduce 109 (src line 855)
+ . reduce 109 (src line 917)
state 271
@@ -5029,7 +5029,7 @@ state 271
expr: expr.LRSH expr
expr: expr.LCOMM expr
- . reduce 110 (src line 859)
+ . reduce 110 (src line 921)
state 272
@@ -5055,7 +5055,7 @@ state 272
expr: expr.LRSH expr
expr: expr.LCOMM expr
- . reduce 111 (src line 863)
+ . reduce 111 (src line 925)
state 273
@@ -5081,7 +5081,7 @@ state 273
expr: expr LRSH expr. (112)
expr: expr.LCOMM expr
- . reduce 112 (src line 867)
+ . reduce 112 (src line 929)
state 274
@@ -5126,7 +5126,7 @@ state 274
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 113 (src line 872)
+ . reduce 113 (src line 934)
state 275
@@ -5134,7 +5134,7 @@ state 275
expr_list: expr_list.',' expr
',' shift 155
- . reduce 51 (src line 430)
+ . reduce 51 (src line 492)
state 276
@@ -5142,7 +5142,7 @@ state 276
expr_list: expr_list.',' expr
',' shift 155
- . reduce 52 (src line 442)
+ . reduce 52 (src line 504)
state 277
@@ -5188,13 +5188,13 @@ state 277
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 277 (src line 1845)
+ . reduce 277 (src line 1907)
state 278
for_stmt: LFOR $$74 for_body. (75)
- . reduce 75 (src line 664)
+ . reduce 75 (src line 726)
state 279
@@ -5210,19 +5210,19 @@ state 280
for_header: osimple_stmt. (71)
';' shift 383
- . reduce 71 (src line 644)
+ . reduce 71 (src line 706)
state 281
for_header: range_stmt. (72)
- . reduce 72 (src line 650)
+ . reduce 72 (src line 712)
state 282
osimple_stmt: simple_stmt. (295)
- . reduce 295 (src line 1915)
+ . reduce 295 (src line 1977)
state 283
@@ -5283,7 +5283,7 @@ state 285
switch_stmt: LSWITCH $$88 if_header.$$89 LBODY caseblock_list '}'
$$89: . (89)
- . reduce 89 (src line 759)
+ . reduce 89 (src line 821)
$$89 goto 387
@@ -5292,14 +5292,14 @@ state 286
if_header: osimple_stmt.';' osimple_stmt
';' shift 388
- . reduce 76 (src line 670)
+ . reduce 76 (src line 732)
state 287
select_stmt: LSELECT $$91 LBODY.caseblock_list '}'
caseblock_list: . (63)
- . reduce 63 (src line 590)
+ . reduce 63 (src line 652)
caseblock_list goto 389
@@ -5307,14 +5307,14 @@ state 288
if_stmt: LIF $$78 if_header.$$79 loop_body $$80 elseif_list else
$$79: . (79)
- . reduce 79 (src line 693)
+ . reduce 79 (src line 755)
$$79 goto 390
state 289
pseudocall: pexpr '(' ')'. (123)
- . reduce 123 (src line 924)
+ . reduce 123 (src line 986)
state 290
@@ -5325,20 +5325,20 @@ state 290
LDDD shift 392
',' shift 393
- . reduce 288 (src line 1896)
+ . reduce 288 (src line 1958)
ocomma goto 391
state 291
expr_or_type_list: expr_or_type. (278)
- . reduce 278 (src line 1850)
+ . reduce 278 (src line 1912)
state 292
pexpr_no_paren: pexpr '.' sym. (128)
- . reduce 128 (src line 947)
+ . reduce 128 (src line 1009)
state 293
@@ -5432,7 +5432,7 @@ state 294
'%' shift 147
'&' shift 148
']' shift 396
- . reduce 291 (src line 1903)
+ . reduce 291 (src line 1965)
state 295
@@ -5467,7 +5467,7 @@ state 296
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 284 (src line 1881)
+ . reduce 284 (src line 1943)
sym goto 123
expr goto 402
@@ -5495,13 +5495,13 @@ state 297
pexpr: '(' expr_or_type ')'. (147)
'{' shift 404
- . reduce 147 (src line 1056)
+ . reduce 147 (src line 1118)
state 298
non_expr_type: '*' non_expr_type. (175)
- . reduce 175 (src line 1205)
+ . reduce 175 (src line 1267)
state 299
@@ -5581,7 +5581,7 @@ state 300
'%' shift 147
'&' shift 148
',' shift 413
- . reduce 288 (src line 1896)
+ . reduce 288 (src line 1958)
ocomma goto 412
@@ -5609,7 +5609,7 @@ state 301
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 284 (src line 1881)
+ . reduce 284 (src line 1943)
sym goto 123
expr goto 402
@@ -5644,38 +5644,38 @@ state 302
state 303
stmt_list: stmt. (270)
- . reduce 270 (src line 1804)
+ . reduce 270 (src line 1866)
state 304
stmt: compound_stmt. (252)
- . reduce 252 (src line 1723)
+ . reduce 252 (src line 1785)
state 305
stmt: common_dcl. (253)
- . reduce 253 (src line 1724)
+ . reduce 253 (src line 1786)
state 306
stmt: non_dcl_stmt. (254)
- . reduce 254 (src line 1728)
+ . reduce 254 (src line 1790)
state 307
stmt: error. (255)
- . reduce 255 (src line 1729)
+ . reduce 255 (src line 1791)
state 308
compound_stmt: '{'.$$59 stmt_list '}'
$$59: . (59)
- . reduce 59 (src line 544)
+ . reduce 59 (src line 606)
$$59 goto 417
@@ -5740,7 +5740,7 @@ state 310
state 311
othertype: LCHAN LCOMM ntype. (194)
- . reduce 194 (src line 1263)
+ . reduce 194 (src line 1325)
state 312
@@ -5753,7 +5753,7 @@ state 312
state 313
ptrtype: '*' ntype. (198)
- . reduce 198 (src line 1275)
+ . reduce 198 (src line 1337)
state 314
@@ -5780,20 +5780,20 @@ state 316
osemi: . (286)
';' shift 424
- . reduce 286 (src line 1893)
+ . reduce 286 (src line 1955)
osemi goto 423
state 317
structtype: LSTRUCT lbrace '}'. (201)
- . reduce 201 (src line 1295)
+ . reduce 201 (src line 1357)
state 318
structdcl_list: structdcl. (226)
- . reduce 226 (src line 1547)
+ . reduce 226 (src line 1609)
state 319
@@ -5832,7 +5832,7 @@ state 320
oliteral: . (302)
LLITERAL shift 428
- . reduce 302 (src line 1935)
+ . reduce 302 (src line 1997)
oliteral goto 427
@@ -5861,13 +5861,13 @@ state 322
state 323
new_name_list: new_name. (272)
- . reduce 272 (src line 1820)
+ . reduce 272 (src line 1882)
state 324
embed: packname. (238)
- . reduce 238 (src line 1646)
+ . reduce 238 (src line 1708)
state 325
@@ -5875,11 +5875,11 @@ state 325
packname: LNAME. (236)
packname: LNAME.'.' sym
- LLITERAL reduce 236 (src line 1621)
- ';' reduce 236 (src line 1621)
+ LLITERAL reduce 236 (src line 1683)
+ ';' reduce 236 (src line 1683)
'.' shift 434
- '}' reduce 236 (src line 1621)
- . reduce 157 (src line 1113)
+ '}' reduce 236 (src line 1683)
+ . reduce 157 (src line 1175)
state 326
@@ -5888,20 +5888,20 @@ state 326
osemi: . (286)
';' shift 436
- . reduce 286 (src line 1893)
+ . reduce 286 (src line 1955)
osemi goto 435
state 327
interfacetype: LINTERFACE lbrace '}'. (203)
- . reduce 203 (src line 1308)
+ . reduce 203 (src line 1370)
state 328
interfacedcl_list: interfacedcl. (228)
- . reduce 228 (src line 1554)
+ . reduce 228 (src line 1616)
state 329
@@ -5915,7 +5915,7 @@ state 329
state 330
interfacedcl: packname. (240)
- . reduce 240 (src line 1658)
+ . reduce 240 (src line 1720)
state 331
@@ -5942,7 +5942,7 @@ state 333
'.' shift 90
'?' shift 12
'@' shift 13
- . reduce 287 (src line 1894)
+ . reduce 287 (src line 1956)
import_here goto 87
sym goto 89
@@ -5952,7 +5952,7 @@ state 333
state 334
import_stmt: import_here import_package import_there. (11)
- . reduce 11 (src line 173)
+ . reduce 11 (src line 235)
state 335
@@ -5972,31 +5972,31 @@ state 336
state 337
hidden_type: hidden_type_misc. (312)
- . reduce 312 (src line 2003)
+ . reduce 312 (src line 2065)
state 338
hidden_type: hidden_type_recv_chan. (313)
- . reduce 313 (src line 2005)
+ . reduce 313 (src line 2067)
state 339
hidden_type: hidden_type_func. (314)
- . reduce 314 (src line 2006)
+ . reduce 314 (src line 2068)
state 340
hidden_type_misc: hidden_importsym. (317)
- . reduce 317 (src line 2012)
+ . reduce 317 (src line 2074)
state 341
hidden_type_misc: LNAME. (318)
- . reduce 318 (src line 2017)
+ . reduce 318 (src line 2079)
state 342
@@ -6131,7 +6131,7 @@ state 354
LNAME shift 10
'?' shift 12
'@' shift 13
- . reduce 296 (src line 1917)
+ . reduce 296 (src line 1979)
sym goto 357
hidden_importsym goto 11
@@ -6151,7 +6151,7 @@ state 355
state 356
hidden_funarg_list: hidden_funarg. (346)
- . reduce 346 (src line 2212)
+ . reduce 346 (src line 2274)
state 357
@@ -6191,7 +6191,7 @@ state 359
LNAME shift 10
'?' shift 12
'@' shift 13
- . reduce 287 (src line 1894)
+ . reduce 287 (src line 1956)
sym goto 105
dcl_name goto 104
@@ -6246,13 +6246,13 @@ state 361
expr_list: expr_list.',' expr
',' shift 155
- . reduce 41 (src line 369)
+ . reduce 41 (src line 431)
state 362
dcl_name_list: dcl_name_list ',' dcl_name. (275)
- . reduce 275 (src line 1835)
+ . reduce 275 (src line 1897)
state 363
@@ -6305,7 +6305,7 @@ state 366
LNAME shift 10
'?' shift 12
'@' shift 13
- . reduce 287 (src line 1894)
+ . reduce 287 (src line 1956)
sym goto 105
dcl_name goto 104
@@ -6362,7 +6362,7 @@ state 368
expr_list: expr_list.',' expr
',' shift 155
- . reduce 43 (src line 379)
+ . reduce 43 (src line 441)
state 369
@@ -6379,7 +6379,7 @@ state 370
LNAME shift 10
'?' shift 12
'@' shift 13
- . reduce 287 (src line 1894)
+ . reduce 287 (src line 1956)
sym goto 112
typedclname goto 111
@@ -6412,7 +6412,7 @@ state 372
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 212 (src line 1469)
+ . reduce 212 (src line 1531)
sym goto 485
dotname goto 493
@@ -6444,7 +6444,7 @@ state 373
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 289 (src line 1897)
+ . reduce 289 (src line 1959)
sym goto 247
ntype goto 249
@@ -6464,25 +6464,25 @@ state 373
state 374
oarg_type_list_ocomma: arg_type_list ocomma. (250)
- . reduce 250 (src line 1711)
+ . reduce 250 (src line 1773)
state 375
arg_type: sym name_or_type. (244)
- . reduce 244 (src line 1683)
+ . reduce 244 (src line 1745)
state 376
arg_type: sym dotdotdot. (245)
- . reduce 245 (src line 1689)
+ . reduce 245 (src line 1751)
state 377
dotdotdot: LDDD ntype. (165)
- . reduce 165 (src line 1185)
+ . reduce 165 (src line 1247)
state 378
@@ -6495,7 +6495,7 @@ state 378
state 379
non_dcl_stmt: labelname ':' $$261 stmt. (262)
- . reduce 262 (src line 1745)
+ . reduce 262 (src line 1807)
state 380
@@ -6508,14 +6508,14 @@ state 380
state 381
for_body: for_header loop_body. (73)
- . reduce 73 (src line 652)
+ . reduce 73 (src line 714)
state 382
loop_body: LBODY.$$65 stmt_list '}'
$$65: . (65)
- . reduce 65 (src line 599)
+ . reduce 65 (src line 661)
$$65 goto 497
@@ -6542,7 +6542,7 @@ state 383
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 294 (src line 1911)
+ . reduce 294 (src line 1973)
sym goto 123
expr goto 48
@@ -6695,7 +6695,7 @@ state 386
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 69 (src line 624)
+ . reduce 69 (src line 686)
state 387
@@ -6728,7 +6728,7 @@ state 388
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 294 (src line 1911)
+ . reduce 294 (src line 1973)
sym goto 123
expr goto 48
@@ -6782,7 +6782,7 @@ state 392
ocomma: . (288)
',' shift 413
- . reduce 288 (src line 1896)
+ . reduce 288 (src line 1958)
ocomma goto 510
@@ -6809,7 +6809,7 @@ state 393
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 289 (src line 1897)
+ . reduce 289 (src line 1959)
sym goto 123
expr goto 173
@@ -6848,7 +6848,7 @@ state 395
state 396
pexpr_no_paren: pexpr '[' expr ']'. (131)
- . reduce 131 (src line 966)
+ . reduce 131 (src line 1028)
state 397
@@ -6875,7 +6875,7 @@ state 397
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 290 (src line 1899)
+ . reduce 290 (src line 1961)
sym goto 123
expr goto 188
@@ -6909,20 +6909,20 @@ state 399
ocomma: . (288)
',' shift 516
- . reduce 288 (src line 1896)
+ . reduce 288 (src line 1958)
ocomma goto 517
state 400
keyval_list: keyval. (280)
- . reduce 280 (src line 1863)
+ . reduce 280 (src line 1925)
state 401
keyval_list: bare_complitexpr. (281)
- . reduce 281 (src line 1868)
+ . reduce 281 (src line 1930)
state 402
@@ -6970,14 +6970,14 @@ state 402
'%' shift 147
'&' shift 148
':' shift 518
- . reduce 142 (src line 1026)
+ . reduce 142 (src line 1088)
state 403
bare_complitexpr: '{'.start_complit braced_keyval_list '}'
start_complit: . (140)
- . reduce 140 (src line 1013)
+ . reduce 140 (src line 1075)
start_complit goto 519
@@ -6985,7 +6985,7 @@ state 404
pexpr_no_paren: '(' expr_or_type ')' '{'.start_complit braced_keyval_list '}'
start_complit: . (140)
- . reduce 140 (src line 1013)
+ . reduce 140 (src line 1075)
start_complit goto 520
@@ -7022,47 +7022,47 @@ state 405
state 406
recvchantype: LCOMM LCHAN ntype. (199)
- . reduce 199 (src line 1281)
+ . reduce 199 (src line 1343)
state 407
ntype: fntype. (167)
non_recvchantype: fntype. (176)
- LBODY reduce 176 (src line 1210)
- '(' reduce 176 (src line 1210)
- '{' reduce 176 (src line 1210)
- . reduce 167 (src line 1192)
+ LBODY reduce 176 (src line 1272)
+ '(' reduce 176 (src line 1272)
+ '{' reduce 176 (src line 1272)
+ . reduce 167 (src line 1254)
state 408
ntype: othertype. (168)
non_recvchantype: othertype. (177)
- LBODY reduce 177 (src line 1212)
- '(' reduce 177 (src line 1212)
- '{' reduce 177 (src line 1212)
- . reduce 168 (src line 1193)
+ LBODY reduce 177 (src line 1274)
+ '(' reduce 177 (src line 1274)
+ '{' reduce 177 (src line 1274)
+ . reduce 168 (src line 1255)
state 409
ntype: ptrtype. (169)
non_recvchantype: ptrtype. (178)
- LBODY reduce 178 (src line 1213)
- '(' reduce 178 (src line 1213)
- '{' reduce 178 (src line 1213)
- . reduce 169 (src line 1194)
+ LBODY reduce 178 (src line 1275)
+ '(' reduce 178 (src line 1275)
+ '{' reduce 178 (src line 1275)
+ . reduce 169 (src line 1256)
state 410
ntype: dotname. (170)
non_recvchantype: dotname. (179)
- LBODY reduce 179 (src line 1214)
- '(' reduce 179 (src line 1214)
- '{' reduce 179 (src line 1214)
- . reduce 170 (src line 1195)
+ LBODY reduce 179 (src line 1276)
+ '(' reduce 179 (src line 1276)
+ '{' reduce 179 (src line 1276)
+ . reduce 170 (src line 1257)
state 411
@@ -7105,7 +7105,7 @@ state 412
state 413
ocomma: ','. (289)
- . reduce 289 (src line 1897)
+ . reduce 289 (src line 1959)
state 414
@@ -7118,7 +7118,7 @@ state 414
state 415
fnliteral: fnlitdcl lbrace stmt_list '}'. (216)
- . reduce 216 (src line 1490)
+ . reduce 216 (src line 1552)
state 416
@@ -7128,11 +7128,11 @@ state 416
error shift 307
LLITERAL shift 68
LBREAK shift 41
- LCASE reduce 251 (src line 1719)
+ LCASE reduce 251 (src line 1781)
LCHAN shift 78
LCONST shift 47
LCONTINUE shift 42
- LDEFAULT reduce 251 (src line 1719)
+ LDEFAULT reduce 251 (src line 1781)
LDEFER shift 44
LFALL shift 40
LFOR shift 50
@@ -7156,9 +7156,9 @@ state 416
'*' shift 58
'&' shift 59
'(' shift 67
- ';' reduce 251 (src line 1719)
+ ';' reduce 251 (src line 1781)
'{' shift 308
- '}' reduce 251 (src line 1719)
+ '}' reduce 251 (src line 1781)
'!' shift 62
'~' shift 63
'[' shift 77
@@ -7229,9 +7229,9 @@ state 417
'*' shift 58
'&' shift 59
'(' shift 67
- ';' reduce 251 (src line 1719)
+ ';' reduce 251 (src line 1781)
'{' shift 308
- '}' reduce 251 (src line 1719)
+ '}' reduce 251 (src line 1781)
'!' shift 62
'~' shift 63
'[' shift 77
@@ -7273,25 +7273,25 @@ state 417
state 418
othertype: '[' oexpr ']' ntype. (191)
- . reduce 191 (src line 1248)
+ . reduce 191 (src line 1310)
state 419
othertype: '[' LDDD ']' ntype. (192)
- . reduce 192 (src line 1253)
+ . reduce 192 (src line 1315)
state 420
non_recvchantype: '(' ntype ')'. (180)
- . reduce 180 (src line 1215)
+ . reduce 180 (src line 1277)
state 421
dotname: name '.' sym. (190)
- . reduce 190 (src line 1236)
+ . reduce 190 (src line 1298)
state 422
@@ -7339,7 +7339,7 @@ state 424
'(' shift 321
'?' shift 12
'@' shift 13
- . reduce 287 (src line 1894)
+ . reduce 287 (src line 1956)
sym goto 119
packname goto 324
@@ -7354,7 +7354,7 @@ state 425
oliteral: . (302)
LLITERAL shift 428
- . reduce 302 (src line 1935)
+ . reduce 302 (src line 1997)
oliteral goto 529
@@ -7373,13 +7373,13 @@ state 426
state 427
structdcl: embed oliteral. (231)
- . reduce 231 (src line 1589)
+ . reduce 231 (src line 1651)
state 428
oliteral: LLITERAL. (303)
- . reduce 303 (src line 1939)
+ . reduce 303 (src line 2001)
state 429
@@ -7403,7 +7403,7 @@ state 431
packname: LNAME.'.' sym
'.' shift 434
- . reduce 236 (src line 1621)
+ . reduce 236 (src line 1683)
state 432
@@ -7411,7 +7411,7 @@ state 432
oliteral: . (302)
LLITERAL shift 428
- . reduce 302 (src line 1935)
+ . reduce 302 (src line 1997)
oliteral goto 533
@@ -7450,7 +7450,7 @@ state 436
'(' shift 331
'?' shift 12
'@' shift 13
- . reduce 287 (src line 1894)
+ . reduce 287 (src line 1956)
sym goto 119
packname goto 330
@@ -7461,7 +7461,7 @@ state 436
state 437
interfacedcl: new_name indcl. (239)
- . reduce 239 (src line 1652)
+ . reduce 239 (src line 1714)
state 438
@@ -7481,7 +7481,7 @@ state 438
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 249 (src line 1707)
+ . reduce 249 (src line 1769)
sym goto 247
ntype goto 249
@@ -7510,25 +7510,25 @@ state 439
state 440
import: LIMPORT '(' import_stmt_list osemi ')'. (9)
- . reduce 9 (src line 170)
+ . reduce 9 (src line 232)
state 441
import_stmt_list: import_stmt_list ';' import_stmt. (14)
- . reduce 14 (src line 222)
+ . reduce 14 (src line 284)
state 442
hidden_import: LIMPORT LNAME LLITERAL ';'. (304)
- . reduce 304 (src line 1944)
+ . reduce 304 (src line 2006)
state 443
hidden_import: LVAR hidden_pkg_importsym hidden_type ';'. (305)
- . reduce 305 (src line 1949)
+ . reduce 305 (src line 2011)
state 444
@@ -7587,7 +7587,7 @@ state 447
LNAME shift 10
'?' shift 12
'@' shift 13
- . reduce 298 (src line 1923)
+ . reduce 298 (src line 1985)
sym goto 546
hidden_importsym goto 11
@@ -7610,7 +7610,7 @@ state 448
'[' shift 342
'?' shift 12
'@' shift 13
- . reduce 300 (src line 1929)
+ . reduce 300 (src line 1991)
sym goto 550
hidden_importsym goto 553
@@ -7625,13 +7625,13 @@ state 448
state 449
hidden_type_misc: '*' hidden_type. (324)
- . reduce 324 (src line 2048)
+ . reduce 324 (src line 2110)
state 450
hidden_type_misc: LCHAN hidden_type_non_recv_chan. (325)
- . reduce 325 (src line 2052)
+ . reduce 325 (src line 2114)
state 451
@@ -7666,13 +7666,13 @@ state 452
state 453
hidden_type_non_recv_chan: hidden_type_misc. (315)
- . reduce 315 (src line 2008)
+ . reduce 315 (src line 2070)
state 454
hidden_type_non_recv_chan: hidden_type_func. (316)
- . reduce 316 (src line 2010)
+ . reduce 316 (src line 2072)
state 455
@@ -7703,7 +7703,7 @@ state 456
LNAME shift 10
'?' shift 12
'@' shift 13
- . reduce 296 (src line 1917)
+ . reduce 296 (src line 1979)
sym goto 357
hidden_importsym goto 11
@@ -7721,7 +7721,7 @@ state 457
state 458
hidden_constant: hidden_literal. (342)
- . reduce 342 (src line 2195)
+ . reduce 342 (src line 2257)
state 459
@@ -7741,7 +7741,7 @@ state 459
state 460
hidden_literal: LLITERAL. (339)
- . reduce 339 (src line 2164)
+ . reduce 339 (src line 2226)
state 461
@@ -7754,7 +7754,7 @@ state 461
state 462
hidden_literal: sym. (341)
- . reduce 341 (src line 2187)
+ . reduce 341 (src line 2249)
state 463
@@ -7776,13 +7776,13 @@ state 463
state 464
hidden_import: LTYPE hidden_pkgtype hidden_type ';'. (308)
- . reduce 308 (src line 1961)
+ . reduce 308 (src line 2023)
state 465
hidden_import: LFUNC hidden_fndcl fnbody ';'. (309)
- . reduce 309 (src line 1965)
+ . reduce 309 (src line 2027)
state 466
@@ -7797,7 +7797,7 @@ state 467
hidden_funarg_list: hidden_funarg_list.',' hidden_funarg
',' shift 469
- . reduce 297 (src line 1921)
+ . reduce 297 (src line 1983)
state 468
@@ -7828,7 +7828,7 @@ state 470
oliteral: . (302)
LLITERAL shift 428
- . reduce 302 (src line 1935)
+ . reduce 302 (src line 1997)
oliteral goto 565
@@ -7856,13 +7856,13 @@ state 471
state 472
common_dcl: LVAR '(' vardcl_list osemi ')'. (29)
- . reduce 29 (src line 310)
+ . reduce 29 (src line 372)
state 473
vardcl_list: vardcl_list ';' vardcl. (221)
- . reduce 221 (src line 1525)
+ . reduce 221 (src line 1587)
state 474
@@ -7870,19 +7870,19 @@ state 474
expr_list: expr_list.',' expr
',' shift 155
- . reduce 40 (src line 365)
+ . reduce 40 (src line 427)
state 475
ntype: '(' ntype ')'. (171)
- . reduce 171 (src line 1196)
+ . reduce 171 (src line 1258)
state 476
common_dcl: lconst '(' constdcl osemi ')'. (32)
- . reduce 32 (src line 324)
+ . reduce 32 (src line 386)
state 477
@@ -7891,20 +7891,20 @@ state 477
osemi: . (286)
';' shift 568
- . reduce 286 (src line 1893)
+ . reduce 286 (src line 1955)
osemi goto 567
state 478
constdcl_list: constdcl1. (222)
- . reduce 222 (src line 1530)
+ . reduce 222 (src line 1592)
state 479
constdcl1: constdcl. (44)
- . reduce 44 (src line 384)
+ . reduce 44 (src line 446)
state 480
@@ -7928,7 +7928,7 @@ state 480
'?' shift 12
'@' shift 13
',' shift 225
- . reduce 46 (src line 390)
+ . reduce 46 (src line 452)
sym goto 123
ntype goto 569
@@ -7947,25 +7947,25 @@ state 481
expr_list: expr_list.',' expr
',' shift 155
- . reduce 42 (src line 374)
+ . reduce 42 (src line 436)
state 482
common_dcl: LTYPE '(' typedcl_list osemi ')'. (36)
- . reduce 36 (src line 345)
+ . reduce 36 (src line 407)
state 483
typedcl_list: typedcl_list ';' typedcl. (225)
- . reduce 225 (src line 1542)
+ . reduce 225 (src line 1604)
state 484
fnbody: '{' stmt_list '}'. (211)
- . reduce 211 (src line 1461)
+ . reduce 211 (src line 1523)
state 485
@@ -7973,19 +7973,19 @@ state 485
fndcl: '(' oarg_type_list_ocomma ')' sym.'(' oarg_type_list_ocomma ')' fnres
'(' shift 570
- . reduce 162 (src line 1158)
+ . reduce 162 (src line 1220)
state 486
fntype: LFUNC '(' oarg_type_list_ocomma ')' fnres. (209)
- . reduce 209 (src line 1448)
+ . reduce 209 (src line 1510)
state 487
fnres: fnret_type. (213)
- . reduce 213 (src line 1474)
+ . reduce 213 (src line 1536)
state 488
@@ -8005,7 +8005,7 @@ state 488
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 249 (src line 1707)
+ . reduce 249 (src line 1769)
sym goto 247
ntype goto 249
@@ -8027,37 +8027,37 @@ state 488
state 489
fnret_type: recvchantype. (184)
- . reduce 184 (src line 1227)
+ . reduce 184 (src line 1289)
state 490
fnret_type: fntype. (185)
- . reduce 185 (src line 1229)
+ . reduce 185 (src line 1291)
state 491
fnret_type: othertype. (186)
- . reduce 186 (src line 1230)
+ . reduce 186 (src line 1292)
state 492
fnret_type: ptrtype. (187)
- . reduce 187 (src line 1231)
+ . reduce 187 (src line 1293)
state 493
fnret_type: dotname. (188)
- . reduce 188 (src line 1232)
+ . reduce 188 (src line 1294)
state 494
arg_type_list: arg_type_list ',' arg_type. (248)
- . reduce 248 (src line 1702)
+ . reduce 248 (src line 1764)
state 495
@@ -8076,7 +8076,7 @@ state 495
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 212 (src line 1469)
+ . reduce 212 (src line 1531)
sym goto 123
dotname goto 493
@@ -8107,7 +8107,7 @@ state 496
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 212 (src line 1469)
+ . reduce 212 (src line 1531)
sym goto 123
dotname goto 493
@@ -8155,9 +8155,9 @@ state 497
'*' shift 58
'&' shift 59
'(' shift 67
- ';' reduce 251 (src line 1719)
+ ';' reduce 251 (src line 1781)
'{' shift 308
- '}' reduce 251 (src line 1719)
+ '}' reduce 251 (src line 1781)
'!' shift 62
'~' shift 63
'[' shift 77
@@ -8289,33 +8289,33 @@ state 501
switch_stmt: LSWITCH $$88 if_header $$89 LBODY.caseblock_list '}'
caseblock_list: . (63)
- . reduce 63 (src line 590)
+ . reduce 63 (src line 652)
caseblock_list goto 577
state 502
if_header: osimple_stmt ';' osimple_stmt. (77)
- . reduce 77 (src line 677)
+ . reduce 77 (src line 739)
state 503
caseblock_list: caseblock_list caseblock. (64)
- . reduce 64 (src line 594)
+ . reduce 64 (src line 656)
state 504
select_stmt: LSELECT $$91 LBODY caseblock_list '}'. (92)
- . reduce 92 (src line 782)
+ . reduce 92 (src line 844)
state 505
caseblock: case.$$61 stmt_list
$$61: . (61)
- . reduce 61 (src line 559)
+ . reduce 61 (src line 621)
$$61 goto 578
@@ -8377,14 +8377,14 @@ state 508
if_stmt: LIF $$78 if_header $$79 loop_body.$$80 elseif_list else
$$80: . (80)
- . reduce 80 (src line 699)
+ . reduce 80 (src line 761)
$$80 goto 581
state 509
pseudocall: pexpr '(' expr_or_type_list ocomma ')'. (124)
- . reduce 124 (src line 929)
+ . reduce 124 (src line 991)
state 510
@@ -8397,19 +8397,19 @@ state 510
state 511
expr_or_type_list: expr_or_type_list ',' expr_or_type. (279)
- . reduce 279 (src line 1855)
+ . reduce 279 (src line 1917)
state 512
pexpr_no_paren: pexpr '.' '(' expr_or_type ')'. (129)
- . reduce 129 (src line 958)
+ . reduce 129 (src line 1020)
state 513
pexpr_no_paren: pexpr '.' '(' LTYPE ')'. (130)
- . reduce 130 (src line 962)
+ . reduce 130 (src line 1024)
state 514
@@ -8424,7 +8424,7 @@ state 514
state 515
pexpr_no_paren: pexpr_no_paren '{' start_complit braced_keyval_list '}'. (137)
- . reduce 137 (src line 998)
+ . reduce 137 (src line 1060)
state 516
@@ -8452,7 +8452,7 @@ state 516
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 289 (src line 1897)
+ . reduce 289 (src line 1959)
sym goto 123
expr goto 402
@@ -8476,7 +8476,7 @@ state 516
state 517
braced_keyval_list: keyval_list ocomma. (285)
- . reduce 285 (src line 1885)
+ . reduce 285 (src line 1947)
state 518
@@ -8546,7 +8546,7 @@ state 519
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 284 (src line 1881)
+ . reduce 284 (src line 1943)
sym goto 123
expr goto 402
@@ -8593,7 +8593,7 @@ state 520
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 284 (src line 1881)
+ . reduce 284 (src line 1943)
sym goto 123
expr goto 402
@@ -8627,19 +8627,19 @@ state 521
state 522
pexpr_no_paren: convtype '(' expr ocomma ')'. (135)
- . reduce 135 (src line 985)
+ . reduce 135 (src line 1047)
state 523
pexpr_no_paren: comptype lbrace start_complit braced_keyval_list '}'. (136)
- . reduce 136 (src line 991)
+ . reduce 136 (src line 1053)
state 524
stmt_list: stmt_list ';' stmt. (271)
- . reduce 271 (src line 1812)
+ . reduce 271 (src line 1874)
state 525
@@ -8654,31 +8654,31 @@ state 525
state 526
othertype: LMAP '[' ntype ']' ntype. (195)
- . reduce 195 (src line 1268)
+ . reduce 195 (src line 1330)
state 527
structtype: LSTRUCT lbrace structdcl_list osemi '}'. (200)
- . reduce 200 (src line 1288)
+ . reduce 200 (src line 1350)
state 528
structdcl_list: structdcl_list ';' structdcl. (227)
- . reduce 227 (src line 1549)
+ . reduce 227 (src line 1611)
state 529
structdcl: new_name_list ntype oliteral. (230)
- . reduce 230 (src line 1564)
+ . reduce 230 (src line 1626)
state 530
new_name_list: new_name_list ',' new_name. (273)
- . reduce 273 (src line 1825)
+ . reduce 273 (src line 1887)
state 531
@@ -8686,7 +8686,7 @@ state 531
oliteral: . (302)
LLITERAL shift 428
- . reduce 302 (src line 1935)
+ . reduce 302 (src line 1997)
oliteral goto 594
@@ -8700,7 +8700,7 @@ state 532
state 533
structdcl: '*' embed oliteral. (233)
- . reduce 233 (src line 1600)
+ . reduce 233 (src line 1662)
state 534
@@ -8713,19 +8713,19 @@ state 534
state 535
packname: LNAME '.' sym. (237)
- . reduce 237 (src line 1632)
+ . reduce 237 (src line 1694)
state 536
interfacetype: LINTERFACE lbrace interfacedcl_list osemi '}'. (202)
- . reduce 202 (src line 1301)
+ . reduce 202 (src line 1363)
state 537
interfacedcl_list: interfacedcl_list ';' interfacedcl. (229)
- . reduce 229 (src line 1559)
+ . reduce 229 (src line 1621)
state 538
@@ -8738,13 +8738,13 @@ state 538
state 539
interfacedcl: '(' packname ')'. (241)
- . reduce 241 (src line 1662)
+ . reduce 241 (src line 1724)
state 540
hidden_type_misc: '[' ']' hidden_type. (319)
- . reduce 319 (src line 2028)
+ . reduce 319 (src line 2090)
state 541
@@ -8787,13 +8787,13 @@ state 544
hidden_structdcl_list: hidden_structdcl_list.';' hidden_structdcl
';' shift 601
- . reduce 299 (src line 1927)
+ . reduce 299 (src line 1989)
state 545
hidden_structdcl_list: hidden_structdcl. (348)
- . reduce 348 (src line 2222)
+ . reduce 348 (src line 2284)
state 546
@@ -8829,13 +8829,13 @@ state 548
hidden_interfacedcl_list: hidden_interfacedcl_list.';' hidden_interfacedcl
';' shift 604
- . reduce 301 (src line 1933)
+ . reduce 301 (src line 1995)
state 549
hidden_interfacedcl_list: hidden_interfacedcl. (350)
- . reduce 350 (src line 2232)
+ . reduce 350 (src line 2294)
state 550
@@ -8848,23 +8848,23 @@ state 550
state 551
hidden_interfacedcl: hidden_type. (334)
- . reduce 334 (src line 2139)
+ . reduce 334 (src line 2201)
state 552
sym: LNAME. (157)
hidden_type_misc: LNAME. (318)
- '(' reduce 157 (src line 1113)
- . reduce 318 (src line 2017)
+ '(' reduce 157 (src line 1175)
+ . reduce 318 (src line 2079)
state 553
sym: hidden_importsym. (158)
hidden_type_misc: hidden_importsym. (317)
- '(' reduce 158 (src line 1122)
- . reduce 317 (src line 2012)
+ '(' reduce 158 (src line 1184)
+ . reduce 317 (src line 2074)
state 554
@@ -8877,13 +8877,13 @@ state 554
state 555
hidden_type_misc: LCHAN LCOMM hidden_type. (327)
- . reduce 327 (src line 2064)
+ . reduce 327 (src line 2126)
state 556
hidden_type_recv_chan: LCOMM LCHAN hidden_type. (328)
- . reduce 328 (src line 2071)
+ . reduce 328 (src line 2133)
state 557
@@ -8896,7 +8896,7 @@ state 557
state 558
hidden_import: LCONST hidden_pkg_importsym '=' hidden_constant ';'. (306)
- . reduce 306 (src line 1953)
+ . reduce 306 (src line 2015)
state 559
@@ -8909,7 +8909,7 @@ state 559
state 560
hidden_literal: '-' LLITERAL. (340)
- . reduce 340 (src line 2169)
+ . reduce 340 (src line 2231)
state 561
@@ -8934,7 +8934,7 @@ state 562
'(' shift 612
'[' shift 342
'@' shift 13
- . reduce 335 (src line 2144)
+ . reduce 335 (src line 2206)
hidden_importsym goto 340
hidden_funres goto 611
@@ -8954,13 +8954,13 @@ state 563
state 564
hidden_funarg_list: hidden_funarg_list ',' hidden_funarg. (347)
- . reduce 347 (src line 2217)
+ . reduce 347 (src line 2279)
state 565
hidden_funarg: sym hidden_type oliteral. (330)
- . reduce 330 (src line 2085)
+ . reduce 330 (src line 2147)
state 566
@@ -8968,7 +8968,7 @@ state 566
oliteral: . (302)
LLITERAL shift 428
- . reduce 302 (src line 1935)
+ . reduce 302 (src line 1997)
oliteral goto 615
@@ -8986,7 +8986,7 @@ state 568
LNAME shift 10
'?' shift 12
'@' shift 13
- . reduce 287 (src line 1894)
+ . reduce 287 (src line 1956)
sym goto 105
dcl_name goto 104
@@ -9000,7 +9000,7 @@ state 569
constdcl1: dcl_name_list ntype. (45)
'=' shift 367
- . reduce 45 (src line 386)
+ . reduce 45 (src line 448)
state 570
@@ -9020,7 +9020,7 @@ state 570
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 249 (src line 1707)
+ . reduce 249 (src line 1769)
sym goto 247
ntype goto 249
@@ -9049,7 +9049,7 @@ state 571
state 572
fndcl: sym '(' oarg_type_list_ocomma ')' fnres. (205)
- . reduce 205 (src line 1336)
+ . reduce 205 (src line 1398)
state 573
@@ -9084,7 +9084,7 @@ state 574
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 294 (src line 1911)
+ . reduce 294 (src line 1973)
sym goto 123
expr goto 48
@@ -9149,7 +9149,7 @@ state 575
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 67 (src line 610)
+ . reduce 67 (src line 672)
state 576
@@ -9195,7 +9195,7 @@ state 576
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 68 (src line 617)
+ . reduce 68 (src line 679)
state 577
@@ -9217,11 +9217,11 @@ state 578
error shift 307
LLITERAL shift 68
LBREAK shift 41
- LCASE reduce 251 (src line 1719)
+ LCASE reduce 251 (src line 1781)
LCHAN shift 78
LCONST shift 47
LCONTINUE shift 42
- LDEFAULT reduce 251 (src line 1719)
+ LDEFAULT reduce 251 (src line 1781)
LDEFER shift 44
LFALL shift 40
LFOR shift 50
@@ -9245,9 +9245,9 @@ state 578
'*' shift 58
'&' shift 59
'(' shift 67
- ';' reduce 251 (src line 1719)
+ ';' reduce 251 (src line 1781)
'{' shift 308
- '}' reduce 251 (src line 1719)
+ '}' reduce 251 (src line 1781)
'!' shift 62
'~' shift 63
'[' shift 77
@@ -9302,27 +9302,27 @@ state 579
state 580
case: LDEFAULT ':'. (58)
- . reduce 58 (src line 524)
+ . reduce 58 (src line 586)
state 581
if_stmt: LIF $$78 if_header $$79 loop_body $$80.elseif_list else
elseif_list: . (84)
- . reduce 84 (src line 734)
+ . reduce 84 (src line 796)
elseif_list goto 628
state 582
pseudocall: pexpr '(' expr_or_type_list LDDD ocomma ')'. (125)
- . reduce 125 (src line 934)
+ . reduce 125 (src line 996)
state 583
pexpr_no_paren: pexpr '[' oexpr ':' oexpr ']'. (132)
- . reduce 132 (src line 970)
+ . reduce 132 (src line 1032)
state 584
@@ -9348,7 +9348,7 @@ state 584
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 290 (src line 1899)
+ . reduce 290 (src line 1961)
sym goto 123
expr goto 188
@@ -9371,19 +9371,19 @@ state 584
state 585
keyval_list: keyval_list ',' keyval. (282)
- . reduce 282 (src line 1872)
+ . reduce 282 (src line 1934)
state 586
keyval_list: keyval_list ',' bare_complitexpr. (283)
- . reduce 283 (src line 1876)
+ . reduce 283 (src line 1938)
state 587
keyval: expr ':' complitexpr. (141)
- . reduce 141 (src line 1020)
+ . reduce 141 (src line 1082)
state 588
@@ -9429,14 +9429,14 @@ state 588
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 144 (src line 1046)
+ . reduce 144 (src line 1108)
state 589
complitexpr: '{'.start_complit braced_keyval_list '}'
start_complit: . (140)
- . reduce 140 (src line 1013)
+ . reduce 140 (src line 1075)
start_complit goto 630
@@ -9458,22 +9458,22 @@ state 592
ntype: '(' ntype ')'. (171)
non_recvchantype: '(' ntype ')'. (180)
- LBODY reduce 180 (src line 1215)
- '(' reduce 180 (src line 1215)
- '{' reduce 180 (src line 1215)
- . reduce 171 (src line 1196)
+ LBODY reduce 180 (src line 1277)
+ '(' reduce 180 (src line 1277)
+ '{' reduce 180 (src line 1277)
+ . reduce 171 (src line 1258)
state 593
compound_stmt: '{' $$59 stmt_list '}'. (60)
- . reduce 60 (src line 549)
+ . reduce 60 (src line 611)
state 594
structdcl: '(' embed ')' oliteral. (232)
- . reduce 232 (src line 1594)
+ . reduce 232 (src line 1656)
state 595
@@ -9481,7 +9481,7 @@ state 595
oliteral: . (302)
LLITERAL shift 428
- . reduce 302 (src line 1935)
+ . reduce 302 (src line 1997)
oliteral goto 633
@@ -9490,7 +9490,7 @@ state 596
oliteral: . (302)
LLITERAL shift 428
- . reduce 302 (src line 1935)
+ . reduce 302 (src line 1997)
oliteral goto 634
@@ -9510,7 +9510,7 @@ state 597
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 212 (src line 1469)
+ . reduce 212 (src line 1531)
sym goto 123
dotname goto 493
@@ -9528,7 +9528,7 @@ state 597
state 598
hidden_type_misc: '[' LLITERAL ']' hidden_type. (320)
- . reduce 320 (src line 2032)
+ . reduce 320 (src line 2094)
state 599
@@ -9555,7 +9555,7 @@ state 599
state 600
hidden_type_misc: LSTRUCT '{' ohidden_structdcl_list '}'. (322)
- . reduce 322 (src line 2040)
+ . reduce 322 (src line 2102)
state 601
@@ -9575,14 +9575,14 @@ state 602
oliteral: . (302)
LLITERAL shift 428
- . reduce 302 (src line 1935)
+ . reduce 302 (src line 1997)
oliteral goto 638
state 603
hidden_type_misc: LINTERFACE '{' ohidden_interfacedcl_list '}'. (323)
- . reduce 323 (src line 2044)
+ . reduce 323 (src line 2106)
state 604
@@ -9616,7 +9616,7 @@ state 605
LNAME shift 10
'?' shift 12
'@' shift 13
- . reduce 296 (src line 1917)
+ . reduce 296 (src line 1979)
sym goto 357
hidden_importsym goto 11
@@ -9627,7 +9627,7 @@ state 605
state 606
hidden_type_misc: LCHAN '(' hidden_type_recv_chan ')'. (326)
- . reduce 326 (src line 2058)
+ . reduce 326 (src line 2120)
state 607
@@ -9645,7 +9645,7 @@ state 607
'(' shift 612
'[' shift 342
'@' shift 13
- . reduce 335 (src line 2144)
+ . reduce 335 (src line 2206)
hidden_importsym goto 340
hidden_funres goto 611
@@ -9672,19 +9672,19 @@ state 608
state 609
hidden_import: LCONST hidden_pkg_importsym hidden_type '=' hidden_constant ';'. (307)
- . reduce 307 (src line 1957)
+ . reduce 307 (src line 2019)
state 610
hidden_fndcl: hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres. (207)
- . reduce 207 (src line 1405)
+ . reduce 207 (src line 1467)
state 611
ohidden_funres: hidden_funres. (336)
- . reduce 336 (src line 2148)
+ . reduce 336 (src line 2210)
state 612
@@ -9694,7 +9694,7 @@ state 612
LNAME shift 10
'?' shift 12
'@' shift 13
- . reduce 296 (src line 1917)
+ . reduce 296 (src line 1979)
sym goto 357
hidden_importsym goto 11
@@ -9705,7 +9705,7 @@ state 612
state 613
hidden_funres: hidden_type. (338)
- . reduce 338 (src line 2155)
+ . reduce 338 (src line 2217)
state 614
@@ -9715,7 +9715,7 @@ state 614
LNAME shift 10
'?' shift 12
'@' shift 13
- . reduce 296 (src line 1917)
+ . reduce 296 (src line 1979)
sym goto 357
hidden_importsym goto 11
@@ -9726,19 +9726,19 @@ state 614
state 615
hidden_funarg: sym LDDD hidden_type oliteral. (331)
- . reduce 331 (src line 2094)
+ . reduce 331 (src line 2156)
state 616
common_dcl: lconst '(' constdcl ';' constdcl_list osemi ')'. (33)
- . reduce 33 (src line 330)
+ . reduce 33 (src line 392)
state 617
constdcl_list: constdcl_list ';' constdcl1. (223)
- . reduce 223 (src line 1532)
+ . reduce 223 (src line 1594)
state 618
@@ -9751,25 +9751,25 @@ state 618
state 619
fnres: '(' oarg_type_list_ocomma ')'. (214)
- . reduce 214 (src line 1478)
+ . reduce 214 (src line 1540)
state 620
loop_body: LBODY $$65 stmt_list '}'. (66)
- . reduce 66 (src line 604)
+ . reduce 66 (src line 666)
state 621
for_header: osimple_stmt ';' osimple_stmt ';' osimple_stmt. (70)
- . reduce 70 (src line 630)
+ . reduce 70 (src line 692)
state 622
switch_stmt: LSWITCH $$88 if_header $$89 LBODY caseblock_list '}'. (90)
- . reduce 90 (src line 768)
+ . reduce 90 (src line 830)
state 623
@@ -9777,13 +9777,13 @@ state 623
stmt_list: stmt_list.';' stmt
';' shift 416
- . reduce 62 (src line 571)
+ . reduce 62 (src line 633)
state 624
case: LCASE expr_or_type_list ':'. (55)
- . reduce 55 (src line 473)
+ . reduce 55 (src line 535)
state 625
@@ -9918,7 +9918,7 @@ state 628
else: . (86)
LELSE shift 650
- . reduce 86 (src line 743)
+ . reduce 86 (src line 805)
elseif goto 649
else goto 648
@@ -9954,7 +9954,7 @@ state 630
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 284 (src line 1881)
+ . reduce 284 (src line 1943)
sym goto 123
expr goto 402
@@ -9980,55 +9980,55 @@ state 630
state 631
bare_complitexpr: '{' start_complit braced_keyval_list '}'. (143)
- . reduce 143 (src line 1040)
+ . reduce 143 (src line 1102)
state 632
pexpr_no_paren: '(' expr_or_type ')' '{' start_complit braced_keyval_list '}'. (138)
- . reduce 138 (src line 1004)
+ . reduce 138 (src line 1066)
state 633
structdcl: '(' '*' embed ')' oliteral. (234)
- . reduce 234 (src line 1606)
+ . reduce 234 (src line 1668)
state 634
structdcl: '*' '(' embed ')' oliteral. (235)
- . reduce 235 (src line 1613)
+ . reduce 235 (src line 1675)
state 635
indcl: '(' oarg_type_list_ocomma ')' fnres. (242)
- . reduce 242 (src line 1668)
+ . reduce 242 (src line 1730)
state 636
hidden_type_misc: LMAP '[' hidden_type ']' hidden_type. (321)
- . reduce 321 (src line 2036)
+ . reduce 321 (src line 2098)
state 637
hidden_structdcl_list: hidden_structdcl_list ';' hidden_structdcl. (349)
- . reduce 349 (src line 2227)
+ . reduce 349 (src line 2289)
state 638
hidden_structdcl: sym hidden_type oliteral. (332)
- . reduce 332 (src line 2110)
+ . reduce 332 (src line 2172)
state 639
hidden_interfacedcl_list: hidden_interfacedcl_list ';' hidden_interfacedcl. (351)
- . reduce 351 (src line 2237)
+ . reduce 351 (src line 2299)
state 640
@@ -10041,7 +10041,7 @@ state 640
state 641
hidden_type_func: LFUNC '(' ohidden_funarg_list ')' ohidden_funres. (329)
- . reduce 329 (src line 2079)
+ . reduce 329 (src line 2141)
state 642
@@ -10081,7 +10081,7 @@ state 645
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 212 (src line 1469)
+ . reduce 212 (src line 1531)
sym goto 123
dotname goto 493
@@ -10193,13 +10193,13 @@ state 647
state 648
if_stmt: LIF $$78 if_header $$79 loop_body $$80 elseif_list else. (81)
- . reduce 81 (src line 703)
+ . reduce 81 (src line 765)
state 649
elseif_list: elseif_list elseif. (85)
- . reduce 85 (src line 738)
+ . reduce 85 (src line 800)
state 650
@@ -10215,7 +10215,7 @@ state 650
state 651
pexpr_no_paren: pexpr '[' oexpr ':' oexpr ':' oexpr ']'. (133)
- . reduce 133 (src line 974)
+ . reduce 133 (src line 1036)
state 652
@@ -10240,7 +10240,7 @@ state 653
'(' shift 612
'[' shift 342
'@' shift 13
- . reduce 335 (src line 2144)
+ . reduce 335 (src line 2206)
hidden_importsym goto 340
hidden_funres goto 611
@@ -10253,13 +10253,13 @@ state 653
state 654
hidden_constant: '(' hidden_literal '+' hidden_literal ')'. (343)
- . reduce 343 (src line 2197)
+ . reduce 343 (src line 2259)
state 655
hidden_funres: '(' ohidden_funarg_list ')'. (337)
- . reduce 337 (src line 2150)
+ . reduce 337 (src line 2212)
state 656
@@ -10277,7 +10277,7 @@ state 656
'(' shift 612
'[' shift 342
'@' shift 13
- . reduce 335 (src line 2144)
+ . reduce 335 (src line 2206)
hidden_importsym goto 340
hidden_funres goto 611
@@ -10290,51 +10290,51 @@ state 656
state 657
fndcl: '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres. (206)
- . reduce 206 (src line 1368)
+ . reduce 206 (src line 1430)
state 658
case: LCASE expr_or_type_list '=' expr ':'. (56)
- . reduce 56 (src line 497)
+ . reduce 56 (src line 559)
state 659
case: LCASE expr_or_type_list LCOLAS expr ':'. (57)
- . reduce 57 (src line 515)
+ . reduce 57 (src line 577)
state 660
elseif: LELSE LIF.$$82 if_header loop_body
$$82: . (82)
- . reduce 82 (src line 720)
+ . reduce 82 (src line 782)
$$82 goto 665
state 661
else: LELSE compound_stmt. (87)
- . reduce 87 (src line 747)
+ . reduce 87 (src line 809)
state 662
complitexpr: '{' start_complit braced_keyval_list '}'. (145)
- . reduce 145 (src line 1048)
+ . reduce 145 (src line 1110)
state 663
hidden_interfacedcl: sym '(' ohidden_funarg_list ')' ohidden_funres. (333)
- . reduce 333 (src line 2134)
+ . reduce 333 (src line 2196)
state 664
hidden_fndcl: '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres. (208)
- . reduce 208 (src line 1431)
+ . reduce 208 (src line 1493)
state 665
@@ -10360,7 +10360,7 @@ state 665
'[' shift 77
'?' shift 12
'@' shift 13
- . reduce 294 (src line 1911)
+ . reduce 294 (src line 1973)
sym goto 123
expr goto 48
@@ -10394,7 +10394,7 @@ state 666
state 667
elseif: LELSE LIF $$82 if_header loop_body. (83)
- . reduce 83 (src line 725)
+ . reduce 83 (src line 787)
76 terminals, 142 nonterminals
diff --git a/src/cmd/9g/cgen.go b/src/cmd/compile/internal/ppc64/cgen.go
similarity index 98%
rename from src/cmd/9g/cgen.go
rename to src/cmd/compile/internal/ppc64/cgen.go
index 5d24a6ff67..37dd6cefb2 100644
--- a/src/cmd/9g/cgen.go
+++ b/src/cmd/compile/internal/ppc64/cgen.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package ppc64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/ppc64"
)
diff --git a/src/cmd/9g/galign.go b/src/cmd/compile/internal/ppc64/galign.go
similarity index 96%
rename from src/cmd/9g/galign.go
rename to src/cmd/compile/internal/ppc64/galign.go
index a2f4a0ef89..73aef6fde9 100644
--- a/src/cmd/9g/galign.go
+++ b/src/cmd/compile/internal/ppc64/galign.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package ppc64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/ppc64"
)
@@ -45,7 +45,7 @@ func betypeinit() {
gc.Widthreg = 8
}
-func main() {
+func Main() {
gc.Thearch.Thechar = thechar
gc.Thearch.Thestring = thestring
gc.Thearch.Thelinkarch = thelinkarch
@@ -72,6 +72,7 @@ func main() {
gc.Thearch.Expandchecks = expandchecks
gc.Thearch.Getg = getg
gc.Thearch.Gins = gins
+ gc.Thearch.Ginscmp = ginscmp
gc.Thearch.Ginscon = ginscon
gc.Thearch.Ginsnop = ginsnop
gc.Thearch.Gmove = gmove
diff --git a/src/cmd/9g/ggen.go b/src/cmd/compile/internal/ppc64/ggen.go
similarity index 97%
rename from src/cmd/9g/ggen.go
rename to src/cmd/compile/internal/ppc64/ggen.go
index 28ebd9cc01..1b936b8a5f 100644
--- a/src/cmd/9g/ggen.go
+++ b/src/cmd/compile/internal/ppc64/ggen.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package ppc64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/ppc64"
"fmt"
@@ -32,7 +32,7 @@ func defframe(ptxt *obj.Prog) {
// iterate through declarations - they are sorted in decreasing xoffset order.
for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
n = l.N
- if !n.Needzero {
+ if !n.Name.Needzero {
continue
}
if n.Class != gc.PAUTO {
@@ -141,9 +141,9 @@ func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) {
check := 0
if gc.Issigned[t.Etype] {
check = 1
- if gc.Isconst(nl, gc.CTINT) && gc.Mpgetfix(nl.Val.U.Xval) != -(1<= uint64(nl.Type.Width*8) {
// large shift gets 2 shifts by width-1
var n3 gc.Node
diff --git a/src/cmd/9g/gsubr.go b/src/cmd/compile/internal/ppc64/gsubr.go
similarity index 94%
rename from src/cmd/9g/gsubr.go
rename to src/cmd/compile/internal/ppc64/gsubr.go
index 8223fe70b1..2501972846 100644
--- a/src/cmd/9g/gsubr.go
+++ b/src/cmd/compile/internal/ppc64/gsubr.go
@@ -28,10 +28,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package ppc64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/big"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/ppc64"
"fmt"
@@ -116,29 +117,55 @@ func ginscon2(as int, n2 *gc.Node, c int64) {
gc.Regfree(&ntmp)
}
-/*
- * set up nodes representing 2^63
- */
-var bigi gc.Node
+func ginscmp(op int, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
+ if gc.Isint[t.Etype] && n1.Op == gc.OLITERAL && n2.Op != gc.OLITERAL {
+ // Reverse comparison to place constant last.
+ op = gc.Brrev(op)
+ n1, n2 = n2, n1
+ }
-var bigf gc.Node
+ var r1, r2, g1, g2 gc.Node
+ gc.Regalloc(&r1, t, n1)
+ gc.Regalloc(&g1, n1.Type, &r1)
+ gc.Cgen(n1, &g1)
+ gmove(&g1, &r1)
+ if gc.Isint[t.Etype] && gc.Isconst(n2, gc.CTINT) {
+ ginscon2(optoas(gc.OCMP, t), &r1, n2.Int())
+ } else {
+ gc.Regalloc(&r2, t, n2)
+ gc.Regalloc(&g2, n1.Type, &r2)
+ gc.Cgen(n2, &g2)
+ gmove(&g2, &r2)
+ rawgins(optoas(gc.OCMP, t), &r1, &r2)
+ gc.Regfree(&g2)
+ gc.Regfree(&r2)
+ }
+ gc.Regfree(&g1)
+ gc.Regfree(&r1)
+ return gc.Gbranch(optoas(op, t), nil, likely)
+}
-var bignodes_did int
+// set up nodes representing 2^63
+var (
+ bigi gc.Node
+ bigf gc.Node
+ bignodes_did bool
+)
func bignodes() {
- if bignodes_did != 0 {
+ if bignodes_did {
return
}
- bignodes_did = 1
+ bignodes_did = true
- gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 1)
- gc.Mpshiftfix(bigi.Val.U.Xval, 63)
+ var i big.Int
+ i.SetInt64(1)
+ i.Lsh(&i, 63)
- bigf = bigi
- bigf.Type = gc.Types[gc.TFLOAT64]
- bigf.Val.Ctype = gc.CTFLT
- bigf.Val.U.Fval = new(gc.Mpflt)
- gc.Mpmovefixflt(bigf.Val.U.Fval, bigi.Val.U.Xval)
+ gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0)
+ bigi.SetBigInt(&i)
+
+ bigi.Convconst(&bigf, gc.Types[gc.TFLOAT64])
}
/*
@@ -173,13 +200,13 @@ func gmove(f *gc.Node, t *gc.Node) {
var con gc.Node
switch tt {
default:
- gc.Convconst(&con, t.Type, &f.Val)
+ f.Convconst(&con, t.Type)
case gc.TINT32,
gc.TINT16,
gc.TINT8:
var con gc.Node
- gc.Convconst(&con, gc.Types[gc.TINT64], &f.Val)
+ f.Convconst(&con, gc.Types[gc.TINT64])
var r1 gc.Node
gc.Regalloc(&r1, con.Type, t)
gins(ppc64.AMOVD, &con, &r1)
@@ -191,7 +218,7 @@ func gmove(f *gc.Node, t *gc.Node) {
gc.TUINT16,
gc.TUINT8:
var con gc.Node
- gc.Convconst(&con, gc.Types[gc.TUINT64], &f.Val)
+ f.Convconst(&con, gc.Types[gc.TUINT64])
var r1 gc.Node
gc.Regalloc(&r1, con.Type, t)
gins(ppc64.AMOVD, &con, &r1)
@@ -519,14 +546,13 @@ hard:
}
func intLiteral(n *gc.Node) (x int64, ok bool) {
- if n == nil || n.Op != gc.OLITERAL {
+ switch {
+ case n == nil:
return
- }
- switch n.Val.Ctype {
- case gc.CTINT, gc.CTRUNE:
- return gc.Mpgetfix(n.Val.U.Xval), true
- case gc.CTBOOL:
- return int64(obj.Bool2int(n.Val.U.Bval)), true
+ case gc.Isconst(n, gc.CTINT):
+ return n.Int(), true
+ case gc.Isconst(n, gc.CTBOOL):
+ return int64(obj.Bool2int(n.Bool())), true
}
return
}
diff --git a/src/cmd/9g/opt.go b/src/cmd/compile/internal/ppc64/opt.go
similarity index 96%
rename from src/cmd/9g/opt.go
rename to src/cmd/compile/internal/ppc64/opt.go
index 4a134f134f..1704f63c48 100644
--- a/src/cmd/9g/opt.go
+++ b/src/cmd/compile/internal/ppc64/opt.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package ppc64
// Many Power ISA arithmetic and logical instructions come in four
// standard variants. These bits let us map between variants.
diff --git a/src/cmd/9g/peep.go b/src/cmd/compile/internal/ppc64/peep.go
similarity index 99%
rename from src/cmd/9g/peep.go
rename to src/cmd/compile/internal/ppc64/peep.go
index 94c9b1554b..16eeb39097 100644
--- a/src/cmd/9g/peep.go
+++ b/src/cmd/compile/internal/ppc64/peep.go
@@ -28,10 +28,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package ppc64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/ppc64"
"fmt"
diff --git a/src/cmd/9g/prog.go b/src/cmd/compile/internal/ppc64/prog.go
similarity index 99%
rename from src/cmd/9g/prog.go
rename to src/cmd/compile/internal/ppc64/prog.go
index e28e389fac..c7e182769d 100644
--- a/src/cmd/9g/prog.go
+++ b/src/cmd/compile/internal/ppc64/prog.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package ppc64
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/ppc64"
)
diff --git a/src/cmd/9g/reg.go b/src/cmd/compile/internal/ppc64/reg.go
similarity index 98%
rename from src/cmd/9g/reg.go
rename to src/cmd/compile/internal/ppc64/reg.go
index fb0c2e37ec..fa1cb71975 100644
--- a/src/cmd/9g/reg.go
+++ b/src/cmd/compile/internal/ppc64/reg.go
@@ -28,10 +28,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package ppc64
import "cmd/internal/obj/ppc64"
-import "cmd/internal/gc"
+import "cmd/compile/internal/gc"
const (
NREGVAR = 64 /* 32 general + 32 floating */
diff --git a/src/cmd/internal/ssa/TODO b/src/cmd/compile/internal/ssa/TODO
similarity index 100%
rename from src/cmd/internal/ssa/TODO
rename to src/cmd/compile/internal/ssa/TODO
diff --git a/src/cmd/internal/ssa/block.go b/src/cmd/compile/internal/ssa/block.go
similarity index 100%
rename from src/cmd/internal/ssa/block.go
rename to src/cmd/compile/internal/ssa/block.go
diff --git a/src/cmd/internal/ssa/blockkind_string.go b/src/cmd/compile/internal/ssa/blockkind_string.go
similarity index 100%
rename from src/cmd/internal/ssa/blockkind_string.go
rename to src/cmd/compile/internal/ssa/blockkind_string.go
diff --git a/src/cmd/internal/ssa/cgen.go b/src/cmd/compile/internal/ssa/cgen.go
similarity index 100%
rename from src/cmd/internal/ssa/cgen.go
rename to src/cmd/compile/internal/ssa/cgen.go
diff --git a/src/cmd/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go
similarity index 100%
rename from src/cmd/internal/ssa/check.go
rename to src/cmd/compile/internal/ssa/check.go
diff --git a/src/cmd/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go
similarity index 100%
rename from src/cmd/internal/ssa/compile.go
rename to src/cmd/compile/internal/ssa/compile.go
diff --git a/src/cmd/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go
similarity index 100%
rename from src/cmd/internal/ssa/config.go
rename to src/cmd/compile/internal/ssa/config.go
diff --git a/src/cmd/internal/ssa/copyelim.go b/src/cmd/compile/internal/ssa/copyelim.go
similarity index 100%
rename from src/cmd/internal/ssa/copyelim.go
rename to src/cmd/compile/internal/ssa/copyelim.go
diff --git a/src/cmd/internal/ssa/critical.go b/src/cmd/compile/internal/ssa/critical.go
similarity index 100%
rename from src/cmd/internal/ssa/critical.go
rename to src/cmd/compile/internal/ssa/critical.go
diff --git a/src/cmd/internal/ssa/cse.go b/src/cmd/compile/internal/ssa/cse.go
similarity index 100%
rename from src/cmd/internal/ssa/cse.go
rename to src/cmd/compile/internal/ssa/cse.go
diff --git a/src/cmd/internal/ssa/deadcode.go b/src/cmd/compile/internal/ssa/deadcode.go
similarity index 100%
rename from src/cmd/internal/ssa/deadcode.go
rename to src/cmd/compile/internal/ssa/deadcode.go
diff --git a/src/cmd/internal/ssa/deadcode_test.go b/src/cmd/compile/internal/ssa/deadcode_test.go
similarity index 100%
rename from src/cmd/internal/ssa/deadcode_test.go
rename to src/cmd/compile/internal/ssa/deadcode_test.go
diff --git a/src/cmd/internal/ssa/dom.go b/src/cmd/compile/internal/ssa/dom.go
similarity index 100%
rename from src/cmd/internal/ssa/dom.go
rename to src/cmd/compile/internal/ssa/dom.go
diff --git a/src/cmd/internal/ssa/export_test.go b/src/cmd/compile/internal/ssa/export_test.go
similarity index 100%
rename from src/cmd/internal/ssa/export_test.go
rename to src/cmd/compile/internal/ssa/export_test.go
diff --git a/src/cmd/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go
similarity index 100%
rename from src/cmd/internal/ssa/func.go
rename to src/cmd/compile/internal/ssa/func.go
diff --git a/src/cmd/internal/ssa/func_test.go b/src/cmd/compile/internal/ssa/func_test.go
similarity index 100%
rename from src/cmd/internal/ssa/func_test.go
rename to src/cmd/compile/internal/ssa/func_test.go
diff --git a/src/cmd/internal/ssa/fuse.go b/src/cmd/compile/internal/ssa/fuse.go
similarity index 100%
rename from src/cmd/internal/ssa/fuse.go
rename to src/cmd/compile/internal/ssa/fuse.go
diff --git a/src/cmd/internal/ssa/generic.go b/src/cmd/compile/internal/ssa/generic.go
similarity index 100%
rename from src/cmd/internal/ssa/generic.go
rename to src/cmd/compile/internal/ssa/generic.go
diff --git a/src/cmd/internal/ssa/id.go b/src/cmd/compile/internal/ssa/id.go
similarity index 100%
rename from src/cmd/internal/ssa/id.go
rename to src/cmd/compile/internal/ssa/id.go
diff --git a/src/cmd/internal/ssa/layout.go b/src/cmd/compile/internal/ssa/layout.go
similarity index 100%
rename from src/cmd/internal/ssa/layout.go
rename to src/cmd/compile/internal/ssa/layout.go
diff --git a/src/cmd/internal/ssa/location.go b/src/cmd/compile/internal/ssa/location.go
similarity index 100%
rename from src/cmd/internal/ssa/location.go
rename to src/cmd/compile/internal/ssa/location.go
diff --git a/src/cmd/internal/ssa/lower.go b/src/cmd/compile/internal/ssa/lower.go
similarity index 100%
rename from src/cmd/internal/ssa/lower.go
rename to src/cmd/compile/internal/ssa/lower.go
diff --git a/src/cmd/internal/ssa/lowerAmd64.go b/src/cmd/compile/internal/ssa/lowerAmd64.go
similarity index 100%
rename from src/cmd/internal/ssa/lowerAmd64.go
rename to src/cmd/compile/internal/ssa/lowerAmd64.go
diff --git a/src/cmd/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go
similarity index 100%
rename from src/cmd/internal/ssa/op.go
rename to src/cmd/compile/internal/ssa/op.go
diff --git a/src/cmd/internal/ssa/op_string.go b/src/cmd/compile/internal/ssa/op_string.go
similarity index 100%
rename from src/cmd/internal/ssa/op_string.go
rename to src/cmd/compile/internal/ssa/op_string.go
diff --git a/src/cmd/internal/ssa/opamd64.go b/src/cmd/compile/internal/ssa/opamd64.go
similarity index 100%
rename from src/cmd/internal/ssa/opamd64.go
rename to src/cmd/compile/internal/ssa/opamd64.go
diff --git a/src/cmd/internal/ssa/opt.go b/src/cmd/compile/internal/ssa/opt.go
similarity index 100%
rename from src/cmd/internal/ssa/opt.go
rename to src/cmd/compile/internal/ssa/opt.go
diff --git a/src/cmd/internal/ssa/phielim.go b/src/cmd/compile/internal/ssa/phielim.go
similarity index 100%
rename from src/cmd/internal/ssa/phielim.go
rename to src/cmd/compile/internal/ssa/phielim.go
diff --git a/src/cmd/internal/ssa/print.go b/src/cmd/compile/internal/ssa/print.go
similarity index 100%
rename from src/cmd/internal/ssa/print.go
rename to src/cmd/compile/internal/ssa/print.go
diff --git a/src/cmd/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go
similarity index 100%
rename from src/cmd/internal/ssa/regalloc.go
rename to src/cmd/compile/internal/ssa/regalloc.go
diff --git a/src/cmd/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go
similarity index 100%
rename from src/cmd/internal/ssa/rewrite.go
rename to src/cmd/compile/internal/ssa/rewrite.go
diff --git a/src/cmd/internal/ssa/rulegen/generic.rules b/src/cmd/compile/internal/ssa/rulegen/generic.rules
similarity index 100%
rename from src/cmd/internal/ssa/rulegen/generic.rules
rename to src/cmd/compile/internal/ssa/rulegen/generic.rules
diff --git a/src/cmd/internal/ssa/rulegen/lower_amd64.rules b/src/cmd/compile/internal/ssa/rulegen/lower_amd64.rules
similarity index 100%
rename from src/cmd/internal/ssa/rulegen/lower_amd64.rules
rename to src/cmd/compile/internal/ssa/rulegen/lower_amd64.rules
diff --git a/src/cmd/internal/ssa/rulegen/rulegen.go b/src/cmd/compile/internal/ssa/rulegen/rulegen.go
similarity index 100%
rename from src/cmd/internal/ssa/rulegen/rulegen.go
rename to src/cmd/compile/internal/ssa/rulegen/rulegen.go
diff --git a/src/cmd/internal/ssa/schedule.go b/src/cmd/compile/internal/ssa/schedule.go
similarity index 100%
rename from src/cmd/internal/ssa/schedule.go
rename to src/cmd/compile/internal/ssa/schedule.go
diff --git a/src/cmd/internal/ssa/sparseset.go b/src/cmd/compile/internal/ssa/sparseset.go
similarity index 100%
rename from src/cmd/internal/ssa/sparseset.go
rename to src/cmd/compile/internal/ssa/sparseset.go
diff --git a/src/cmd/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go
similarity index 100%
rename from src/cmd/internal/ssa/stackalloc.go
rename to src/cmd/compile/internal/ssa/stackalloc.go
diff --git a/src/cmd/internal/ssa/type.go b/src/cmd/compile/internal/ssa/type.go
similarity index 100%
rename from src/cmd/internal/ssa/type.go
rename to src/cmd/compile/internal/ssa/type.go
diff --git a/src/cmd/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go
similarity index 100%
rename from src/cmd/internal/ssa/value.go
rename to src/cmd/compile/internal/ssa/value.go
diff --git a/src/cmd/8g/cgen.go b/src/cmd/compile/internal/x86/cgen.go
similarity index 96%
rename from src/cmd/8g/cgen.go
rename to src/cmd/compile/internal/x86/cgen.go
index dfbdafefe3..1768674e42 100644
--- a/src/cmd/8g/cgen.go
+++ b/src/cmd/compile/internal/x86/cgen.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package x86
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/x86"
)
@@ -17,7 +17,7 @@ import (
*/
func igenindex(n *gc.Node, res *gc.Node, bounded bool) *obj.Prog {
if !gc.Is64(n.Type) {
- if n.Addable {
+ if n.Addable && (gc.Simtype[n.Etype] == gc.TUINT32 || gc.Simtype[n.Etype] == gc.TINT32) {
// nothing to do.
*res = *n
} else {
diff --git a/src/cmd/8g/cgen64.go b/src/cmd/compile/internal/x86/cgen64.go
similarity index 97%
rename from src/cmd/8g/cgen64.go
rename to src/cmd/compile/internal/x86/cgen64.go
index a682e2fb44..0b061ffb60 100644
--- a/src/cmd/8g/cgen64.go
+++ b/src/cmd/compile/internal/x86/cgen64.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package x86
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/x86"
)
@@ -162,7 +162,7 @@ func cgen64(n *gc.Node, res *gc.Node) {
// shld hi:lo, c
// shld lo:t, c
case gc.OLROT:
- v := uint64(gc.Mpgetfix(r.Val.U.Xval))
+ v := uint64(r.Int())
if v >= 32 {
// reverse during load to do the first 32 bits of rotate
@@ -189,7 +189,7 @@ func cgen64(n *gc.Node, res *gc.Node) {
case gc.OLSH:
if r.Op == gc.OLITERAL {
- v := uint64(gc.Mpgetfix(r.Val.U.Xval))
+ v := uint64(r.Int())
if v >= 64 {
if gc.Is64(r.Type) {
splitclean()
@@ -278,7 +278,7 @@ func cgen64(n *gc.Node, res *gc.Node) {
case gc.ORSH:
if r.Op == gc.OLITERAL {
- v := uint64(gc.Mpgetfix(r.Val.U.Xval))
+ v := uint64(r.Int())
if v >= 64 {
if gc.Is64(r.Type) {
splitclean()
@@ -400,9 +400,8 @@ func cgen64(n *gc.Node, res *gc.Node) {
if lo2.Op == gc.OLITERAL {
// special cases for constants.
- lv := uint32(gc.Mpgetfix(lo2.Val.U.Xval))
-
- hv := uint32(gc.Mpgetfix(hi2.Val.U.Xval))
+ lv := uint32(lo2.Int())
+ hv := uint32(hi2.Int())
splitclean() // right side
split64(res, &lo2, &hi2)
switch n.Op {
diff --git a/src/cmd/8g/galign.go b/src/cmd/compile/internal/x86/galign.go
similarity index 96%
rename from src/cmd/8g/galign.go
rename to src/cmd/compile/internal/x86/galign.go
index e96b628dcc..2b602e1bb3 100644
--- a/src/cmd/8g/galign.go
+++ b/src/cmd/compile/internal/x86/galign.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package x86
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/x86"
"fmt"
@@ -39,7 +39,7 @@ func betypeinit() {
gc.Widthreg = 4
}
-func main() {
+func Main() {
gc.Thearch.Thechar = thechar
gc.Thearch.Thestring = thestring
gc.Thearch.Thelinkarch = thelinkarch
@@ -81,6 +81,7 @@ func main() {
gc.Thearch.Expandchecks = expandchecks
gc.Thearch.Getg = getg
gc.Thearch.Gins = gins
+ gc.Thearch.Ginscmp = ginscmp
gc.Thearch.Ginscon = ginscon
gc.Thearch.Ginsnop = ginsnop
gc.Thearch.Gmove = gmove
diff --git a/src/cmd/8g/ggen.go b/src/cmd/compile/internal/x86/ggen.go
similarity index 98%
rename from src/cmd/8g/ggen.go
rename to src/cmd/compile/internal/x86/ggen.go
index 59025525fa..dabc139f30 100644
--- a/src/cmd/8g/ggen.go
+++ b/src/cmd/compile/internal/x86/ggen.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package x86
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/x86"
)
@@ -30,7 +30,7 @@ func defframe(ptxt *obj.Prog) {
ax := uint32(0)
for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
n = l.N
- if !n.Needzero {
+ if !n.Name.Needzero {
continue
}
if n.Class != gc.PAUTO {
@@ -216,9 +216,9 @@ func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node, ax *gc.Node, dx *gc.N
check := 0
if gc.Issigned[t.Etype] {
check = 1
- if gc.Isconst(nl, gc.CTINT) && gc.Mpgetfix(nl.Val.U.Xval) != -1<= uint64(nl.Type.Width*8) {
// large shift gets 2 shifts by width-1
gins(a, ncon(uint32(w)-1), &n1)
diff --git a/src/cmd/8g/gsubr.go b/src/cmd/compile/internal/x86/gsubr.go
similarity index 95%
rename from src/cmd/8g/gsubr.go
rename to src/cmd/compile/internal/x86/gsubr.go
index 34ddfe0619..baf251781c 100644
--- a/src/cmd/8g/gsubr.go
+++ b/src/cmd/compile/internal/x86/gsubr.go
@@ -28,10 +28,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package x86
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/big"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/x86"
"fmt"
@@ -582,6 +583,45 @@ func ginscon(as int, c int64, n2 *gc.Node) {
gins(as, &n1, n2)
}
+func ginscmp(op int, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
+ if gc.Isint[t.Etype] || int(t.Etype) == gc.Tptr {
+ if (n1.Op == gc.OLITERAL || n1.Op == gc.OADDR && n1.Left.Op == gc.ONAME) && n2.Op != gc.OLITERAL {
+ // Reverse comparison to place constant (including address constant) last.
+ op = gc.Brrev(op)
+ n1, n2 = n2, n1
+ }
+ }
+
+ // General case.
+ var r1, r2, g1, g2 gc.Node
+ if n1.Op == gc.ONAME && n1.Class&gc.PHEAP == 0 || n1.Op == gc.OINDREG {
+ r1 = *n1
+ } else {
+ gc.Regalloc(&r1, t, n1)
+ gc.Regalloc(&g1, n1.Type, &r1)
+ gc.Cgen(n1, &g1)
+ gmove(&g1, &r1)
+ }
+ if n2.Op == gc.OLITERAL && gc.Isint[t.Etype] || n2.Op == gc.OADDR && n2.Left.Op == gc.ONAME && n2.Left.Class == gc.PEXTERN {
+ r2 = *n2
+ } else {
+ gc.Regalloc(&r2, t, n2)
+ gc.Regalloc(&g2, n1.Type, &r2)
+ gc.Cgen(n2, &g2)
+ gmove(&g2, &r2)
+ }
+ gins(optoas(gc.OCMP, t), &r1, &r2)
+ if r1.Op == gc.OREGISTER {
+ gc.Regfree(&g1)
+ gc.Regfree(&r1)
+ }
+ if r2.Op == gc.OREGISTER {
+ gc.Regfree(&g2)
+ gc.Regfree(&r2)
+ }
+ return gc.Gbranch(optoas(op, t), nil, likely)
+}
+
/*
* swap node contents
*/
@@ -602,7 +642,7 @@ func ncon(i uint32) *gc.Node {
if ncon_n.Type == nil {
gc.Nodconst(&ncon_n, gc.Types[gc.TUINT32], 0)
}
- gc.Mpmovecfix(ncon_n.Val.U.Xval, int64(i))
+ ncon_n.SetInt(int64(i))
return &ncon_n
}
@@ -638,7 +678,7 @@ func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) {
case gc.ONAME:
if n.Class == gc.PPARAMREF {
var n1 gc.Node
- gc.Cgen(n.Heapaddr, &n1)
+ gc.Cgen(n.Name.Heapaddr, &n1)
sclean[nsclean-1] = n1
n = &n1
}
@@ -660,8 +700,8 @@ func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) {
case gc.OLITERAL:
var n1 gc.Node
- gc.Convconst(&n1, n.Type, &n.Val)
- i := gc.Mpgetfix(n1.Val.U.Xval)
+ n.Convconst(&n1, n.Type)
+ i := n1.Int()
gc.Nodconst(lo, gc.Types[gc.TUINT32], int64(uint32(i)))
i >>= 32
if n.Type.Etype == gc.TINT64 {
@@ -682,36 +722,36 @@ func splitclean() {
}
}
-/*
- * set up nodes representing fp constants
- */
-var zerof gc.Node
-
-var two64f gc.Node
-
-var two63f gc.Node
-
-var bignodes_did int
+// set up nodes representing fp constants
+var (
+ zerof gc.Node
+ two63f gc.Node
+ two64f gc.Node
+ bignodes_did bool
+)
func bignodes() {
- if bignodes_did != 0 {
+ if bignodes_did {
return
}
- bignodes_did = 1
+ bignodes_did = true
- two64f = *ncon(0)
- two64f.Type = gc.Types[gc.TFLOAT64]
- two64f.Val.Ctype = gc.CTFLT
- two64f.Val.U.Fval = new(gc.Mpflt)
- gc.Mpmovecflt(two64f.Val.U.Fval, 18446744073709551616.)
+ gc.Nodconst(&zerof, gc.Types[gc.TINT64], 0)
+ zerof.Convconst(&zerof, gc.Types[gc.TFLOAT64])
- two63f = two64f
- two63f.Val.U.Fval = new(gc.Mpflt)
- gc.Mpmovecflt(two63f.Val.U.Fval, 9223372036854775808.)
+ var i big.Int
+ i.SetInt64(1)
+ i.Lsh(&i, 63)
+ var bigi gc.Node
- zerof = two64f
- zerof.Val.U.Fval = new(gc.Mpflt)
- gc.Mpmovecflt(zerof.Val.U.Fval, 0)
+ gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0)
+ bigi.SetBigInt(&i)
+ bigi.Convconst(&two63f, gc.Types[gc.TFLOAT64])
+
+ gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0)
+ i.Lsh(&i, 1)
+ bigi.SetBigInt(&i)
+ bigi.Convconst(&two64f, gc.Types[gc.TFLOAT64])
}
func memname(n *gc.Node, t *gc.Type) {
@@ -750,7 +790,7 @@ func gmove(f *gc.Node, t *gc.Node) {
// convert constant to desired type
if f.Op == gc.OLITERAL {
var con gc.Node
- gc.Convconst(&con, t.Type, &f.Val)
+ f.Convconst(&con, t.Type)
f = &con
ft = gc.Simsimtype(con.Type)
}
@@ -1021,7 +1061,7 @@ func floatmove(f *gc.Node, t *gc.Node) {
// convert constant to desired type
if f.Op == gc.OLITERAL {
var con gc.Node
- gc.Convconst(&con, t.Type, &f.Val)
+ f.Convconst(&con, t.Type)
f = &con
ft = gc.Simsimtype(con.Type)
diff --git a/src/cmd/8g/peep.go b/src/cmd/compile/internal/x86/peep.go
similarity index 99%
rename from src/cmd/8g/peep.go
rename to src/cmd/compile/internal/x86/peep.go
index e309aea785..8b50eab077 100644
--- a/src/cmd/8g/peep.go
+++ b/src/cmd/compile/internal/x86/peep.go
@@ -28,10 +28,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package x86
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/x86"
"fmt"
diff --git a/src/cmd/8g/prog.go b/src/cmd/compile/internal/x86/prog.go
similarity index 99%
rename from src/cmd/8g/prog.go
rename to src/cmd/compile/internal/x86/prog.go
index 1346c20f2b..f96a1aa945 100644
--- a/src/cmd/8g/prog.go
+++ b/src/cmd/compile/internal/x86/prog.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package x86
import (
- "cmd/internal/gc"
+ "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/x86"
)
diff --git a/src/cmd/8g/reg.go b/src/cmd/compile/internal/x86/reg.go
similarity index 98%
rename from src/cmd/8g/reg.go
rename to src/cmd/compile/internal/x86/reg.go
index 50b5b97ab1..8c97171e47 100644
--- a/src/cmd/8g/reg.go
+++ b/src/cmd/compile/internal/x86/reg.go
@@ -28,10 +28,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package x86
import "cmd/internal/obj/x86"
-import "cmd/internal/gc"
+import "cmd/compile/internal/gc"
const (
NREGVAR = 16 /* 8 integer + 8 floating */
diff --git a/src/cmd/compile/main.go b/src/cmd/compile/main.go
new file mode 100644
index 0000000000..7b69c34424
--- /dev/null
+++ b/src/cmd/compile/main.go
@@ -0,0 +1,34 @@
+// 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 (
+ "cmd/compile/internal/amd64"
+ "cmd/compile/internal/arm"
+ "cmd/compile/internal/arm64"
+ "cmd/compile/internal/ppc64"
+ "cmd/compile/internal/x86"
+ "cmd/internal/obj"
+ "fmt"
+ "os"
+)
+
+func main() {
+ switch obj.Getgoarch() {
+ default:
+ fmt.Fprintf(os.Stderr, "compile: unknown architecture %q\n", obj.Getgoarch())
+ os.Exit(2)
+ case "386":
+ x86.Main()
+ case "amd64", "amd64p32":
+ amd64.Main()
+ case "arm":
+ arm.Main()
+ case "arm64":
+ arm64.Main()
+ case "ppc64", "ppc64le":
+ ppc64.Main()
+ }
+}
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index 64b2399972..0cdb7d69f7 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -20,7 +20,6 @@ var (
goarch string
gobin string
gohostarch string
- gohostchar string
gohostos string
goos string
goarm string
@@ -30,10 +29,8 @@ var (
goextlinkenabled string
workdir string
tooldir string
- gochar string
oldgoos string
oldgoarch string
- oldgochar string
slash string
exe string
defaultcc string
@@ -48,17 +45,13 @@ var (
vflag int // verbosity
)
-// The known architecture letters.
-var gochars = "5667899"
-
// The known architectures.
var okgoarch = []string{
- // same order as gochars
- "arm",
+ "386",
"amd64",
"amd64p32",
+ "arm",
"arm64",
- "386",
"ppc64",
"ppc64le",
}
@@ -147,22 +140,18 @@ func xinit() {
gohostarch = b
}
- i := find(gohostarch, okgoarch)
- if i < 0 {
+ if find(gohostarch, okgoarch) < 0 {
fatal("unknown $GOHOSTARCH %s", gohostarch)
}
- gohostchar = gochars[i : i+1]
b = os.Getenv("GOARCH")
if b == "" {
b = gohostarch
}
goarch = b
- i = find(goarch, okgoarch)
- if i < 0 {
+ if find(goarch, okgoarch) < 0 {
fatal("unknown $GOARCH %s", goarch)
}
- gochar = gochars[i : i+1]
b = os.Getenv("GO_EXTLINK_ENABLED")
if b != "" {
@@ -374,7 +363,7 @@ var oldtool = []string{
// Unreleased directories (relative to $GOROOT) that should
// not be in release branches.
var unreleased = []string{
- "src/cmd/link",
+ "src/cmd/newlink",
"src/cmd/objwriter",
"src/debug/goobj",
"src/old",
@@ -436,7 +425,7 @@ func setup() {
}
// If $GOBIN is set and has a Go compiler, it must be cleaned.
- for _, char := range gochars {
+ for _, char := range "56789" {
if isfile(pathf("%s%s%c%s", gobin, slash, char, "g")) {
for _, old := range oldtool {
xremove(pathf("%s/%s", gobin, old))
@@ -540,7 +529,7 @@ func install(dir string) {
if elem == "go" {
elem = "go_bootstrap"
}
- link = []string{fmt.Sprintf("%s/%sl", tooldir, gochar), "-o", pathf("%s/%s%s", tooldir, elem, exe)}
+ link = []string{pathf("%s/link", tooldir), "-o", pathf("%s/%s%s", tooldir, elem, exe)}
targ = len(link) - 1
}
ttarg := mtime(link[targ])
@@ -675,7 +664,7 @@ func install(dir string) {
} else {
archive = b
}
- compile := []string{pathf("%s/%sg", tooldir, gochar), "-pack", "-o", b, "-p", pkg}
+ compile := []string{pathf("%s/compile", tooldir), "-pack", "-o", b, "-p", pkg}
if dir == "runtime" {
compile = append(compile, "-+", "-asmhdr", pathf("%s/go_asm.h", workdir))
}
@@ -703,11 +692,7 @@ func install(dir string) {
b := pathf("%s/%s", workdir, filepath.Base(p))
// Change the last character of the output file (which was c or s).
- if gohostos == "plan9" {
- b = b[:len(b)-1] + gohostchar
- } else {
- b = b[:len(b)-1] + "o"
- }
+ b = b[:len(b)-1] + "o"
compile = append(compile, "-o", b, p)
bgrun(path, compile...)
@@ -897,17 +882,9 @@ var buildorder = []string{
// compilers but build only the $GOARCH ones.
var cleantab = []string{
// Commands and C libraries.
- "cmd/5g",
- "cmd/5l",
- "cmd/6g",
- "cmd/6l",
- "cmd/7g",
- "cmd/7l",
- "cmd/8g",
- "cmd/8l",
- "cmd/9g",
- "cmd/9l",
+ "cmd/compile",
"cmd/go",
+ "cmd/link",
"cmd/old5a",
"cmd/old6a",
"cmd/old8a",
@@ -1043,7 +1020,6 @@ func cmdenv() {
xprintf(format, "GOHOSTARCH", gohostarch)
xprintf(format, "GOHOSTOS", gohostos)
xprintf(format, "GOTOOLDIR", tooldir)
- xprintf(format, "GOCHAR", gochar)
if goarch == "arm" {
xprintf(format, "GOARM", goarm)
}
@@ -1088,10 +1064,8 @@ func cmdbootstrap() {
// For the main bootstrap, building for host os/arch.
oldgoos = goos
oldgoarch = goarch
- oldgochar = gochar
goos = gohostos
goarch = gohostarch
- gochar = gohostchar
os.Setenv("GOHOSTARCH", gohostarch)
os.Setenv("GOHOSTOS", gohostos)
os.Setenv("GOARCH", goarch)
@@ -1105,37 +1079,22 @@ func cmdbootstrap() {
// than in a standard release like Go 1.4, so don't do this rebuild by default.
if false {
xprintf("##### Building Go toolchain using itself.\n")
- for _, pattern := range buildorder {
- if pattern == "cmd/go" {
+ for _, dir := range buildorder {
+ if dir == "cmd/go" {
break
}
- dir := pattern
- if strings.Contains(pattern, "%s") {
- dir = fmt.Sprintf(pattern, gohostchar)
- }
install(dir)
- if oldgochar != gohostchar && strings.Contains(pattern, "%s") {
- install(fmt.Sprintf(pattern, oldgochar))
- }
}
xprintf("\n")
}
xprintf("##### Building compilers and go_bootstrap for host, %s/%s.\n", gohostos, gohostarch)
- for _, pattern := range buildorder {
- dir := pattern
- if strings.Contains(pattern, "%s") {
- dir = fmt.Sprintf(pattern, gohostchar)
- }
+ for _, dir := range buildorder {
install(dir)
- if oldgochar != gohostchar && strings.Contains(pattern, "%s") {
- install(fmt.Sprintf(pattern, oldgochar))
- }
}
goos = oldgoos
goarch = oldgoarch
- gochar = oldgochar
os.Setenv("GOARCH", goarch)
os.Setenv("GOOS", goos)
diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go
index f5037fab88..7988129868 100644
--- a/src/cmd/dist/buildtool.go
+++ b/src/cmd/dist/buildtool.go
@@ -23,31 +23,34 @@ import (
// which are commands, and entries beginning with internal/, which are
// packages supporting the commands.
var bootstrapDirs = []string{
- "5g",
- "5l",
- "6g",
- "6l",
- "7g",
- "7l",
- "8g",
- "8l",
- "9g",
- "9l",
"asm",
"asm/internal/arch",
"asm/internal/asm",
"asm/internal/flags",
"asm/internal/lex",
+ "compile",
+ "compile/internal/amd64",
+ "compile/internal/arm",
+ "compile/internal/arm64",
+ "compile/internal/big",
+ "compile/internal/gc",
+ "compile/internal/ppc64",
+ "compile/internal/ssa",
+ "compile/internal/x86",
"internal/asm",
- "internal/gc/big",
- "internal/gc",
- "internal/ld",
+ "internal/gcprog",
"internal/obj",
"internal/obj/arm",
"internal/obj/arm64",
"internal/obj/ppc64",
"internal/obj/x86",
- "internal/ssa",
+ "link",
+ "link/internal/amd64",
+ "link/internal/arm",
+ "link/internal/arm64",
+ "link/internal/ld",
+ "link/internal/ppc64",
+ "link/internal/x86",
"old5a",
"old6a",
"old8a",
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index 1ed099583e..f5a0dc50f1 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -14,6 +14,7 @@ import (
"os/exec"
"path/filepath"
"regexp"
+ "runtime"
"strconv"
"strings"
"time"
@@ -23,8 +24,11 @@ func cmdtest() {
var t tester
flag.BoolVar(&t.listMode, "list", false, "list available tests")
flag.BoolVar(&t.noRebuild, "no-rebuild", false, "don't rebuild std and cmd packages")
+ flag.BoolVar(&t.keepGoing, "k", false, "keep going even when error occurred")
flag.StringVar(&t.banner, "banner", "##### ", "banner prefix; blank means no section banners")
- flag.StringVar(&t.runRxStr, "run", "", "run only those tests matching the regular expression; empty means to run all")
+ flag.StringVar(&t.runRxStr, "run", os.Getenv("GOTESTONLY"),
+ "run only those tests matching the regular expression; empty means to run all. "+
+ "Special exception: if the string begins with '!', the match is inverted.")
xflagparse(0)
t.run()
}
@@ -33,8 +37,10 @@ func cmdtest() {
type tester struct {
listMode bool
noRebuild bool
+ keepGoing bool
runRxStr string
runRx *regexp.Regexp
+ runRxWant bool
banner string // prefix, or "" for none
goroot string
@@ -129,6 +135,19 @@ func (t *tester) run() {
}
if t.runRxStr != "" {
+ // Temporary (2015-05-14) special case for "std",
+ // which the plan9 builder was using for ages. Delete
+ // this once we update dashboard/builders.go to use a
+ // regexp instead.
+ if runtime.GOOS == "plan9" && t.runRxStr == "std" {
+ t.runRxStr = "^go_test:"
+ }
+ if t.runRxStr[0] == '!' {
+ t.runRxWant = false
+ t.runRxStr = t.runRxStr[1:]
+ } else {
+ t.runRxWant = true
+ }
t.runRx = regexp.MustCompile(t.runRxStr)
}
@@ -146,8 +165,9 @@ func (t *tester) run() {
os.Unsetenv("GOROOT_FINAL")
var lastHeading string
+ ok := true
for _, dt := range t.tests {
- if t.runRx != nil && !t.runRx.MatchString(dt.name) {
+ if t.runRx != nil && (t.runRx.MatchString(dt.name) != t.runRxWant) {
t.partial = true
continue
}
@@ -159,10 +179,18 @@ func (t *tester) run() {
fmt.Printf("# go tool dist test -run=^%s$\n", dt.name)
}
if err := dt.fn(); err != nil {
- log.Fatalf("Failed: %v", err)
+ ok = false
+ if t.keepGoing {
+ log.Printf("Failed: %v", err)
+ } else {
+ log.Fatalf("Failed: %v", err)
+ }
}
}
- if t.partial {
+ if !ok {
+ fmt.Println("\nFAILED")
+ os.Exit(1)
+ } else if t.partial {
fmt.Println("\nALL TESTS PASSED (some were excluded)")
} else {
fmt.Println("\nALL TESTS PASSED")
@@ -173,52 +201,71 @@ func (t *tester) timeout(sec int) string {
return "-timeout=" + fmt.Sprint(time.Duration(sec)*time.Second*time.Duration(t.timeoutScale))
}
-func (t *tester) registerTests() {
- // Register a separate logical test for each package in the standard library
- // but actually group them together at execution time to share the cost of
- // building packages shared between them.
- all, err := exec.Command("go", "list", "std", "cmd").Output()
- if err != nil {
- log.Fatalf("Error running go list std cmd: %v", err)
- }
- // ranGoTest and stdMatches are state closed over by the
- // stdlib testing func below. The tests are run sequentially,
- // so there's no need for locks.
- var (
- ranGoTest bool
- stdMatches []string
- )
- for _, pkg := range strings.Fields(string(all)) {
- testName := "go_test:" + pkg
- if t.runRx == nil || t.runRx.MatchString(testName) {
- stdMatches = append(stdMatches, pkg)
- }
- t.tests = append(t.tests, distTest{
- name: testName,
- heading: "Testing packages.",
- fn: func() error {
- if ranGoTest {
- return nil
- }
- ranGoTest = true
- cmd := exec.Command("go", append([]string{
- "test",
- "-short",
- t.timeout(120),
- "-gcflags=" + os.Getenv("GO_GCFLAGS"),
- }, stdMatches...)...)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- return cmd.Run()
- },
- })
- }
+// ranGoTest and stdMatches are state closed over by the stdlib
+// testing func in registerStdTest below. The tests are run
+// sequentially, so there's no need for locks.
+var (
+ ranGoTest bool
+ stdMatches []string
+)
- // Old hack for when Plan 9 on GCE was too slow.
- // We're keeping this until test sharding (Issue 10029) is finished, though.
- if os.Getenv("GOTESTONLY") == "std" {
- t.partial = true
- return
+func (t *tester) registerStdTest(pkg string) {
+ testName := "go_test:" + pkg
+ if t.runRx == nil || t.runRx.MatchString(testName) {
+ stdMatches = append(stdMatches, pkg)
+ }
+ t.tests = append(t.tests, distTest{
+ name: testName,
+ heading: "Testing packages.",
+ fn: func() error {
+ if ranGoTest {
+ return nil
+ }
+ ranGoTest = true
+ cmd := exec.Command("go", append([]string{
+ "test",
+ "-short",
+ t.timeout(120),
+ "-gcflags=" + os.Getenv("GO_GCFLAGS"),
+ }, stdMatches...)...)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ return cmd.Run()
+ },
+ })
+}
+
+// validStdPkg reports whether pkg looks like a standard library package name.
+// Notably, it's not blank and doesn't contain regexp characters.
+func validStdPkg(pkg string) bool {
+ if pkg == "" {
+ return false
+ }
+ for _, r := range pkg {
+ switch {
+ case 'a' <= r && r <= 'z':
+ case 'A' <= r && r <= 'Z':
+ case '0' <= r && r <= '9':
+ case r == '_':
+ case r == '/':
+ default:
+ return false
+ }
+ }
+ return true
+}
+
+func (t *tester) registerTests() {
+ // Fast path to avoid the ~1 second of `go list std cmd` when
+ // the caller passed -run=^go_test:foo/bar$ (as the continuous
+ // build coordinator does).
+ if strings.HasPrefix(t.runRxStr, "^go_test:") && strings.HasSuffix(t.runRxStr, "$") {
+ pkg := strings.TrimPrefix(t.runRxStr, "^go_test:")
+ pkg = strings.TrimSuffix(pkg, "$")
+ if validStdPkg(pkg) {
+ t.registerStdTest(pkg)
+ return
+ }
}
// Runtime CPU tests.
@@ -244,9 +291,7 @@ func (t *tester) registerTests() {
},
})
- iOS := t.goos == "darwin" && (t.goarch == "arm" || t.goarch == "arm64")
-
- if t.cgoEnabled && t.goos != "android" && !iOS {
+ if t.cgoEnabled && t.goos != "android" && !t.iOS() {
// Disabled on android and iOS. golang.org/issue/8345
t.tests = append(t.tests, distTest{
name: "cgo_stdio",
@@ -265,7 +310,7 @@ func (t *tester) registerTests() {
},
})
}
- if t.cgoEnabled && t.goos != "android" && !iOS {
+ if t.cgoEnabled && t.goos != "android" && !t.iOS() {
// TODO(crawshaw): reenable on android and iOS
// golang.org/issue/8345
//
@@ -295,7 +340,7 @@ func (t *tester) registerTests() {
heading: "../misc/cgo/testso",
fn: t.cgoTestSOWindows,
})
- } else if t.hasBash() && t.goos != "android" && !iOS {
+ } else if t.hasBash() && t.goos != "android" && !t.iOS() {
t.registerTest("testso", "../misc/cgo/testso", "./test.bash")
}
if t.supportedBuildmode("c-archive") {
@@ -305,28 +350,28 @@ func (t *tester) registerTests() {
t.registerTest("testcshared", "../misc/cgo/testcshared", "./test.bash")
}
if t.supportedBuildmode("shared") {
- t.registerTest("testshared", "../misc/cgo/testshared", "./test.bash")
+ t.registerTest("testshared", "../misc/cgo/testshared", "go", "test")
}
if t.gohostos == "linux" && t.goarch == "amd64" {
t.registerTest("testasan", "../misc/cgo/testasan", "go", "run", "main.go")
}
- if t.hasBash() && t.goos != "android" && !iOS && t.gohostos != "windows" {
+ if t.hasBash() && t.goos != "android" && !t.iOS() && t.gohostos != "windows" {
t.registerTest("cgo_errors", "../misc/cgo/errors", "./test.bash")
}
if t.gohostos == "linux" && t.extLink() {
t.registerTest("testsigfwd", "../misc/cgo/testsigfwd", "go", "run", "main.go")
}
}
- if t.hasBash() && t.goos != "nacl" && t.goos != "android" && !iOS {
+ if t.hasBash() && t.goos != "nacl" && t.goos != "android" && !t.iOS() {
t.registerTest("doc_progs", "../doc/progs", "time", "go", "run", "run.go")
t.registerTest("wiki", "../doc/articles/wiki", "./test.bash")
t.registerTest("codewalk", "../doc/codewalk", "time", "./run")
t.registerTest("shootout", "../test/bench/shootout", "time", "./timing.sh", "-test")
}
- if t.goos != "android" && !iOS {
+ if t.goos != "android" && !t.iOS() {
t.registerTest("bench_go1", "../test/bench/go1", "go", "test")
}
- if t.goos != "android" && !iOS {
+ if t.goos != "android" && !t.iOS() {
// TODO(bradfitz): shard down into these tests, as
// this is one of the slowest (and most shardable)
// tests.
@@ -336,7 +381,7 @@ func (t *tester) registerTests() {
fn: t.testDirTest,
})
}
- if t.goos != "nacl" && t.goos != "android" && !iOS {
+ if t.goos != "nacl" && t.goos != "android" && !t.iOS() {
t.tests = append(t.tests, distTest{
name: "api",
heading: "API check",
@@ -346,6 +391,44 @@ func (t *tester) registerTests() {
})
}
+ // Register the standard library tests lasts, to avoid the ~1 second latency
+ // of running `go list std cmd` if we're running a specific test.
+ // Now we know the names of all the other tests registered so far.
+ if !t.wantSpecificRegisteredTest() {
+ all, err := exec.Command("go", "list", "std", "cmd").Output()
+ if err != nil {
+ log.Fatalf("Error running go list std cmd: %v", err)
+ }
+ // Put the standard library tests first.
+ orig := t.tests
+ t.tests = nil
+ for _, pkg := range strings.Fields(string(all)) {
+ t.registerStdTest(pkg)
+ }
+ t.tests = append(t.tests, orig...)
+ }
+}
+
+// wantSpecificRegisteredTest reports whether the caller is requesting a
+// run of a specific test via the flag -run=^TESTNAME$ (as is done by the
+// continuous build coordinator).
+func (t *tester) wantSpecificRegisteredTest() bool {
+ if !strings.HasPrefix(t.runRxStr, "^") || !strings.HasSuffix(t.runRxStr, "$") {
+ return false
+ }
+ test := t.runRxStr[1 : len(t.runRxStr)-1]
+ return t.isRegisteredTestName(test)
+}
+
+// isRegisteredTestName reports whether a test named testName has already
+// been registered.
+func (t *tester) isRegisteredTestName(testName string) bool {
+ for _, tt := range t.tests {
+ if tt.name == testName {
+ return true
+ }
+ }
+ return false
}
func (t *tester) registerTest(name, dirBanner, bin string, args ...string) {
diff --git a/src/cmd/doc/main.go b/src/cmd/doc/main.go
index b3be2a975b..18dafc298c 100644
--- a/src/cmd/doc/main.go
+++ b/src/cmd/doc/main.go
@@ -132,11 +132,12 @@ func parseArgs() (*build.Package, string, string) {
// slash+1: if there's no slash, the value is -1 and start is 0; otherwise
// start is the byte after the slash.
for start := slash + 1; start < len(arg); start = period + 1 {
- period = start + strings.Index(arg[start:], ".")
+ period = strings.Index(arg[start:], ".")
symbol := ""
if period < 0 {
period = len(arg)
} else {
+ period += start
symbol = arg[period+1:]
}
// Have we identified a package already?
diff --git a/src/cmd/doc/pkg.go b/src/cmd/doc/pkg.go
index 3a0aa7ff89..5c8976b663 100644
--- a/src/cmd/doc/pkg.go
+++ b/src/cmd/doc/pkg.go
@@ -115,7 +115,7 @@ func (pkg *Package) emit(comment string, node ast.Node) {
log.Fatal(err)
}
if comment != "" {
- pkg.newlines(1)
+ pkg.newlines(2) // Guarantee blank line before comment.
doc.ToText(&pkg.buf, comment, " ", "\t", 80)
}
pkg.newlines(1)
@@ -190,6 +190,7 @@ func (pkg *Package) packageDoc() {
pkg.valueSummary(pkg.doc.Vars)
pkg.funcSummary(pkg.doc.Funcs)
pkg.typeSummary()
+ pkg.bugs()
}
// packageClause prints the package clause.
@@ -253,6 +254,18 @@ func (pkg *Package) typeSummary() {
}
}
+// bugs prints the BUGS information for the package.
+// TODO: Provide access to TODOs and NOTEs as well (very noisy so off by default)?
+func (pkg *Package) bugs() {
+ if pkg.doc.Notes["BUG"] == nil {
+ return
+ }
+ pkg.Printf("\n")
+ for _, note := range pkg.doc.Notes["BUG"] {
+ pkg.Printf("%s: %v\n", "BUG", note.Body)
+ }
+}
+
// findValues finds the doc.Values that describe the symbol.
func (pkg *Package) findValues(symbol string, docValues []*doc.Value) (values []*doc.Value) {
for _, value := range docValues {
@@ -332,13 +345,16 @@ func (pkg *Package) symbolDoc(symbol string) {
}
decl := typ.Decl
spec := pkg.findTypeSpec(decl, typ.Name)
- trimUnexportedFields(spec)
+ trimUnexportedElems(spec)
// If there are multiple types defined, reduce to just this one.
if len(decl.Specs) > 1 {
decl.Specs = []ast.Spec{spec}
}
pkg.emit(typ.Doc, decl)
// Show associated methods, constants, etc.
+ if len(typ.Consts) > 0 || len(typ.Vars) > 0 || len(typ.Funcs) > 0 || len(typ.Methods) > 0 {
+ pkg.Printf("\n")
+ }
pkg.valueSummary(typ.Consts)
pkg.valueSummary(typ.Vars)
pkg.funcSummary(typ.Funcs)
@@ -353,22 +369,26 @@ func (pkg *Package) symbolDoc(symbol string) {
}
}
-// trimUnexportedFields modifies spec in place to elide unexported fields (unless
-// the unexported flag is set). If spec is not a structure declartion, nothing happens.
-func trimUnexportedFields(spec *ast.TypeSpec) {
+// trimUnexportedElems modifies spec in place to elide unexported fields from
+// structs and methods from interfaces (unless the unexported flag is set).
+func trimUnexportedElems(spec *ast.TypeSpec) {
if *unexported {
- // We're printing all fields.
return
}
- // It must be a struct for us to care. (We show unexported methods in interfaces.)
- structType, ok := spec.Type.(*ast.StructType)
- if !ok {
- return
+ switch typ := spec.Type.(type) {
+ case *ast.StructType:
+ typ.Fields = trimUnexportedFields(typ.Fields, "fields")
+ case *ast.InterfaceType:
+ typ.Methods = trimUnexportedFields(typ.Methods, "methods")
}
+}
+
+// trimUnexportedFields returns the field list trimmed of unexported fields.
+func trimUnexportedFields(fields *ast.FieldList, what string) *ast.FieldList {
trimmed := false
- list := make([]*ast.Field, 0, len(structType.Fields.List))
- for _, field := range structType.Fields.List {
- // Trims if any is unexported. Fine in practice.
+ list := make([]*ast.Field, 0, len(fields.List))
+ for _, field := range fields.List {
+ // Trims if any is unexported. Good enough in practice.
ok := true
for _, name := range field.Names {
if !isExported(name.Name) {
@@ -381,19 +401,23 @@ func trimUnexportedFields(spec *ast.TypeSpec) {
list = append(list, field)
}
}
- if trimmed {
- unexportedField := &ast.Field{
- Type: ast.NewIdent(""), // Hack: printer will treat this as a field with a named type.
- Comment: &ast.CommentGroup{
- List: []*ast.Comment{
- &ast.Comment{
- Text: "// Has unexported fields.\n",
- },
+ if !trimmed {
+ return fields
+ }
+ unexportedField := &ast.Field{
+ Type: ast.NewIdent(""), // Hack: printer will treat this as a field with a named type.
+ Comment: &ast.CommentGroup{
+ List: []*ast.Comment{
+ &ast.Comment{
+ Text: fmt.Sprintf("// Has unexported %s.\n", what),
},
},
- }
- list = append(list, unexportedField)
- structType.Fields.List = list
+ },
+ }
+ return &ast.FieldList{
+ Opening: fields.Opening,
+ List: append(list, unexportedField),
+ Closing: fields.Closing,
}
}
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index e0d4a6c0fe..2b1cbf98ec 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -503,6 +503,7 @@ syntax of package template. The default output is equivalent to -f
Name string // package name
Doc string // package documentation string
Target string // install path
+ Shlib string // the shared library that contains this package (only set when -linkshared)
Goroot bool // is this package in the Go root?
Standard bool // is this package part of the standard Go library?
Stale bool // would 'go install' do anything for this package?
@@ -1053,7 +1054,7 @@ environment variable (see 'go help gopath').
If no import paths are given, the action applies to the
package in the current directory.
-There are three reserved names for paths that should not be used
+There are four reserved names for paths that should not be used
for packages to be built with the go tool:
- "main" denotes the top-level package in a stand-alone executable.
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index fda126b008..17ff7e0cbb 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -547,9 +547,6 @@ var (
goarch string
goos string
exeSuffix string
-
- archCharVal string
- archCharErr error
)
func init() {
@@ -558,16 +555,6 @@ func init() {
if goos == "windows" {
exeSuffix = ".exe"
}
- archCharVal, archCharErr = build.ArchChar(goarch)
-}
-
-// archChar returns the architecture character. This is only needed
-// for the gc toolchain, so only fail if we actually need it.
-func archChar() string {
- if archCharErr != nil {
- fatalf("%s", archCharErr)
- }
- return archCharVal
}
// A builder holds global state about a build.
@@ -782,8 +769,8 @@ func (b *builder) action1(mode buildMode, depMode buildMode, p *Package, looksha
b.actionCache[key] = a
return a
}
- pkgs := readpkglist(filepath.Join(p.build.PkgTargetRoot, shlib))
- a = b.libaction(shlib, pkgs, modeInstall, depMode)
+ pkgs := readpkglist(shlib)
+ a = b.libaction(filepath.Base(shlib), pkgs, modeInstall, depMode)
b.actionCache[key2] = a
b.actionCache[key] = a
return a
@@ -1208,7 +1195,7 @@ func (b *builder) build(a *action) (err error) {
fmt.Fprintf(os.Stderr, "%s\n", a.p.ImportPath)
}
- if a.p.Standard && a.p.ImportPath == "runtime" && buildContext.Compiler == "gc" && archChar() != "" &&
+ if a.p.Standard && a.p.ImportPath == "runtime" && buildContext.Compiler == "gc" &&
(!hasString(a.p.GoFiles, "zgoos_"+buildContext.GOOS+".go") ||
!hasString(a.p.GoFiles, "zgoarch_"+buildContext.GOARCH+".go")) {
return fmt.Errorf("%s/%s must be bootstrapped using make%v", buildContext.GOOS, buildContext.GOARCH, defaultSuffix())
@@ -1371,15 +1358,8 @@ func (b *builder) build(a *action) (err error) {
}
}
- var objExt string
- if _, ok := buildToolchain.(gccgoToolchain); ok {
- objExt = "o"
- } else {
- objExt = archChar()
- }
-
for _, file := range cfiles {
- out := file[:len(file)-len(".c")] + "." + objExt
+ out := file[:len(file)-len(".c")] + ".o"
if err := buildToolchain.cc(b, a.p, obj, obj+out, file); err != nil {
return err
}
@@ -1388,7 +1368,7 @@ func (b *builder) build(a *action) (err error) {
// Assemble .s files.
for _, file := range sfiles {
- out := file[:len(file)-len(".s")] + "." + objExt
+ out := file[:len(file)-len(".s")] + ".o"
if err := buildToolchain.asm(b, a.p, obj, obj+out, file); err != nil {
return err
}
@@ -1532,7 +1512,7 @@ func (b *builder) linkShared(a *action) (err error) {
}
ldflags = append(ldflags, d.p.ImportPath+"="+d.target)
}
- return b.run(".", a.target, nil, buildToolExec, tool(archChar()+"l"), "-o", a.target, importArgs, ldflags)
+ return b.run(".", a.target, nil, buildToolExec, tool("link"), "-o", a.target, importArgs, ldflags)
}
// install is the action for installing a single package or executable.
@@ -2109,18 +2089,18 @@ func (noToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error
type gcToolchain struct{}
func (gcToolchain) compiler() string {
- return tool(archChar() + "g")
+ return tool("compile")
}
func (gcToolchain) linker() string {
- return tool(archChar() + "l")
+ return tool("link")
}
func (gcToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool, importArgs []string, gofiles []string) (ofile string, output []byte, err error) {
if archive != "" {
ofile = archive
} else {
- out := "_go_." + archChar()
+ out := "_go_.o"
ofile = obj + out
}
@@ -2152,7 +2132,7 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool,
gcargs = append(gcargs, "-installsuffix", buildContext.InstallSuffix)
}
- args := []interface{}{buildToolExec, tool(archChar() + "g"), "-o", ofile, "-trimpath", b.work, buildGcflags, gcargs, "-D", p.localPrefix, importArgs}
+ args := []interface{}{buildToolExec, tool("compile"), "-o", ofile, "-trimpath", b.work, buildGcflags, gcargs, "-D", p.localPrefix, importArgs}
if ofile == archive {
args = append(args, "-pack")
}
@@ -2182,9 +2162,22 @@ func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
}
// Disable checks when additional flags are passed, as the old assemblers
// don't implement some of them (e.g., -shared).
- if verifyAsm && goarch != "arm64" && len(buildAsmflags) == 0 {
- if err := toolVerify(b, p, "old"+archChar()+"a", ofile, args); err != nil {
- return err
+ if verifyAsm && len(buildAsmflags) == 0 {
+ old := ""
+ switch goarch {
+ case "arm":
+ old = "old5a"
+ case "amd64", "amd64p32":
+ old = "old6a"
+ case "386":
+ old = "old8a"
+ case "ppc64", "ppc64le":
+ old = "old9a"
+ }
+ if old != "" {
+ if err := toolVerify(b, p, old, ofile, args); err != nil {
+ return err
+ }
}
}
return nil
@@ -2333,7 +2326,7 @@ func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action,
ldflags = setextld(ldflags, compiler)
ldflags = append(ldflags, "-buildmode="+ldBuildmode)
ldflags = append(ldflags, buildLdflags...)
- return b.run(".", p.ImportPath, nil, buildToolExec, tool(archChar()+"l"), "-o", out, importArgs, ldflags, mainpkg)
+ return b.run(".", p.ImportPath, nil, buildToolExec, tool("link"), "-o", out, importArgs, ldflags, mainpkg)
}
func (gcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error {
@@ -2785,13 +2778,6 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
cgoflags := []string{}
// TODO: make cgo not depend on $GOARCH?
- var objExt string
- if _, ok := buildToolchain.(gccgoToolchain); ok {
- objExt = "o"
- } else {
- objExt = archChar()
- }
-
if p.Standard && p.ImportPath == "runtime/cgo" {
cgoflags = append(cgoflags, "-import_runtime_cgo=false")
}
@@ -2836,7 +2822,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
// cc _cgo_defun.c
_, gccgo := buildToolchain.(gccgoToolchain)
if gccgo {
- defunObj := obj + "_cgo_defun." + objExt
+ defunObj := obj + "_cgo_defun.o"
if err := buildToolchain.cc(b, p, obj, defunObj, defunC); err != nil {
return nil, nil, err
}
diff --git a/src/cmd/go/env.go b/src/cmd/go/env.go
index 863eb4d26e..8d427b37c2 100644
--- a/src/cmd/go/env.go
+++ b/src/cmd/go/env.go
@@ -49,10 +49,6 @@ func mkEnv() []envVar {
{"TERM", "dumb"},
}
- if archCharErr == nil {
- env = append(env, envVar{"GOCHAR", archChar()})
- }
-
if goos != "plan9" {
cmd := b.gccCmd(".")
env = append(env, envVar{"CC", cmd[0]})
diff --git a/src/cmd/go/fmt.go b/src/cmd/go/fmt.go
index 65dc3ca599..1722b9d568 100644
--- a/src/cmd/go/fmt.go
+++ b/src/cmd/go/fmt.go
@@ -4,6 +4,11 @@
package main
+import (
+ "os"
+ "path/filepath"
+)
+
func init() {
addBuildFlagsNX(cmdFmt)
}
@@ -29,10 +34,31 @@ See also: go fix, go vet.
}
func runFmt(cmd *Command, args []string) {
+ gofmt := gofmtPath()
for _, pkg := range packages(args) {
// Use pkg.gofiles instead of pkg.Dir so that
// the command only applies to this package,
// not to packages in subdirectories.
- run(stringList("gofmt", "-l", "-w", relPaths(pkg.allgofiles)))
+ run(stringList(gofmt, "-l", "-w", relPaths(pkg.allgofiles)))
}
}
+
+func gofmtPath() string {
+ gofmt := "gofmt"
+ if toolIsWindows {
+ gofmt += toolWindowsExtension
+ }
+
+ gofmtPath := filepath.Join(gobin, gofmt)
+ if _, err := os.Stat(gofmtPath); err == nil {
+ return gofmtPath
+ }
+
+ gofmtPath = filepath.Join(goroot, "bin", gofmt)
+ if _, err := os.Stat(gofmtPath); err == nil {
+ return gofmtPath
+ }
+
+ // fallback to looking for gofmt in $PATH
+ return "gofmt"
+}
diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go
index 56e8493e1a..2062f0c4ee 100644
--- a/src/cmd/go/help.go
+++ b/src/cmd/go/help.go
@@ -47,7 +47,7 @@ environment variable (see 'go help gopath').
If no import paths are given, the action applies to the
package in the current directory.
-There are three reserved names for paths that should not be used
+There are four reserved names for paths that should not be used
for packages to be built with the go tool:
- "main" denotes the top-level package in a stand-alone executable.
diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index 9466aad6a6..601c30362f 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -394,25 +394,17 @@ const (
// goTools is a map of Go program import path to install target directory.
var goTools = map[string]targetDir{
- "cmd/5g": toTool,
- "cmd/5l": toTool,
- "cmd/6g": toTool,
- "cmd/6l": toTool,
- "cmd/7g": toTool,
- "cmd/7l": toTool,
- "cmd/8g": toTool,
- "cmd/8l": toTool,
- "cmd/9g": toTool,
- "cmd/9l": toTool,
"cmd/addr2line": toTool,
"cmd/api": toTool,
"cmd/asm": toTool,
+ "cmd/compile": toTool,
"cmd/cgo": toTool,
"cmd/cover": toTool,
"cmd/dist": toTool,
"cmd/doc": toTool,
"cmd/fix": toTool,
"cmd/link": toTool,
+ "cmd/newlink": toTool,
"cmd/nm": toTool,
"cmd/objdump": toTool,
"cmd/old5a": toTool,
@@ -536,7 +528,8 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
shlibnamefile := p.target[:len(p.target)-2] + ".shlibname"
shlib, err := ioutil.ReadFile(shlibnamefile)
if err == nil {
- p.Shlib = strings.TrimSpace(string(shlib))
+ libname := strings.TrimSpace(string(shlib))
+ p.Shlib = filepath.Join(p.build.PkgTargetRoot, libname)
} else if !os.IsNotExist(err) {
fatalf("unexpected error reading %s: %v", shlibnamefile, err)
}
@@ -680,10 +673,10 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
p.Target = p.target
// The gc toolchain only permits C source files with cgo.
- if len(p.CFiles) > 0 && !p.usesCgo() && buildContext.Compiler == "gc" {
+ if len(p.CFiles) > 0 && !p.usesCgo() && !p.usesSwig() && buildContext.Compiler == "gc" {
p.Error = &PackageError{
ImportStack: stk.copy(),
- Err: fmt.Sprintf("C source files not allowed when not using cgo: %s", strings.Join(p.CFiles, " ")),
+ Err: fmt.Sprintf("C source files not allowed when not using cgo or SWIG: %s", strings.Join(p.CFiles, " ")),
}
return p
}
diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go
index 408104d776..2179000afd 100644
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -115,8 +115,12 @@ var vcsGit = &vcsCmd{
tagLookupCmd: []tagCmd{
{"show-ref tags/{tag} origin/{tag}", `((?:tags|origin)/\S+)$`},
},
- tagSyncCmd: "checkout {tag}",
- tagSyncDefault: "checkout master",
+ tagSyncCmd: "checkout {tag}",
+ // both createCmd and downloadCmd update the working dir.
+ // No need to do more here. We used to 'checkout master'
+ // but that doesn't work if the default branch is not named master.
+ // See golang.org/issue/9032.
+ tagSyncDefault: "",
scheme: []string{"git", "https", "http", "git+ssh"},
pingCmd: "ls-remote {scheme}://{repo}",
diff --git a/src/cmd/internal/asm/lexbody.go b/src/cmd/internal/asm/lexbody.go
index b5e5d1eee2..a1519c8566 100644
--- a/src/cmd/internal/asm/lexbody.go
+++ b/src/cmd/internal/asm/lexbody.go
@@ -149,7 +149,7 @@ func newfile(s string, f *os.File) {
}
fi.P = nil
- obj.Linklinehist(Ctxt, int(Lineno), s, 0)
+ Ctxt.LineHist.Push(int(Lineno), s)
}
var thetext *obj.LSym
@@ -630,7 +630,7 @@ loop:
n, _ = i.F.Read(i.B[:])
if n == 0 {
i.F.Close()
- obj.Linklinehist(Ctxt, int(Lineno), "", 0)
+ Ctxt.LineHist.Pop(int(Lineno))
goto pop
}
fi.P = i.B[1:n]
diff --git a/src/cmd/internal/asm/macbody.go b/src/cmd/internal/asm/macbody.go
index c488ea1e56..4565d3a37f 100644
--- a/src/cmd/internal/asm/macbody.go
+++ b/src/cmd/internal/asm/macbody.go
@@ -32,7 +32,6 @@ package asm
import (
"bytes"
- "cmd/internal/obj"
"fmt"
"os"
"strings"
@@ -683,7 +682,7 @@ func maclin() {
}
nn:
- obj.Linklinehist(Ctxt, int(Lineno), symb, int(n))
+ Ctxt.LineHist.Update(int(Lineno), symb, int(n))
return
bad:
@@ -796,7 +795,7 @@ func macprag() {
/*
* put pragma-line in as a funny history
*/
- obj.Linklinehist(Ctxt, int(Lineno), symb, -1)
+ Ctxt.AddImport(symb)
return
}
if s != nil && s.Name == "pack" {
diff --git a/src/cmd/internal/gc/go.errors b/src/cmd/internal/gc/go.errors
deleted file mode 100644
index 8370a2007d..0000000000
--- a/src/cmd/internal/gc/go.errors
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2010 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.
-
-// Example-based syntax error messages.
-// See yaccerrors.go.
-
-package gc
-
-var yymsg = []struct {
- yystate int
- yychar int
- msg string
-}{
- // Each line of the form % token list
- // is converted by yaccerrors.go into the yystate and yychar caused
- // by that token list.
-
- % loadsys package LIMPORT '(' LLITERAL import_package import_there ','
- "unexpected comma during import block"},
-
- % loadsys package LIMPORT LNAME ';'
- "missing import path; require quoted string"},
-
- % loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header ';'
- "missing { after if clause"},
-
- % loadsys package imports LFUNC LNAME '(' ')' '{' LSWITCH if_header ';'
- "missing { after switch clause"},
-
- % loadsys package imports LFUNC LNAME '(' ')' '{' LFOR for_header ';'
- "missing { after for clause"},
-
- % loadsys package imports LFUNC LNAME '(' ')' '{' LFOR ';' LBODY
- "missing { after for clause"},
-
- % loadsys package imports LFUNC LNAME '(' ')' ';' '{'
- "unexpected semicolon or newline before {"},
-
- % loadsys package imports LTYPE LNAME ';'
- "unexpected semicolon or newline in type declaration"},
-
- % loadsys package imports LCHAN '}'
- "unexpected } in channel type"},
-
- % loadsys package imports LCHAN ')'
- "unexpected ) in channel type"},
-
- % loadsys package imports LCHAN ','
- "unexpected comma in channel type"},
-
- % loadsys package imports LFUNC LNAME '(' ')' '{' if_stmt ';' LELSE
- "unexpected semicolon or newline before else"},
-
- % loadsys package imports LTYPE LNAME LINTERFACE '{' LNAME ',' LNAME
- "name list not allowed in interface type"},
-
- % loadsys package imports LFUNC LNAME '(' ')' '{' LFOR LVAR LNAME '=' LNAME
- "var declaration not allowed in for initializer"},
-
- % loadsys package imports LVAR LNAME '[' ']' LNAME '{'
- "unexpected { at end of statement"},
-
- % loadsys package imports LFUNC LNAME '(' ')' '{' LVAR LNAME '[' ']' LNAME '{'
- "unexpected { at end of statement"},
-
- % loadsys package imports LFUNC LNAME '(' ')' '{' LDEFER LNAME ';'
- "argument to go/defer must be function call"},
-
- % loadsys package imports LVAR LNAME '=' LNAME '{' LNAME ';'
- "need trailing comma before newline in composite literal"},
-
- % loadsys package imports LVAR LNAME '=' comptype '{' LNAME ';'
- "need trailing comma before newline in composite literal"},
-
- % loadsys package imports LFUNC LNAME '(' ')' '{' LFUNC LNAME
- "nested func not allowed"},
-
- % loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header loop_body LELSE ';'
- "else must be followed by if or statement block"},
-}
diff --git a/src/cmd/internal/gc/yaccerrors.go b/src/cmd/internal/gc/yaccerrors.go
deleted file mode 100644
index 9dc54d9c8c..0000000000
--- a/src/cmd/internal/gc/yaccerrors.go
+++ /dev/null
@@ -1,194 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build ignore
-
-// This program implements the core idea from
-//
-// Clinton L. Jeffery, Generating LR syntax error messages from examples,
-// ACM TOPLAS 25(5) (September 2003). http://doi.acm.org/10.1145/937563.937566
-//
-// It reads Bison's summary of a grammar followed by a file
-// like go.errors, replacing lines beginning with % by the
-// yystate and yychar that will be active when an error happens
-// while parsing that line.
-//
-// Unlike the system described in the paper, the lines in go.errors
-// give grammar symbol name lists, not actual program fragments.
-// This is a little less programmer-friendly but doesn't require being
-// able to run the text through lex.c.
-
-package main
-
-import (
- "bufio"
- "fmt"
- "io"
- "log"
- "os"
- "strconv"
- "strings"
-)
-
-func xatoi(s string) int {
- n, err := strconv.Atoi(s)
- if err != nil {
- log.Fatal(err)
- }
- return n
-}
-
-func trimParen(s string) string {
- s = strings.TrimPrefix(s, "(")
- s = strings.TrimSuffix(s, ")")
- return s
-}
-
-type action struct {
- token string
- n int
-}
-
-var shift = map[int][]action{}
-var reduce = map[int][]action{}
-
-type rule struct {
- lhs string
- size int
-}
-
-var rules = map[int]rule{}
-
-func readYaccOutput() {
- r, err := os.Open("y.output")
- if err != nil {
- log.Fatal(err)
- }
- defer r.Close()
-
- var state int
-
- scanner := bufio.NewScanner(r)
- for scanner.Scan() {
- f := strings.Fields(scanner.Text())
- nf := len(f)
-
- if nf >= 4 && f[1] == "terminals," && f[3] == "nonterminals" {
- // We're done.
- break
- }
-
- if nf >= 2 && f[0] == "state" {
- state = xatoi(f[1])
- continue
- }
- if nf >= 3 && (f[1] == "shift" || f[1] == "goto") {
- shift[state] = append(shift[state], action{f[0], xatoi(f[2])})
- continue
- }
- if nf >= 3 && f[1] == "reduce" {
- reduce[state] = append(reduce[state], action{f[0], xatoi(f[2])})
- continue
- }
- if nf >= 3 && strings.HasSuffix(f[0], ":") && strings.HasPrefix(f[nf-1], "(") && strings.HasSuffix(f[nf-1], ")") {
- n := xatoi(trimParen(f[nf-1]))
-
- size := nf - 2
- if size == 1 && f[1] == "." {
- size = 0
- }
-
- rules[n] = rule{strings.TrimSuffix(f[0], ":"), size}
- continue
- }
- }
-}
-
-func runMachine(w io.Writer, s string) {
- f := strings.Fields(s)
-
- // Run it through the LR machine and print the induced "yystate, yychar,"
- // at the point where the error happens.
-
- var stack []int
- state := 0
- i := 1
- tok := ""
-
-Loop:
- if tok == "" && i < len(f) {
- tok = f[i]
- i++
- }
-
- for _, a := range shift[state] {
- if a.token == tok {
- if false {
- fmt.Println("SHIFT ", tok, " ", state, " -> ", a)
- }
- stack = append(stack, state)
- state = a.n
- tok = ""
- goto Loop
- }
- }
-
- for _, a := range reduce[state] {
- if a.token == tok || a.token == "." {
- stack = append(stack, state)
- rule, ok := rules[a.n]
- if !ok {
- log.Fatal("missing rule")
- }
- stack = stack[:len(stack)-rule.size]
- state = stack[len(stack)-1]
- stack = stack[:len(stack)-1]
- if tok != "" {
- i--
- }
- tok = rule.lhs
- if false {
- fmt.Println("REDUCE ", stack, " ", state, " ", tok, " rule ", rule)
- }
- goto Loop
- }
- }
-
- // No shift or reduce applied - found the error.
- fmt.Fprintf(w, "\t{%d, %s,\n", state, tok)
-}
-
-func processGoErrors() {
- r, err := os.Open("go.errors")
- if err != nil {
- log.Fatal(err)
- }
- defer r.Close()
-
- w, err := os.Create("yymsg.go")
- if err != nil {
- log.Fatal(err)
- }
- defer w.Close()
-
- fmt.Fprintf(w, "// DO NOT EDIT - generated with go generate\n\n")
-
- scanner := bufio.NewScanner(r)
- for scanner.Scan() {
- s := scanner.Text()
-
- // Treat % as first field on line as introducing a pattern (token sequence).
- if strings.HasPrefix(strings.TrimSpace(s), "%") {
- runMachine(w, s)
- continue
- }
-
- fmt.Fprintln(w, s)
- }
-}
-
-func main() {
- readYaccOutput()
- processGoErrors()
-}
diff --git a/src/cmd/internal/gc/yymsg.go b/src/cmd/internal/gc/yymsg.go
deleted file mode 100644
index cb45cb8d1b..0000000000
--- a/src/cmd/internal/gc/yymsg.go
+++ /dev/null
@@ -1,83 +0,0 @@
-// DO NOT EDIT - generated with go generate
-
-// Copyright 2010 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.
-
-// Example-based syntax error messages.
-// See yaccerrors.go.
-
-package gc
-
-var yymsg = []struct {
- yystate int
- yychar int
- msg string
-}{
- // Each line of the form % token list
- // is converted by yaccerrors.go into the yystate and yychar caused
- // by that token list.
-
- {332, ',',
- "unexpected comma during import block"},
-
- {89, ';',
- "missing import path; require quoted string"},
-
- {390, ';',
- "missing { after if clause"},
-
- {387, ';',
- "missing { after switch clause"},
-
- {279, ';',
- "missing { after for clause"},
-
- {498, LBODY,
- "missing { after for clause"},
-
- {17, '{',
- "unexpected semicolon or newline before {"},
-
- {111, ';',
- "unexpected semicolon or newline in type declaration"},
-
- {78, '}',
- "unexpected } in channel type"},
-
- {78, ')',
- "unexpected ) in channel type"},
-
- {78, ',',
- "unexpected comma in channel type"},
-
- {416, LELSE,
- "unexpected semicolon or newline before else"},
-
- {329, ',',
- "name list not allowed in interface type"},
-
- {279, LVAR,
- "var declaration not allowed in for initializer"},
-
- {25, '{',
- "unexpected { at end of statement"},
-
- {371, '{',
- "unexpected { at end of statement"},
-
- {122, ';',
- "argument to go/defer must be function call"},
-
- {398, ';',
- "need trailing comma before newline in composite literal"},
-
- {414, ';',
- "need trailing comma before newline in composite literal"},
-
- {124, LNAME,
- "nested func not allowed"},
-
- {650, ';',
- "else must be followed by if or statement block"},
-}
diff --git a/src/cmd/internal/gcprog/gcprog.go b/src/cmd/internal/gcprog/gcprog.go
new file mode 100644
index 0000000000..5845f7d65e
--- /dev/null
+++ b/src/cmd/internal/gcprog/gcprog.go
@@ -0,0 +1,298 @@
+// 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 gcprog implements an encoder for packed GC pointer bitmaps,
+// known as GC programs.
+//
+// Program Format
+//
+// The GC program encodes a sequence of 0 and 1 bits indicating scalar or pointer words in an object.
+// The encoding is a simple Lempel-Ziv program, with codes to emit literal bits and to repeat the
+// last n bits c times.
+//
+// The possible codes are:
+//
+// 00000000: stop
+// 0nnnnnnn: emit n bits copied from the next (n+7)/8 bytes, least significant bit first
+// 10000000 n c: repeat the previous n bits c times; n, c are varints
+// 1nnnnnnn c: repeat the previous n bits c times; c is a varint
+//
+// The numbers n and c, when they follow a code, are encoded as varints
+// using the same encoding as encoding/binary's Uvarint.
+//
+package gcprog
+
+import (
+ "fmt"
+ "io"
+)
+
+const progMaxLiteral = 127 // maximum n for literal n bit code
+
+// A Writer is an encoder for GC programs.
+//
+// The typical use of a Writer is to call Init, maybe call Debug,
+// make a sequence of Ptr, Advance, Repeat, and Append calls
+// to describe the data type, and then finally call End.
+type Writer struct {
+ writeByte func(byte)
+ symoff int
+ index int64
+ b [progMaxLiteral]byte
+ nb int
+ debug io.Writer
+ debugBuf []byte
+}
+
+// Init initializes w to write a new GC program
+// by calling writeByte for each byte in the program.
+func (w *Writer) Init(writeByte func(byte)) {
+ w.writeByte = writeByte
+}
+
+// Debug causes the writer to print a debugging trace to out
+// during future calls to methods like Ptr, Advance, and End.
+// It also enables debugging checks during the encoding.
+func (w *Writer) Debug(out io.Writer) {
+ w.debug = out
+}
+
+// BitIndex returns the number of bits written to the bit stream so far.
+func (w *Writer) BitIndex() int64 {
+ return w.index
+}
+
+// byte writes the byte x to the output.
+func (w *Writer) byte(x byte) {
+ if w.debug != nil {
+ w.debugBuf = append(w.debugBuf, x)
+ }
+ w.writeByte(x)
+}
+
+// End marks the end of the program, writing any remaining bytes.
+func (w *Writer) End() {
+ w.flushlit()
+ w.byte(0)
+ if w.debug != nil {
+ index := progbits(w.debugBuf)
+ if index != w.index {
+ println("gcprog: End wrote program for", index, "bits, but current index is", w.index)
+ panic("gcprog: out of sync")
+ }
+ }
+}
+
+// Ptr emits a 1 into the bit stream at the given bit index.
+// that is, it records that the index'th word in the object memory is a pointer.
+// Any bits between the current index and the new index
+// are set to zero, meaning the corresponding words are scalars.
+func (w *Writer) Ptr(index int64) {
+ if index < w.index {
+ println("gcprog: Ptr at index", index, "but current index is", w.index)
+ panic("gcprog: invalid Ptr index")
+ }
+ w.ZeroUntil(index)
+ if w.debug != nil {
+ fmt.Fprintf(w.debug, "gcprog: ptr at %d\n", index)
+ }
+ w.lit(1)
+}
+
+// ShouldRepeat reports whether it would be worthwhile to
+// use a Repeat to describe c elements of n bits each,
+// compared to just emitting c copies of the n-bit description.
+func (w *Writer) ShouldRepeat(n, c int64) bool {
+ // Should we lay out the bits directly instead of
+ // encoding them as a repetition? Certainly if count==1,
+ // since there's nothing to repeat, but also if the total
+ // size of the plain pointer bits for the type will fit in
+ // 4 or fewer bytes, since using a repetition will require
+ // flushing the current bits plus at least one byte for
+ // the repeat size and one for the repeat count.
+ return c > 1 && c*n > 4*8
+}
+
+// Repeat emits an instruction to repeat the description
+// of the last n words c times (including the initial description, c+1 times in total).
+func (w *Writer) Repeat(n, c int64) {
+ if n == 0 || c == 0 {
+ return
+ }
+ w.flushlit()
+ if w.debug != nil {
+ fmt.Fprintf(w.debug, "gcprog: repeat %d × %d\n", n, c)
+ }
+ if n < 128 {
+ w.byte(0x80 | byte(n))
+ } else {
+ w.byte(0x80)
+ w.varint(n)
+ }
+ w.varint(c)
+ w.index += n * c
+}
+
+// ZeroUntil adds zeros to the bit stream until reaching the given index;
+// that is, it records that the words from the most recent pointer until
+// the index'th word are scalars.
+// ZeroUntil is usually called in preparation for a call to Repeat, Append, or End.
+func (w *Writer) ZeroUntil(index int64) {
+ if index < w.index {
+ println("gcprog: Advance", index, "but index is", w.index)
+ panic("gcprog: invalid Advance index")
+ }
+ skip := (index - w.index)
+ if skip == 0 {
+ return
+ }
+ if skip < 4*8 {
+ if w.debug != nil {
+ fmt.Fprintf(w.debug, "gcprog: advance to %d by literals\n", index)
+ }
+ for i := int64(0); i < skip; i++ {
+ w.lit(0)
+ }
+ return
+ }
+
+ if w.debug != nil {
+ fmt.Fprintf(w.debug, "gcprog: advance to %d by repeat\n", index)
+ }
+ w.lit(0)
+ w.flushlit()
+ w.Repeat(1, skip-1)
+}
+
+// Append emits the given GC program into the current output.
+// The caller asserts that the program emits n bits (describes n words),
+// and Append panics if that is not true.
+func (w *Writer) Append(prog []byte, n int64) {
+ w.flushlit()
+ if w.debug != nil {
+ fmt.Fprintf(w.debug, "gcprog: append prog for %d ptrs\n", n)
+ fmt.Fprintf(w.debug, "\t")
+ }
+ n1 := progbits(prog)
+ if n1 != n {
+ panic("gcprog: wrong bit count in append")
+ }
+ // The last byte of the prog terminates the program.
+ // Don't emit that, or else our own program will end.
+ for i, x := range prog[:len(prog)-1] {
+ if w.debug != nil {
+ if i > 0 {
+ fmt.Fprintf(w.debug, " ")
+ }
+ fmt.Fprintf(w.debug, "%02x", x)
+ }
+ w.byte(x)
+ }
+ if w.debug != nil {
+ fmt.Fprintf(w.debug, "\n")
+ }
+ w.index += n
+}
+
+// progbits returns the length of the bit stream encoded by the program p.
+func progbits(p []byte) int64 {
+ var n int64
+ for len(p) > 0 {
+ x := p[0]
+ p = p[1:]
+ if x == 0 {
+ break
+ }
+ if x&0x80 == 0 {
+ count := x &^ 0x80
+ n += int64(count)
+ p = p[(count+7)/8:]
+ continue
+ }
+ nbit := int64(x &^ 0x80)
+ if nbit == 0 {
+ nbit, p = readvarint(p)
+ }
+ var count int64
+ count, p = readvarint(p)
+ n += nbit * count
+ }
+ if len(p) > 0 {
+ println("gcprog: found end instruction after", n, "ptrs, with", len(p), "bytes remaining")
+ panic("gcprog: extra data at end of program")
+ }
+ return n
+}
+
+// readvarint reads a varint from p, returning the value and the remainder of p.
+func readvarint(p []byte) (int64, []byte) {
+ var v int64
+ var nb uint
+ for {
+ c := p[0]
+ p = p[1:]
+ v |= int64(c&^0x80) << nb
+ nb += 7
+ if c&0x80 == 0 {
+ break
+ }
+ }
+ return v, p
+}
+
+// lit adds a single literal bit to w.
+func (w *Writer) lit(x byte) {
+ if w.nb == progMaxLiteral {
+ w.flushlit()
+ }
+ w.b[w.nb] = x
+ w.nb++
+ w.index++
+}
+
+// varint emits the varint encoding of x.
+func (w *Writer) varint(x int64) {
+ if x < 0 {
+ panic("gcprog: negative varint")
+ }
+ for x >= 0x80 {
+ w.byte(byte(0x80 | x))
+ x >>= 7
+ }
+ w.byte(byte(x))
+}
+
+// flushlit flushes any pending literal bits.
+func (w *Writer) flushlit() {
+ if w.nb == 0 {
+ return
+ }
+ if w.debug != nil {
+ fmt.Fprintf(w.debug, "gcprog: flush %d literals\n", w.nb)
+ fmt.Fprintf(w.debug, "\t%v\n", w.b[:w.nb])
+ fmt.Fprintf(w.debug, "\t%02x", byte(w.nb))
+ }
+ w.byte(byte(w.nb))
+ var bits uint8
+ for i := 0; i < w.nb; i++ {
+ bits |= w.b[i] << uint(i%8)
+ if (i+1)%8 == 0 {
+ if w.debug != nil {
+ fmt.Fprintf(w.debug, " %02x", bits)
+ }
+ w.byte(bits)
+ bits = 0
+ }
+ }
+ if w.nb%8 != 0 {
+ if w.debug != nil {
+ fmt.Fprintf(w.debug, " %02x", bits)
+ }
+ w.byte(bits)
+ }
+ if w.debug != nil {
+ fmt.Fprintf(w.debug, "\n")
+ }
+ w.nb = 0
+}
diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go
index 6e00cb55ab..9e643932be 100644
--- a/src/cmd/internal/obj/arm64/asm7.go
+++ b/src/cmd/internal/obj/arm64/asm7.go
@@ -2677,8 +2677,8 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
case 59: /* stxr/stlxr */
o1 = opstore(ctxt, int(p.As))
- if p.To2.Type != obj.TYPE_NONE {
- o1 |= uint32(p.To2.Reg&31) << 16
+ if p.RegTo2 != obj.REG_NONE {
+ o1 |= uint32(p.RegTo2&31) << 16
} else {
o1 |= 0x1F << 16
}
diff --git a/src/cmd/internal/obj/line_test.go b/src/cmd/internal/obj/line_test.go
index dde5d64e17..5486f0d648 100644
--- a/src/cmd/internal/obj/line_test.go
+++ b/src/cmd/internal/obj/line_test.go
@@ -13,13 +13,13 @@ func TestLineHist(t *testing.T) {
ctxt := new(Link)
ctxt.Hash = make(map[SymVer]*LSym)
- Linklinehist(ctxt, 1, "a.c", 0)
- Linklinehist(ctxt, 3, "a.h", 0)
- Linklinehist(ctxt, 5, "", 0)
- Linklinehist(ctxt, 7, "linedir", 2)
- Linklinehist(ctxt, 9, "", 0)
- Linklinehist(ctxt, 11, "b.c", 0)
- Linklinehist(ctxt, 13, "", 0)
+ ctxt.LineHist.Push(1, "a.c")
+ ctxt.LineHist.Push(3, "a.h")
+ ctxt.LineHist.Pop(5)
+ ctxt.LineHist.Update(7, "linedir", 2)
+ ctxt.LineHist.Pop(9)
+ ctxt.LineHist.Push(11, "b.c")
+ ctxt.LineHist.Pop(13)
var expect = []string{
0: "??:0",
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index 9f5e87b4c3..2fc12c1eb1 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -206,7 +206,6 @@ type Prog struct {
From Addr
From3 Addr
To Addr
- To2 Addr
Opt interface{}
Forwd *Prog
Pcond *Prog
@@ -217,12 +216,12 @@ type Prog struct {
Spadj int32
As int16
Reg int16
+ RegTo2 int16 // 2nd register output operand
Mark uint16
Optab uint16
Scond uint8
Back uint8
Ft uint8
- F3t uint8
Tt uint8
Isize uint8
Printed uint8
diff --git a/src/cmd/internal/obj/mgc0.go b/src/cmd/internal/obj/mgc0.go
index 2407deaf32..a385d607bb 100644
--- a/src/cmd/internal/obj/mgc0.go
+++ b/src/cmd/internal/obj/mgc0.go
@@ -21,16 +21,6 @@ package obj
// Used by cmd/gc.
-const (
- GcBits = 4
- BitsPerPointer = 2
- BitsDead = 0
- BitsScalar = 1
- BitsPointer = 2
- BitsMask = 3
- PointersPerByte = 8 / BitsPerPointer
-)
-
const (
InsData = 1 + iota
InsArray
diff --git a/src/cmd/internal/obj/obj.go b/src/cmd/internal/obj/obj.go
index 39db2396e7..af3290d3a5 100644
--- a/src/cmd/internal/obj/obj.go
+++ b/src/cmd/internal/obj/obj.go
@@ -241,12 +241,6 @@ func (h *LineHist) LineString(lineno int) string {
return text
}
-// TODO(rsc): Replace call sites with use of ctxt.LineHist.
-// Note that all call sites use showAll=false, showFullPath=false.
-func Linklinefmt(ctxt *Link, lineno int, showAll, showFullPath bool) string {
- return ctxt.LineHist.LineString(lineno)
-}
-
// FileLine returns the file name and line number
// at the top of the stack for the given lineno.
func (h *LineHist) FileLine(lineno int) (file string, line int) {
@@ -287,30 +281,3 @@ func linkgetline(ctxt *Link, lineno int32, f **LSym, l *int32) {
func Linkprfile(ctxt *Link, line int) {
fmt.Printf("%s ", ctxt.LineHist.LineString(line))
}
-
-// Linklinehist pushes, amends, or pops an entry on the line history stack.
-// If f != "" and n == 0, the call pushes the start of a new file named f at lineno.
-// If f != "" and n > 0, the call amends the top of the stack to record that lineno
-// now corresponds to f at line n.
-// If f == "", the call pops the topmost entry from the stack, picking up
-// the parent file at the line following the one where the corresponding push occurred.
-//
-// If n < 0, linklinehist records f as a package required by the current compilation
-// (nothing to do with line numbers).
-//
-// TODO(rsc): Replace uses with direct calls to ctxt.Hist methods.
-func Linklinehist(ctxt *Link, lineno int, f string, n int) {
- switch {
- case n < 0:
- ctxt.AddImport(f)
-
- case f == "":
- ctxt.LineHist.Pop(lineno)
-
- case n == 0:
- ctxt.LineHist.Push(lineno, f)
-
- default:
- ctxt.LineHist.Update(lineno, f, n)
- }
-}
diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go
index b0b209184f..efecae62ac 100644
--- a/src/cmd/internal/obj/util.go
+++ b/src/cmd/internal/obj/util.go
@@ -213,10 +213,17 @@ func Getgoos() string {
}
func Getgoarm() string {
- return envOr("GOARM", defaultGOARM)
+ switch v := envOr("GOARM", defaultGOARM); v {
+ case "5", "6", "7":
+ return v
+ }
+ // Fail here, rather than validate at multiple call sites.
+ log.Fatalf("Invalid GOARM value. Must be 5, 6, or 7.")
+ panic("unreachable")
}
func Getgo386() string {
+ // Validated by cmd/8g.
return envOr("GO386", defaultGO386)
}
@@ -234,7 +241,7 @@ func Atoi(s string) int {
}
func (p *Prog) Line() string {
- return Linklinefmt(p.Ctxt, int(p.Lineno), false, false)
+ return p.Ctxt.LineHist.LineString(int(p.Lineno))
}
var armCondCode = []string{
@@ -320,8 +327,8 @@ func (p *Prog) String() string {
if p.To.Type != TYPE_NONE {
fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.To))
}
- if p.To2.Type != TYPE_NONE {
- fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.To2))
+ if p.RegTo2 != REG_NONE {
+ fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.RegTo2)))
}
return buf.String()
}
@@ -333,7 +340,7 @@ func (ctxt *Link) NewProg() *Prog {
}
func (ctxt *Link) Line(n int) string {
- return Linklinefmt(ctxt, n, false, false)
+ return ctxt.LineHist.LineString(n)
}
func Getcallerpc(interface{}) uintptr {
diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go
index 0c0cc04548..2b9c2670df 100644
--- a/src/cmd/internal/obj/x86/asm6.go
+++ b/src/cmd/internal/obj/x86/asm6.go
@@ -2974,15 +2974,12 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
if p.Ft == 0 {
p.Ft = uint8(oclass(ctxt, p, &p.From))
}
- if p.F3t == 0 {
- p.F3t = uint8(oclass(ctxt, p, &p.From3))
- }
if p.Tt == 0 {
p.Tt = uint8(oclass(ctxt, p, &p.To))
}
ft := int(p.Ft) * Ymax
- f3t := int(p.F3t) * Ymax
+ f3t := oclass(ctxt, p, &p.From3) * Ymax
tt := int(p.Tt) * Ymax
xo := obj.Bool2int(o.op[0] == 0x0f)
diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go
index 7a4fc128e6..4798c8f7fb 100644
--- a/src/cmd/internal/obj/x86/obj6.go
+++ b/src/cmd/internal/obj/x86/obj6.go
@@ -350,9 +350,6 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
if p.From3.Name == obj.NAME_EXTERN {
ctxt.Diag("don't know how to handle %v with -dynlink", p)
}
- if p.To2.Name == obj.NAME_EXTERN {
- ctxt.Diag("don't know how to handle %v with -dynlink", p)
- }
var source *obj.Addr
if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
diff --git a/src/cmd/internal/objfile/macho.go b/src/cmd/internal/objfile/macho.go
index a6cd02b930..7371c0d9d1 100644
--- a/src/cmd/internal/objfile/macho.go
+++ b/src/cmd/internal/objfile/macho.go
@@ -13,6 +13,8 @@ import (
"sort"
)
+const stabTypeMask = 0xe0
+
type machoFile struct {
macho *macho.File
}
@@ -34,12 +36,19 @@ func (f *machoFile) symbols() ([]Sym, error) {
// We infer the size of a symbol by looking at where the next symbol begins.
var addrs []uint64
for _, s := range f.macho.Symtab.Syms {
- addrs = append(addrs, s.Value)
+ // Skip stab debug info.
+ if s.Type&stabTypeMask == 0 {
+ addrs = append(addrs, s.Value)
+ }
}
sort.Sort(uint64s(addrs))
var syms []Sym
for _, s := range f.macho.Symtab.Syms {
+ if s.Type&stabTypeMask != 0 {
+ // Skip stab debug info.
+ continue
+ }
sym := Sym{Name: s.Name, Addr: s.Value, Code: '?'}
i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value })
if i < len(addrs) {
diff --git a/src/cmd/6l/asm.go b/src/cmd/link/internal/amd64/asm.go
similarity index 89%
rename from src/cmd/6l/asm.go
rename to src/cmd/link/internal/amd64/asm.go
index a025ce6ea6..74ec9dd3ea 100644
--- a/src/cmd/6l/asm.go
+++ b/src/cmd/link/internal/amd64/asm.go
@@ -28,11 +28,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package amd64
import (
- "cmd/internal/ld"
"cmd/internal/obj"
+ "cmd/link/internal/ld"
"debug/elf"
"fmt"
"log"
@@ -44,24 +44,6 @@ func PADDR(x uint32) uint32 {
var zeroes string
-func needlib(name string) int {
- if name[0] == '\x00' {
- return 0
- }
-
- /* reuse hash code in symbol table */
- p := fmt.Sprintf(".elfload.%s", name)
-
- s := ld.Linklookup(ld.Ctxt, p, 0)
-
- if s.Type == 0 {
- s.Type = 100 // avoid SDATA, etc.
- return 1
- }
-
- return 0
-}
-
func Addcall(ctxt *ld.Link, s *ld.LSym, t *ld.LSym) int64 {
s.Reachable = true
i := s.Size
@@ -292,7 +274,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
break
}
if ld.Iself {
- adddynsym(ld.Ctxt, targ)
+ ld.Adddynsym(ld.Ctxt, targ)
rela := ld.Linklookup(ld.Ctxt, ".rela", 0)
ld.Addaddrplus(ld.Ctxt, rela, s, int64(r.Off))
if r.Siz == 8 {
@@ -316,7 +298,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
// just in case the C code assigns to the variable,
// and of course it only works for single pointers,
// but we only need to support cgo and that's all it needs.
- adddynsym(ld.Ctxt, targ)
+ ld.Adddynsym(ld.Ctxt, targ)
got := ld.Linklookup(ld.Ctxt, ".got", 0)
s.Type = got.Type | obj.SSUB
@@ -423,9 +405,9 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
v = uint32(rs.Dynid)
v |= 1 << 27 // external relocation
} else {
- v = uint32((rs.Sect.(*ld.Section)).Extnum)
+ v = uint32(rs.Sect.Extnum)
if v == 0 {
- ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, (rs.Sect.(*ld.Section)).Name, rs.Type)
+ ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, rs.Sect.Name, rs.Type)
return -1
}
}
@@ -544,7 +526,7 @@ func addpltsym(s *ld.LSym) {
return
}
- adddynsym(ld.Ctxt, s)
+ ld.Adddynsym(ld.Ctxt, s)
if ld.Iself {
plt := ld.Linklookup(ld.Ctxt, ".plt", 0)
@@ -612,7 +594,7 @@ func addgotsym(s *ld.LSym) {
return
}
- adddynsym(ld.Ctxt, s)
+ ld.Adddynsym(ld.Ctxt, s)
got := ld.Linklookup(ld.Ctxt, ".got", 0)
s.Got = int32(got.Size)
ld.Adduint64(ld.Ctxt, got, 0)
@@ -629,80 +611,6 @@ func addgotsym(s *ld.LSym) {
}
}
-func adddynsym(ctxt *ld.Link, s *ld.LSym) {
- if s.Dynid >= 0 {
- return
- }
-
- if ld.Iself {
- s.Dynid = int32(ld.Nelfsym)
- ld.Nelfsym++
-
- d := ld.Linklookup(ctxt, ".dynsym", 0)
-
- name := s.Extname
- ld.Adduint32(ctxt, d, uint32(ld.Addstring(ld.Linklookup(ctxt, ".dynstr", 0), name)))
-
- /* type */
- t := ld.STB_GLOBAL << 4
-
- if s.Cgoexport != 0 && s.Type&obj.SMASK == obj.STEXT {
- t |= ld.STT_FUNC
- } else {
- t |= ld.STT_OBJECT
- }
- ld.Adduint8(ctxt, d, uint8(t))
-
- /* reserved */
- ld.Adduint8(ctxt, d, 0)
-
- /* section where symbol is defined */
- if s.Type == obj.SDYNIMPORT {
- ld.Adduint16(ctxt, d, ld.SHN_UNDEF)
- } else {
- ld.Adduint16(ctxt, d, 1)
- }
-
- /* value */
- if s.Type == obj.SDYNIMPORT {
- ld.Adduint64(ctxt, d, 0)
- } else {
- ld.Addaddr(ctxt, d, s)
- }
-
- /* size of object */
- ld.Adduint64(ctxt, d, uint64(s.Size))
-
- if s.Cgoexport&ld.CgoExportDynamic == 0 && s.Dynimplib != "" && needlib(s.Dynimplib) != 0 {
- ld.Elfwritedynent(ld.Linklookup(ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(ld.Linklookup(ctxt, ".dynstr", 0), s.Dynimplib)))
- }
- } else if ld.HEADTYPE == obj.Hdarwin {
- ld.Diag("adddynsym: missed symbol %s (%s)", s.Name, s.Extname)
- } else if ld.HEADTYPE == obj.Hwindows {
- } else // already taken care of
- {
- ld.Diag("adddynsym: unsupported binary format")
- }
-}
-
-func adddynlib(lib string) {
- if needlib(lib) == 0 {
- return
- }
-
- if ld.Iself {
- s := ld.Linklookup(ld.Ctxt, ".dynstr", 0)
- if s.Size == 0 {
- ld.Addstring(s, "")
- }
- ld.Elfwritedynent(ld.Linklookup(ld.Ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(s, lib)))
- } else if ld.HEADTYPE == obj.Hdarwin {
- ld.Machoadddynlib(lib)
- } else {
- ld.Diag("adddynlib: unsupported binary format")
- }
-}
-
func asmb() {
if ld.Debug['v'] != 0 {
fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
@@ -802,7 +710,7 @@ func asmb() {
symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen)
case obj.Hdarwin:
- symo = int64(ld.Segdata.Fileoff + uint64(ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND))) + uint64(machlink))
+ symo = int64(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
case obj.Hlinux,
obj.Hfreebsd,
diff --git a/src/cmd/6l/l.go b/src/cmd/link/internal/amd64/l.go
similarity index 96%
rename from src/cmd/6l/l.go
rename to src/cmd/link/internal/amd64/l.go
index 6b42088de3..2537419eff 100644
--- a/src/cmd/6l/l.go
+++ b/src/cmd/link/internal/amd64/l.go
@@ -28,7 +28,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package amd64
const (
thechar = '6'
@@ -40,7 +40,8 @@ const (
MINLC = 1
)
-/* Used by ../ld/dwarf.c */
+/* Used by ../internal/ld/dwarf.go */
const (
DWARFREGSP = 7
+ DWARFREGLR = 16
)
diff --git a/src/cmd/6l/obj.go b/src/cmd/link/internal/amd64/obj.go
similarity index 91%
rename from src/cmd/6l/obj.go
rename to src/cmd/link/internal/amd64/obj.go
index 9e6dc60e2d..1aa4422ed9 100644
--- a/src/cmd/6l/obj.go
+++ b/src/cmd/link/internal/amd64/obj.go
@@ -28,18 +28,18 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package amd64
import (
- "cmd/internal/ld"
"cmd/internal/obj"
+ "cmd/link/internal/ld"
"fmt"
"log"
)
// Reading object files.
-func main() {
+func Main() {
linkarchinit()
ld.Ldmain()
}
@@ -59,10 +59,9 @@ func linkarchinit() {
ld.Thearch.Maxalign = MaxAlign
ld.Thearch.Minlc = MINLC
ld.Thearch.Dwarfregsp = DWARFREGSP
+ ld.Thearch.Dwarfreglr = DWARFREGLR
- ld.Thearch.Adddynlib = adddynlib
ld.Thearch.Adddynrel = adddynrel
- ld.Thearch.Adddynsym = adddynsym
ld.Thearch.Archinit = archinit
ld.Thearch.Archreloc = archreloc
ld.Thearch.Archrelocvariant = archrelocvariant
@@ -91,7 +90,7 @@ func archinit() {
ld.Linkmode = ld.LinkInternal
}
- if ld.Buildmode == ld.BuildmodeCShared || ld.DynlinkingGo() {
+ if ld.Buildmode == ld.BuildmodeCArchive || ld.Buildmode == ld.BuildmodeCShared || ld.DynlinkingGo() {
ld.Linkmode = ld.LinkExternal
}
@@ -169,14 +168,6 @@ func archinit() {
ld.Elfinit()
ld.HEADR = ld.ELFRESERVE
- if ld.Buildmode == ld.BuildmodeShared {
- // When building a shared library we write a package list
- // note that can get quite large. The external linker will
- // re-layout all the sections anyway, so making this larger
- // just wastes a little space in the intermediate object
- // file, not the final shared library.
- ld.HEADR *= 3
- }
if ld.INITTEXT == -1 {
ld.INITTEXT = (1 << 22) + int64(ld.HEADR)
}
diff --git a/src/cmd/link/internal/amd64/z.go b/src/cmd/link/internal/amd64/z.go
new file mode 100644
index 0000000000..f70035b9e3
--- /dev/null
+++ b/src/cmd/link/internal/amd64/z.go
@@ -0,0 +1 @@
+package amd64
diff --git a/src/cmd/5l/asm.go b/src/cmd/link/internal/arm/asm.go
similarity index 87%
rename from src/cmd/5l/asm.go
rename to src/cmd/link/internal/arm/asm.go
index 85ea684fc7..39d4550917 100644
--- a/src/cmd/5l/asm.go
+++ b/src/cmd/link/internal/arm/asm.go
@@ -28,33 +28,15 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package arm
import (
- "cmd/internal/ld"
"cmd/internal/obj"
+ "cmd/link/internal/ld"
"fmt"
"log"
)
-func needlib(name string) int {
- if name[0] == '\x00' {
- return 0
- }
-
- /* reuse hash code in symbol table */
- p := fmt.Sprintf(".dynlib.%s", name)
-
- s := ld.Linklookup(ld.Ctxt, p, 0)
-
- if s.Type == 0 {
- s.Type = 100 // avoid SDATA, etc.
- return 1
- }
-
- return 0
-}
-
func gentext() {
}
@@ -194,7 +176,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
break
}
if ld.Iself {
- adddynsym(ld.Ctxt, targ)
+ ld.Adddynsym(ld.Ctxt, targ)
rel := ld.Linklookup(ld.Ctxt, ".rel", 0)
ld.Addaddrplus(ld.Ctxt, rel, s, int64(r.Off))
ld.Adduint32(ld.Ctxt, rel, ld.ELF32_R_INFO(uint32(targ.Dynid), ld.R_ARM_GLOB_DAT)) // we need a nil + A dynmic reloc
@@ -297,9 +279,9 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
v = uint32(rs.Dynid)
v |= 1 << 27 // external relocation
} else {
- v = uint32((rs.Sect.(*ld.Section)).Extnum)
+ v = uint32(rs.Sect.Extnum)
if v == 0 {
- ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, (rs.Sect.(*ld.Section)).Name, rs.Type)
+ ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, rs.Sect.Name, rs.Type)
return -1
}
}
@@ -440,7 +422,7 @@ func addpltsym(ctxt *ld.Link, s *ld.LSym) {
return
}
- adddynsym(ctxt, s)
+ ld.Adddynsym(ctxt, s)
if ld.Iself {
plt := ld.Linklookup(ctxt, ".plt", 0)
@@ -495,7 +477,7 @@ func addgotsym(ctxt *ld.Link, s *ld.LSym) {
return
}
- adddynsym(ctxt, s)
+ ld.Adddynsym(ctxt, s)
got := ld.Linklookup(ctxt, ".got", 0)
s.Got = int32(got.Size)
ld.Adduint32(ctxt, got, 0)
@@ -509,72 +491,6 @@ func addgotsym(ctxt *ld.Link, s *ld.LSym) {
}
}
-func adddynsym(ctxt *ld.Link, s *ld.LSym) {
- if s.Dynid >= 0 {
- return
- }
-
- if ld.Iself {
- s.Dynid = int32(ld.Nelfsym)
- ld.Nelfsym++
-
- d := ld.Linklookup(ctxt, ".dynsym", 0)
-
- /* name */
- name := s.Extname
-
- ld.Adduint32(ctxt, d, uint32(ld.Addstring(ld.Linklookup(ctxt, ".dynstr", 0), name)))
-
- /* value */
- if s.Type == obj.SDYNIMPORT {
- ld.Adduint32(ctxt, d, 0)
- } else {
- ld.Addaddr(ctxt, d, s)
- }
-
- /* size */
- ld.Adduint32(ctxt, d, 0)
-
- /* type */
- t := ld.STB_GLOBAL << 4
-
- if (s.Cgoexport&ld.CgoExportDynamic != 0) && s.Type&obj.SMASK == obj.STEXT {
- t |= ld.STT_FUNC
- } else {
- t |= ld.STT_OBJECT
- }
- ld.Adduint8(ctxt, d, uint8(t))
- ld.Adduint8(ctxt, d, 0)
-
- /* shndx */
- if s.Type == obj.SDYNIMPORT {
- ld.Adduint16(ctxt, d, ld.SHN_UNDEF)
- } else {
- ld.Adduint16(ctxt, d, 1)
- }
- } else {
- ld.Diag("adddynsym: unsupported binary format")
- }
-}
-
-func adddynlib(lib string) {
- if needlib(lib) == 0 {
- return
- }
-
- if ld.Iself {
- s := ld.Linklookup(ld.Ctxt, ".dynstr", 0)
- if s.Size == 0 {
- ld.Addstring(s, "")
- }
- ld.Elfwritedynent(ld.Linklookup(ld.Ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(s, lib)))
- } else if ld.HEADTYPE == obj.Hdarwin {
- ld.Machoadddynlib(lib)
- } else {
- ld.Diag("adddynlib: unsupported binary format")
- }
-}
-
func asmb() {
if ld.Debug['v'] != 0 {
fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
@@ -617,14 +533,12 @@ func asmb() {
fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime())
}
- if ld.Debug['w'] == 0 { // TODO(minux): enable DWARF Support
- dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)))
- ld.Cseek(int64(dwarfoff))
+ dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)))
+ ld.Cseek(int64(dwarfoff))
- ld.Segdwarf.Fileoff = uint64(ld.Cpos())
- ld.Dwarfemitdebugsections()
- ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff
- }
+ ld.Segdwarf.Fileoff = uint64(ld.Cpos())
+ ld.Dwarfemitdebugsections()
+ ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff
machlink = uint32(ld.Domacholink())
}
@@ -651,7 +565,7 @@ func asmb() {
symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
case obj.Hdarwin:
- symo = uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Filelen), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)) + int64(machlink))
+ symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
}
ld.Cseek(int64(symo))
diff --git a/src/cmd/5l/l.go b/src/cmd/link/internal/arm/l.go
similarity index 98%
rename from src/cmd/5l/l.go
rename to src/cmd/link/internal/arm/l.go
index a52154594d..4973772163 100644
--- a/src/cmd/5l/l.go
+++ b/src/cmd/link/internal/arm/l.go
@@ -28,7 +28,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package arm
// Writing object files.
@@ -72,7 +72,8 @@ const (
MINLC = 4
)
-/* Used by ../ld/dwarf.c */
+/* Used by ../internal/ld/dwarf.go */
const (
DWARFREGSP = 13
+ DWARFREGLR = 14
)
diff --git a/src/cmd/5l/obj.go b/src/cmd/link/internal/arm/obj.go
similarity index 97%
rename from src/cmd/5l/obj.go
rename to src/cmd/link/internal/arm/obj.go
index fa74908005..14fe7a64eb 100644
--- a/src/cmd/5l/obj.go
+++ b/src/cmd/link/internal/arm/obj.go
@@ -28,18 +28,18 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package arm
import (
- "cmd/internal/ld"
"cmd/internal/obj"
+ "cmd/link/internal/ld"
"fmt"
"log"
)
// Reading object files.
-func main() {
+func Main() {
linkarchinit()
ld.Ldmain()
}
@@ -56,10 +56,9 @@ func linkarchinit() {
ld.Thearch.Maxalign = MaxAlign
ld.Thearch.Minlc = MINLC
ld.Thearch.Dwarfregsp = DWARFREGSP
+ ld.Thearch.Dwarfreglr = DWARFREGLR
- ld.Thearch.Adddynlib = adddynlib
ld.Thearch.Adddynrel = adddynrel
- ld.Thearch.Adddynsym = adddynsym
ld.Thearch.Archinit = archinit
ld.Thearch.Archreloc = archreloc
ld.Thearch.Archrelocvariant = archrelocvariant
diff --git a/src/cmd/7l/asm.go b/src/cmd/link/internal/arm64/asm.go
similarity index 88%
rename from src/cmd/7l/asm.go
rename to src/cmd/link/internal/arm64/asm.go
index a17899dcf0..3aebd8a223 100644
--- a/src/cmd/7l/asm.go
+++ b/src/cmd/link/internal/arm64/asm.go
@@ -28,11 +28,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package arm64
import (
- "cmd/internal/ld"
"cmd/internal/obj"
+ "cmd/link/internal/ld"
"encoding/binary"
"fmt"
"log"
@@ -40,24 +40,6 @@ import (
func gentext() {}
-func needlib(name string) int {
- if name[0] == '\x00' {
- return 0
- }
-
- /* reuse hash code in symbol table */
- p := fmt.Sprintf(".dynlib.%s", name)
-
- s := ld.Linklookup(ld.Ctxt, p, 0)
-
- if s.Type == 0 {
- s.Type = 100 // avoid SDATA, etc.
- return 1
- }
-
- return 0
-}
-
func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) {
log.Fatalf("adddynrela not implemented")
}
@@ -125,9 +107,9 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
v = uint32(rs.Dynid)
v |= 1 << 27 // external relocation
} else {
- v = uint32((rs.Sect.(*ld.Section)).Extnum)
+ v = uint32(rs.Sect.Extnum)
if v == 0 {
- ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, (rs.Sect.(*ld.Section)).Name, rs.Type)
+ ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, rs.Sect.Name, rs.Type)
return -1
}
}
@@ -293,28 +275,6 @@ func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 {
return -1
}
-func adddynsym(ctxt *ld.Link, s *ld.LSym) {
- // TODO(minux): implement when needed.
-}
-
-func adddynlib(lib string) {
- if needlib(lib) == 0 {
- return
- }
-
- if ld.Iself {
- s := ld.Linklookup(ld.Ctxt, ".dynstr", 0)
- if s.Size == 0 {
- ld.Addstring(s, "")
- }
- ld.Elfwritedynent(ld.Linklookup(ld.Ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(s, lib)))
- } else if ld.HEADTYPE == obj.Hdarwin {
- ld.Machoadddynlib(lib)
- } else {
- ld.Diag("adddynlib: unsupported binary format")
- }
-}
-
func asmb() {
if ld.Debug['v'] != 0 {
fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
@@ -357,14 +317,12 @@ func asmb() {
fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime())
}
- if ld.Debug['w'] == 0 { // TODO(minux): enable DWARF Support
- dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)))
- ld.Cseek(int64(dwarfoff))
+ dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)))
+ ld.Cseek(int64(dwarfoff))
- ld.Segdwarf.Fileoff = uint64(ld.Cpos())
- ld.Dwarfemitdebugsections()
- ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff
- }
+ ld.Segdwarf.Fileoff = uint64(ld.Cpos())
+ ld.Dwarfemitdebugsections()
+ ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff
machlink = uint32(ld.Domacholink())
}
@@ -391,7 +349,7 @@ func asmb() {
symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
case obj.Hdarwin:
- symo = uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Filelen), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)) + int64(machlink))
+ symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
}
ld.Cseek(int64(symo))
diff --git a/src/cmd/7l/l.go b/src/cmd/link/internal/arm64/l.go
similarity index 98%
rename from src/cmd/7l/l.go
rename to src/cmd/link/internal/arm64/l.go
index 6f90acb107..8d0d57e72a 100644
--- a/src/cmd/7l/l.go
+++ b/src/cmd/link/internal/arm64/l.go
@@ -28,7 +28,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package arm64
// Writing object files.
@@ -71,7 +71,8 @@ const (
MINLC = 4
)
-/* Used by ../ld/dwarf.c */
+/* Used by ../internal/ld/dwarf.go */
const (
DWARFREGSP = 31
+ DWARFREGLR = 30
)
diff --git a/src/cmd/7l/obj.go b/src/cmd/link/internal/arm64/obj.go
similarity index 97%
rename from src/cmd/7l/obj.go
rename to src/cmd/link/internal/arm64/obj.go
index f8ac7d33ea..56f5815903 100644
--- a/src/cmd/7l/obj.go
+++ b/src/cmd/link/internal/arm64/obj.go
@@ -28,18 +28,18 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package arm64
import (
- "cmd/internal/ld"
"cmd/internal/obj"
+ "cmd/link/internal/ld"
"fmt"
"log"
)
// Reading object files.
-func main() {
+func Main() {
linkarchinit()
ld.Ldmain()
}
@@ -56,10 +56,9 @@ func linkarchinit() {
ld.Thearch.Maxalign = MaxAlign
ld.Thearch.Minlc = MINLC
ld.Thearch.Dwarfregsp = DWARFREGSP
+ ld.Thearch.Dwarfreglr = DWARFREGLR
- ld.Thearch.Adddynlib = adddynlib
ld.Thearch.Adddynrel = adddynrel
- ld.Thearch.Adddynsym = adddynsym
ld.Thearch.Archinit = archinit
ld.Thearch.Archreloc = archreloc
ld.Thearch.Archrelocvariant = archrelocvariant
diff --git a/src/cmd/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go
similarity index 100%
rename from src/cmd/internal/ld/ar.go
rename to src/cmd/link/internal/ld/ar.go
diff --git a/src/cmd/internal/ld/arch.go b/src/cmd/link/internal/ld/arch.go
similarity index 100%
rename from src/cmd/internal/ld/arch.go
rename to src/cmd/link/internal/ld/arch.go
diff --git a/src/cmd/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
similarity index 88%
rename from src/cmd/internal/ld/data.go
rename to src/cmd/link/internal/ld/data.go
index 3194bd568e..fd1cdd64bb 100644
--- a/src/cmd/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -32,9 +32,11 @@
package ld
import (
+ "cmd/internal/gcprog"
"cmd/internal/obj"
"fmt"
"log"
+ "os"
"strings"
)
@@ -520,7 +522,7 @@ func relocsym(s *LSym) {
} else if HEADTYPE == obj.Hdarwin {
if r.Type == obj.R_CALL {
if rs.Type != obj.SHOSTOBJ {
- o += int64(uint64(Symaddr(rs)) - (rs.Sect.(*Section)).Vaddr)
+ o += int64(uint64(Symaddr(rs)) - rs.Sect.Vaddr)
}
o -= int64(r.Off) // relative to section offset, not symbol
} else {
@@ -532,7 +534,7 @@ func relocsym(s *LSym) {
o += int64(r.Siz)
// GNU ld always add VirtualAddress of the .text section to the
// relocated address, compensate that.
- o -= int64(s.Sect.(*Section).Vaddr - PEBASE)
+ o -= int64(s.Sect.Vaddr - PEBASE)
} else {
Diag("unhandled pcrel relocation for %s", headstring)
}
@@ -963,6 +965,22 @@ func Addstring(s *LSym, str string) int64 {
return int64(r)
}
+// addgostring adds str, as a Go string value, to s. symname is the name of the
+// symbol used to define the string data and must be unique per linked object.
+func addgostring(s *LSym, symname, str string) {
+ sym := Linklookup(Ctxt, symname, 0)
+ if sym.Type != obj.Sxxx {
+ Diag("duplicate symname in addgostring: %s", symname)
+ }
+ sym.Reachable = true
+ sym.Local = true
+ sym.Type = obj.SRODATA
+ sym.Size = int64(len(str))
+ sym.P = []byte(str)
+ Addaddr(Ctxt, s, sym)
+ adduint(Ctxt, s, uint64(len(str)))
+}
+
func addinitarrdata(s *LSym) {
p := s.Name + ".ptr"
sp := Linklookup(Ctxt, p, 0)
@@ -1028,165 +1046,65 @@ func maxalign(s *LSym, type_ int) int32 {
return max
}
-// Helper object for building GC type programs.
-type ProgGen struct {
- s *LSym
- datasize int32
- data [256 / obj.PointersPerByte]uint8
- pos int64
+const debugGCProg = false
+
+type GCProg struct {
+ sym *LSym
+ w gcprog.Writer
}
-func proggeninit(g *ProgGen, s *LSym) {
- g.s = s
- g.datasize = 0
- g.pos = 0
- g.data = [256 / obj.PointersPerByte]uint8{}
-}
-
-func proggenemit(g *ProgGen, v uint8) {
- Adduint8(Ctxt, g.s, v)
-}
-
-// Writes insData block from g->data.
-func proggendataflush(g *ProgGen) {
- if g.datasize == 0 {
- return
- }
- proggenemit(g, obj.InsData)
- proggenemit(g, uint8(g.datasize))
- s := (g.datasize + obj.PointersPerByte - 1) / obj.PointersPerByte
- for i := int32(0); i < s; i++ {
- proggenemit(g, g.data[i])
- }
- g.datasize = 0
- g.data = [256 / obj.PointersPerByte]uint8{}
-}
-
-func proggendata(g *ProgGen, d uint8) {
- g.data[g.datasize/obj.PointersPerByte] |= d << uint((g.datasize%obj.PointersPerByte)*obj.BitsPerPointer)
- g.datasize++
- if g.datasize == 255 {
- proggendataflush(g)
+func (p *GCProg) Init(name string) {
+ p.sym = Linklookup(Ctxt, name, 0)
+ p.w.Init(p.writeByte)
+ if debugGCProg {
+ fmt.Fprintf(os.Stderr, "ld: start GCProg %s\n", name)
+ p.w.Debug(os.Stderr)
}
}
-// Skip v bytes due to alignment, etc.
-func proggenskip(g *ProgGen, off int64, v int64) {
- for i := off; i < off+v; i++ {
- if (i % int64(Thearch.Ptrsize)) == 0 {
- proggendata(g, obj.BitsScalar)
- }
+func (p *GCProg) writeByte(x byte) {
+ Adduint8(Ctxt, p.sym, x)
+}
+
+func (p *GCProg) End(size int64) {
+ p.w.ZeroUntil(size / int64(Thearch.Ptrsize))
+ p.w.End()
+ if debugGCProg {
+ fmt.Fprintf(os.Stderr, "ld: end GCProg\n")
}
}
-// Emit insArray instruction.
-func proggenarray(g *ProgGen, length int64) {
- var i int32
-
- proggendataflush(g)
- proggenemit(g, obj.InsArray)
- for i = 0; i < int32(Thearch.Ptrsize); i, length = i+1, length>>8 {
- proggenemit(g, uint8(length))
- }
-}
-
-func proggenarrayend(g *ProgGen) {
- proggendataflush(g)
- proggenemit(g, obj.InsArrayEnd)
-}
-
-func proggenfini(g *ProgGen, size int64) {
- proggenskip(g, g.pos, size-g.pos)
- proggendataflush(g)
- proggenemit(g, obj.InsEnd)
-}
-
-// This function generates GC pointer info for global variables.
-func proggenaddsym(g *ProgGen, s *LSym) {
- if s.Size == 0 {
- return
- }
-
- // Skip alignment hole from the previous symbol.
- proggenskip(g, g.pos, s.Value-g.pos)
-
- g.pos += s.Value - g.pos
-
- // The test for names beginning with . here is meant
- // to keep .dynamic and .dynsym from turning up as
- // conservative symbols. They should be marked SELFSECT
- // and not SDATA, but sometimes that doesn't happen.
- // Leave debugging the SDATA issue for the Go rewrite.
-
- if s.Gotype == nil && s.Size >= int64(Thearch.Ptrsize) && s.Name[0] != '.' {
- // conservative scan
+func (p *GCProg) AddSym(s *LSym) {
+ typ := s.Gotype
+ // Things without pointers should be in SNOPTRDATA or SNOPTRBSS;
+ // everything we see should have pointers and should therefore have a type.
+ if typ == nil {
Diag("missing Go type information for global symbol: %s size %d", s.Name, int(s.Size))
-
- if (s.Size%int64(Thearch.Ptrsize) != 0) || (g.pos%int64(Thearch.Ptrsize) != 0) {
- Diag("proggenaddsym: unaligned conservative symbol %s: size=%d pos=%d", s.Name, s.Size, g.pos)
- }
- size := (s.Size + int64(Thearch.Ptrsize) - 1) / int64(Thearch.Ptrsize) * int64(Thearch.Ptrsize)
- if size < int64(32*Thearch.Ptrsize) {
- // Emit small symbols as data.
- for i := int64(0); i < size/int64(Thearch.Ptrsize); i++ {
- proggendata(g, obj.BitsPointer)
- }
- } else {
- // Emit large symbols as array.
- proggenarray(g, size/int64(Thearch.Ptrsize))
-
- proggendata(g, obj.BitsPointer)
- proggenarrayend(g)
- }
-
- g.pos = s.Value + size
- } else if s.Gotype == nil || decodetype_noptr(s.Gotype) != 0 || s.Size < int64(Thearch.Ptrsize) || s.Name[0] == '.' {
- // no scan
- if s.Size < int64(32*Thearch.Ptrsize) {
- // Emit small symbols as data.
- // This case also handles unaligned and tiny symbols, so tread carefully.
- for i := s.Value; i < s.Value+s.Size; i++ {
- if (i % int64(Thearch.Ptrsize)) == 0 {
- proggendata(g, obj.BitsScalar)
- }
- }
- } else {
- // Emit large symbols as array.
- if (s.Size%int64(Thearch.Ptrsize) != 0) || (g.pos%int64(Thearch.Ptrsize) != 0) {
- Diag("proggenaddsym: unaligned noscan symbol %s: size=%d pos=%d", s.Name, s.Size, g.pos)
- }
- proggenarray(g, s.Size/int64(Thearch.Ptrsize))
- proggendata(g, obj.BitsScalar)
- proggenarrayend(g)
- }
-
- g.pos = s.Value + s.Size
- } else if decodetype_usegcprog(s.Gotype) != 0 {
- // gc program, copy directly
- proggendataflush(g)
-
- gcprog := decodetype_gcprog(s.Gotype)
- size := decodetype_size(s.Gotype)
- if (size%int64(Thearch.Ptrsize) != 0) || (g.pos%int64(Thearch.Ptrsize) != 0) {
- Diag("proggenaddsym: unaligned gcprog symbol %s: size=%d pos=%d", s.Name, s.Size, g.pos)
- }
- for i := int64(0); i < int64(len(gcprog.P)-1); i++ {
- proggenemit(g, uint8(gcprog.P[i]))
- }
- g.pos = s.Value + size
- } else {
- // gc mask, it's small so emit as data
- mask := decodetype_gcmask(s.Gotype)
-
- size := decodetype_size(s.Gotype)
- if (size%int64(Thearch.Ptrsize) != 0) || (g.pos%int64(Thearch.Ptrsize) != 0) {
- Diag("proggenaddsym: unaligned gcmask symbol %s: size=%d pos=%d", s.Name, s.Size, g.pos)
- }
- for i := int64(0); i < size; i += int64(Thearch.Ptrsize) {
- proggendata(g, uint8((mask[i/int64(Thearch.Ptrsize)/2]>>uint64((i/int64(Thearch.Ptrsize)%2)*4+2))&obj.BitsMask))
- }
- g.pos = s.Value + size
+ return
}
+
+ ptrsize := int64(Thearch.Ptrsize)
+ nptr := decodetype_ptrdata(typ) / ptrsize
+
+ if debugGCProg {
+ fmt.Fprintf(os.Stderr, "gcprog sym: %s at %d (ptr=%d+%d)\n", s.Name, s.Value, s.Value/ptrsize, nptr)
+ }
+
+ if decodetype_usegcprog(typ) == 0 {
+ // Copy pointers from mask into program.
+ mask := decodetype_gcmask(typ)
+ for i := int64(0); i < nptr; i++ {
+ if (mask[i/8]>>uint(i%8))&1 != 0 {
+ p.w.Ptr(s.Value/ptrsize + i)
+ }
+ }
+ return
+ }
+
+ // Copy program.
+ prog := decodetype_gcprog(typ)
+ p.w.ZeroUntil(s.Value / ptrsize)
+ p.w.Append(prog[4:], nptr)
}
func growdatsize(datsizep *int64, s *LSym) {
@@ -1394,15 +1312,13 @@ func dodata() {
/* data */
sect = addsection(&Segdata, ".data", 06)
-
sect.Align = maxalign(s, obj.SBSS-1)
datsize = Rnd(datsize, int64(sect.Align))
sect.Vaddr = uint64(datsize)
Linklookup(Ctxt, "runtime.data", 0).Sect = sect
Linklookup(Ctxt, "runtime.edata", 0).Sect = sect
- gcdata := Linklookup(Ctxt, "runtime.gcdata", 0)
- var gen ProgGen
- proggeninit(&gen, gcdata)
+ var gc GCProg
+ gc.Init("runtime.gcdata")
for ; s != nil && s.Type < obj.SBSS; s = s.Next {
if s.Type == obj.SINITARR {
Ctxt.Cursym = s
@@ -1413,33 +1329,30 @@ func dodata() {
s.Type = obj.SDATA
datsize = aligndatsize(datsize, s)
s.Value = int64(uint64(datsize) - sect.Vaddr)
- proggenaddsym(&gen, s) // gc
+ gc.AddSym(s)
growdatsize(&datsize, s)
}
-
sect.Length = uint64(datsize) - sect.Vaddr
- proggenfini(&gen, int64(sect.Length)) // gc
+ gc.End(int64(sect.Length))
/* bss */
sect = addsection(&Segdata, ".bss", 06)
-
sect.Align = maxalign(s, obj.SNOPTRBSS-1)
datsize = Rnd(datsize, int64(sect.Align))
sect.Vaddr = uint64(datsize)
Linklookup(Ctxt, "runtime.bss", 0).Sect = sect
Linklookup(Ctxt, "runtime.ebss", 0).Sect = sect
- gcbss := Linklookup(Ctxt, "runtime.gcbss", 0)
- proggeninit(&gen, gcbss)
+ gc = GCProg{}
+ gc.Init("runtime.gcbss")
for ; s != nil && s.Type < obj.SNOPTRBSS; s = s.Next {
s.Sect = sect
datsize = aligndatsize(datsize, s)
s.Value = int64(uint64(datsize) - sect.Vaddr)
- proggenaddsym(&gen, s) // gc
+ gc.AddSym(s)
growdatsize(&datsize, s)
}
-
sect.Length = uint64(datsize) - sect.Vaddr
- proggenfini(&gen, int64(sect.Length)) // gc
+ gc.End(int64(sect.Length))
/* pointer-free bss */
sect = addsection(&Segdata, ".noptrbss", 06)
@@ -1768,13 +1681,20 @@ func address() {
for sym := datap; sym != nil; sym = sym.Next {
Ctxt.Cursym = sym
if sym.Sect != nil {
- sym.Value += int64((sym.Sect.(*Section)).Vaddr)
+ sym.Value += int64(sym.Sect.Vaddr)
}
for sub = sym.Sub; sub != nil; sub = sub.Sub {
sub.Value += sym.Value
}
}
+ if Buildmode == BuildmodeShared {
+ s := Linklookup(Ctxt, "go.link.abihashbytes", 0)
+ sectSym := Linklookup(Ctxt, ".note.go.abihash", 0)
+ s.Sect = sectSym.Sect
+ s.Value = int64(sectSym.Sect.Vaddr + 16)
+ }
+
xdefine("runtime.text", obj.STEXT, int64(text.Vaddr))
xdefine("runtime.etext", obj.STEXT, int64(text.Vaddr+text.Length))
xdefine("runtime.rodata", obj.SRODATA, int64(rodata.Vaddr))
diff --git a/src/cmd/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go
similarity index 76%
rename from src/cmd/internal/ld/decodesym.go
rename to src/cmd/link/internal/ld/decodesym.go
index 754c89f12b..c1cf4d7181 100644
--- a/src/cmd/internal/ld/decodesym.go
+++ b/src/cmd/link/internal/ld/decodesym.go
@@ -4,7 +4,10 @@
package ld
-import "cmd/internal/obj"
+import (
+ "cmd/internal/obj"
+ "debug/elf"
+)
// Decoding the type.* symbols. This has to be in sync with
// ../../runtime/type.go, or more specifically, with what
@@ -44,7 +47,7 @@ func decode_inuxi(p []byte, sz int) uint64 {
// commonsize returns the size of the common prefix for all type
// structures (runtime._type).
func commonsize() int {
- return 9*Thearch.Ptrsize + 8
+ return 8*Thearch.Ptrsize + 8
}
// Type.commonType.kind
@@ -67,14 +70,43 @@ func decodetype_size(s *LSym) int64 {
return int64(decode_inuxi(s.P, Thearch.Ptrsize)) // 0x8 / 0x10
}
-// Type.commonType.gc
-func decodetype_gcprog(s *LSym) *LSym {
- if s.Type == obj.SDYNIMPORT {
- // The gcprog for "type.$name" is calle "type..gcprog.$name".
- x := "type..gcprog." + s.Name[5:]
- return Linklookup(Ctxt, x, 0)
+// Type.commonType.ptrdata
+func decodetype_ptrdata(s *LSym) int64 {
+ return int64(decode_inuxi(s.P[Thearch.Ptrsize:], Thearch.Ptrsize)) // 0x8 / 0x10
+}
+
+// Find the elf.Section of a given shared library that contains a given address.
+func findShlibSection(path string, addr uint64) *elf.Section {
+ for _, shlib := range Ctxt.Shlibs {
+ if shlib.Path == path {
+ for _, sect := range shlib.File.Sections {
+ if sect.Addr <= addr && addr <= sect.Addr+sect.Size {
+ return sect
+ }
+ }
+ }
}
- return decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+2*int32(Thearch.Ptrsize))
+ return nil
+}
+
+// Type.commonType.gc
+func decodetype_gcprog(s *LSym) []byte {
+ if s.Type == obj.SDYNIMPORT {
+ addr := decodetype_gcprog_shlib(s)
+ sect := findShlibSection(s.File, addr)
+ if sect != nil {
+ // A gcprog is a 4-byte uint32 indicating length, followed by
+ // the actual program.
+ progsize := make([]byte, 4)
+ sect.ReadAt(progsize, int64(addr-sect.Addr))
+ progbytes := make([]byte, Ctxt.Arch.ByteOrder.Uint32(progsize))
+ sect.ReadAt(progbytes, int64(addr-sect.Addr+4))
+ return append(progsize, progbytes...)
+ }
+ Exitf("cannot find gcprog for %s", s.Name)
+ return nil
+ }
+ return decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize)).P
}
func decodetype_gcprog_shlib(s *LSym) uint64 {
@@ -83,9 +115,16 @@ func decodetype_gcprog_shlib(s *LSym) uint64 {
func decodetype_gcmask(s *LSym) []byte {
if s.Type == obj.SDYNIMPORT {
- // ldshlibsyms makes special efforts to read the value
- // of gcmask for types defined in that shared library.
- return s.gcmask
+ addr := decodetype_gcprog_shlib(s)
+ ptrdata := decodetype_ptrdata(s)
+ sect := findShlibSection(s.File, addr)
+ if sect != nil {
+ r := make([]byte, ptrdata/int64(Thearch.Ptrsize))
+ sect.ReadAt(r, int64(addr-sect.Addr))
+ return r
+ }
+ Exitf("cannot find gcmask for %s", s.Name)
+ return nil
}
mask := decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize))
return mask.P
diff --git a/src/cmd/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
similarity index 93%
rename from src/cmd/internal/ld/dwarf.go
rename to src/cmd/link/internal/ld/dwarf.go
index 6d90404b13..b8fb2e6b55 100644
--- a/src/cmd/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -17,6 +17,7 @@ package ld
import (
"cmd/internal/obj"
"fmt"
+ "os"
"strings"
)
@@ -240,6 +241,7 @@ var abbrevs = [DW_NABRV]DWAbbrev{
{DW_AT_low_pc, DW_FORM_addr},
{DW_AT_high_pc, DW_FORM_addr},
{DW_AT_stmt_list, DW_FORM_data4},
+ {DW_AT_comp_dir, DW_FORM_string},
},
},
@@ -694,6 +696,9 @@ func adddwarfrel(sec *LSym, sym *LSym, offsetbase int64, siz int, addend int64)
if Iself && Thearch.Thechar == '6' {
addend = 0
}
+ if HEADTYPE == obj.Hdarwin {
+ addend += sym.Value
+ }
switch siz {
case 4:
Thearch.Lput(uint32(addend))
@@ -1547,6 +1552,13 @@ func flushunit(dwinfo *DWDie, pc int64, pcsym *LSym, unitstart int64, header_len
}
}
+func getCompilationDir() string {
+ if dir, err := os.Getwd(); err == nil {
+ return dir
+ }
+ return "/"
+}
+
func writelines() {
if linesec == nil {
linesec = Linklookup(Ctxt, ".dwarfline", 0)
@@ -1571,6 +1583,9 @@ func writelines() {
newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT, int64(lang), 0)
newattr(dwinfo, DW_AT_stmt_list, DW_CLS_PTR, unitstart-lineo, 0)
newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, s.Value, s)
+ // OS X linker requires compilation dir or absolute path in comp unit name to output debug info.
+ compDir := getCompilationDir()
+ newattr(dwinfo, DW_AT_comp_dir, DW_CLS_STRING, int64(len(compDir)), compDir)
// Write .debug_line Line Number Program Header (sec 6.2.4)
// Fields marked with (*) must be changed for 64-bit dwarf
@@ -1692,11 +1707,17 @@ func writelines() {
switch a.Name {
case obj.A_AUTO:
dt = DW_ABRV_AUTO
- offs = int64(a.Aoffset) - int64(Thearch.Ptrsize)
+ offs = int64(a.Aoffset)
+ if !haslinkregister() {
+ offs -= int64(Thearch.Ptrsize)
+ }
case obj.A_PARAM:
dt = DW_ABRV_PARAM
offs = int64(a.Aoffset)
+ if haslinkregister() {
+ offs += int64(Thearch.Ptrsize)
+ }
default:
continue
@@ -1749,7 +1770,6 @@ func writelines() {
const (
CIERESERVE = 16
DATAALIGNMENTFACTOR = -4
- FAKERETURNCOLUMN = 16 // TODO gdb6 doesn't like > 15?
)
func putpccfadelta(deltapc int64, cfa int64) {
@@ -1778,21 +1798,30 @@ func writeframes() {
frameo = Cpos()
// Emit the CIE, Section 6.4.1
- Thearch.Lput(CIERESERVE) // initial length, must be multiple of thearch.ptrsize
- Thearch.Lput(0xffffffff) // cid.
- Cput(3) // dwarf version (appendix F)
- Cput(0) // augmentation ""
- uleb128put(1) // code_alignment_factor
- sleb128put(DATAALIGNMENTFACTOR) // guess
- uleb128put(FAKERETURNCOLUMN) // return_address_register
+ Thearch.Lput(CIERESERVE) // initial length, must be multiple of thearch.ptrsize
+ Thearch.Lput(0xffffffff) // cid.
+ Cput(3) // dwarf version (appendix F)
+ Cput(0) // augmentation ""
+ uleb128put(1) // code_alignment_factor
+ sleb128put(DATAALIGNMENTFACTOR) // guess
+ uleb128put(int64(Thearch.Dwarfreglr)) // return_address_register
Cput(DW_CFA_def_cfa)
uleb128put(int64(Thearch.Dwarfregsp)) // register SP (**ABI-dependent, defined in l.h)
- uleb128put(int64(Thearch.Ptrsize)) // offset
+ if haslinkregister() {
+ uleb128put(int64(0)) // offset
+ } else {
+ uleb128put(int64(Thearch.Ptrsize)) // offset
+ }
- Cput(DW_CFA_offset + FAKERETURNCOLUMN) // return address
- uleb128put(int64(-Thearch.Ptrsize) / DATAALIGNMENTFACTOR) // at cfa - x*4
+ Cput(DW_CFA_offset_extended)
+ uleb128put(int64(Thearch.Dwarfreglr)) // return address
+ if haslinkregister() {
+ uleb128put(int64(0) / DATAALIGNMENTFACTOR) // at cfa - 0
+ } else {
+ uleb128put(int64(-Thearch.Ptrsize) / DATAALIGNMENTFACTOR) // at cfa - x*4
+ }
// 4 is to exclude the length field.
pad := CIERESERVE + frameo + 4 - Cpos()
@@ -1834,7 +1863,11 @@ func writeframes() {
}
}
- putpccfadelta(int64(nextpc)-int64(pcsp.pc), int64(Thearch.Ptrsize)+int64(pcsp.value))
+ if haslinkregister() {
+ putpccfadelta(int64(nextpc)-int64(pcsp.pc), int64(pcsp.value))
+ } else {
+ putpccfadelta(int64(nextpc)-int64(pcsp.pc), int64(Thearch.Ptrsize)+int64(pcsp.value))
+ }
}
fdesize = Cpos() - fdeo - 4 // exclude the length field.
@@ -2065,6 +2098,14 @@ func writedwarfreloc(s *LSym) int64 {
return start
}
+func addmachodwarfsect(prev *Section, name string) *Section {
+ sect := addsection(&Segdwarf, name, 04)
+ sect.Extnum = prev.Extnum + 1
+ sym := Linklookup(Ctxt, name, 0)
+ sym.Sect = sect
+ return sect
+}
+
/*
* This is the main entry point for generating dwarf. After emitting
* the mandatory debug_abbrev section, it calls writelines() to set up
@@ -2079,8 +2120,33 @@ func Dwarfemitdebugsections() {
return
}
- if Linkmode == LinkExternal && !Iself {
- return
+ if Linkmode == LinkExternal {
+ if !Iself && HEADTYPE != obj.Hdarwin {
+ return
+ }
+ if HEADTYPE == obj.Hdarwin {
+ sect := Segdata.Sect
+ // find the last section.
+ for sect.Next != nil {
+ sect = sect.Next
+ }
+ sect = addmachodwarfsect(sect, ".debug_abbrev")
+ sect = addmachodwarfsect(sect, ".debug_line")
+ sect = addmachodwarfsect(sect, ".debug_frame")
+ sect = addmachodwarfsect(sect, ".debug_info")
+
+ infosym = Linklookup(Ctxt, ".debug_info", 0)
+ infosym.Hide = 1
+
+ abbrevsym = Linklookup(Ctxt, ".debug_abbrev", 0)
+ abbrevsym.Hide = 1
+
+ linesym = Linklookup(Ctxt, ".debug_line", 0)
+ linesym.Hide = 1
+
+ framesym = Linklookup(Ctxt, ".debug_frame", 0)
+ framesym.Hide = 1
+ }
}
// For diagnostic messages.
@@ -2173,6 +2239,15 @@ func Dwarfemitdebugsections() {
for Cpos()&7 != 0 {
Cput(0)
}
+ if HEADTYPE != obj.Hdarwin {
+ dwarfemitreloc()
+ }
+}
+
+func dwarfemitreloc() {
+ if Debug['w'] != 0 { // disable dwarf
+ return
+ }
inforeloco = writedwarfreloc(infosec)
inforelocsize = Cpos() - inforeloco
align(inforelocsize)
@@ -2402,14 +2477,15 @@ func dwarfaddelfheaders() {
/*
* Macho
*/
-func dwarfaddmachoheaders() {
+func dwarfaddmachoheaders(ms *MachoSeg) {
if Debug['w'] != 0 { // disable dwarf
return
}
// Zero vsize segments won't be loaded in memory, even so they
// have to be page aligned in the file.
- fakestart := abbrevo &^ 0xfff
+ fakestart := Rnd(int64(Segdwarf.Fileoff), 0x1000)
+ addr := Segdata.Vaddr + Segdata.Length
nsect := 4
if pubnamessize > 0 {
@@ -2425,57 +2501,94 @@ func dwarfaddmachoheaders() {
nsect++
}
- ms := newMachoSeg("__DWARF", nsect)
- ms.fileoffset = uint64(fakestart)
- ms.filesize = uint64(abbrevo) - uint64(fakestart)
- ms.vaddr = ms.fileoffset + Segdata.Vaddr - Segdata.Fileoff
+ if Linkmode != LinkExternal {
+ ms = newMachoSeg("__DWARF", nsect)
+ ms.fileoffset = uint64(fakestart)
+ ms.filesize = Segdwarf.Filelen
+ ms.vaddr = addr
+ }
msect := newMachoSect(ms, "__debug_abbrev", "__DWARF")
msect.off = uint32(abbrevo)
msect.size = uint64(abbrevsize)
- msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
- ms.filesize += msect.size
+ msect.addr = addr
+ addr += msect.size
+ msect.flag = 0x02000000
+ if abbrevsym != nil {
+ abbrevsym.Value = int64(msect.addr)
+ }
msect = newMachoSect(ms, "__debug_line", "__DWARF")
msect.off = uint32(lineo)
msect.size = uint64(linesize)
- msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
- ms.filesize += msect.size
+ msect.addr = addr
+ addr += msect.size
+ msect.flag = 0x02000000
+ if linesym != nil {
+ linesym.Value = int64(msect.addr)
+ }
+ if linerelocsize > 0 {
+ msect.nreloc = uint32(len(linesec.R))
+ msect.reloc = uint32(linereloco)
+ }
msect = newMachoSect(ms, "__debug_frame", "__DWARF")
msect.off = uint32(frameo)
msect.size = uint64(framesize)
- msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
- ms.filesize += msect.size
+ msect.addr = addr
+ addr += msect.size
+ msect.flag = 0x02000000
+ if framesym != nil {
+ framesym.Value = int64(msect.addr)
+ }
+ if framerelocsize > 0 {
+ msect.nreloc = uint32(len(framesec.R))
+ msect.reloc = uint32(framereloco)
+ }
msect = newMachoSect(ms, "__debug_info", "__DWARF")
msect.off = uint32(infoo)
msect.size = uint64(infosize)
- msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
- ms.filesize += msect.size
+ msect.addr = addr
+ addr += msect.size
+ msect.flag = 0x02000000
+ if infosym != nil {
+ infosym.Value = int64(msect.addr)
+ }
+ if inforelocsize > 0 {
+ msect.nreloc = uint32(len(infosec.R))
+ msect.reloc = uint32(inforeloco)
+ }
if pubnamessize > 0 {
msect := newMachoSect(ms, "__debug_pubnames", "__DWARF")
msect.off = uint32(pubnameso)
msect.size = uint64(pubnamessize)
- msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
- ms.filesize += msect.size
+ msect.addr = addr
+ addr += msect.size
+ msect.flag = 0x02000000
}
if pubtypessize > 0 {
msect := newMachoSect(ms, "__debug_pubtypes", "__DWARF")
msect.off = uint32(pubtypeso)
msect.size = uint64(pubtypessize)
- msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
- ms.filesize += msect.size
+ msect.addr = addr
+ addr += msect.size
+ msect.flag = 0x02000000
}
if arangessize > 0 {
msect := newMachoSect(ms, "__debug_aranges", "__DWARF")
msect.off = uint32(arangeso)
msect.size = uint64(arangessize)
- msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
- ms.filesize += msect.size
+ msect.addr = addr
+ addr += msect.size
+ msect.flag = 0x02000000
+ if arangesrelocsize > 0 {
+ msect.nreloc = uint32(len(arangessec.R))
+ msect.reloc = uint32(arangesreloco)
+ }
}
// TODO(lvd) fix gdb/python to load MachO (16 char section name limit)
@@ -2483,8 +2596,9 @@ func dwarfaddmachoheaders() {
msect := newMachoSect(ms, "__debug_gdb_scripts", "__DWARF")
msect.off = uint32(gdbscripto)
msect.size = uint64(gdbscriptsize)
- msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
- ms.filesize += msect.size
+ msect.addr = addr
+ addr += msect.size
+ msect.flag = 0x02000000
}
}
diff --git a/src/cmd/internal/ld/dwarf_defs.go b/src/cmd/link/internal/ld/dwarf_defs.go
similarity index 100%
rename from src/cmd/internal/ld/dwarf_defs.go
rename to src/cmd/link/internal/ld/dwarf_defs.go
diff --git a/src/cmd/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
similarity index 92%
rename from src/cmd/internal/ld/elf.go
rename to src/cmd/link/internal/ld/elf.go
index 5c17b2da6f..68d21f415c 100644
--- a/src/cmd/internal/ld/elf.go
+++ b/src/cmd/link/internal/ld/elf.go
@@ -6,8 +6,12 @@ package ld
import (
"cmd/internal/obj"
+ "crypto/sha1"
"encoding/binary"
"fmt"
+ "path/filepath"
+ "sort"
+ "strings"
)
/*
@@ -1199,32 +1203,15 @@ func elfwritebuildinfo() int {
return int(sh.size)
}
-// Go package list note
+// Go specific notes
const (
ELF_NOTE_GOPKGLIST_TAG = 1
+ ELF_NOTE_GOABIHASH_TAG = 2
+ ELF_NOTE_GODEPS_TAG = 3
)
var ELF_NOTE_GO_NAME = []byte("GO\x00\x00")
-func elfgopkgnote(sh *ElfShdr, startva uint64, resoff uint64) int {
- n := len(ELF_NOTE_GO_NAME) + int(Rnd(int64(len(pkglistfornote)), 4))
- return elfnote(sh, startva, resoff, n, false)
-}
-
-func elfwritegopkgnote() int {
- sh := elfwritenotehdr(".note.go.pkg-list", uint32(len(ELF_NOTE_GO_NAME)), uint32(len(pkglistfornote)), ELF_NOTE_GOPKGLIST_TAG)
- if sh == nil {
- return 0
- }
-
- Cwrite(ELF_NOTE_GO_NAME)
- Cwrite(pkglistfornote)
- var zero = make([]byte, 4)
- Cwrite(zero[:int(Rnd(int64(len(pkglistfornote)), 4)-int64(len(pkglistfornote)))])
-
- return int(sh.size)
-}
-
var elfverneed int
type Elfaux struct {
@@ -1455,6 +1442,24 @@ func elfshalloc(sect *Section) *ElfShdr {
func elfshbits(sect *Section) *ElfShdr {
sh := elfshalloc(sect)
+ // If this section has already been set up as a note, we assume type_ and
+ // flags are already correct, but the other fields still need filling in.
+ if sh.type_ == SHT_NOTE {
+ if Linkmode != LinkExternal {
+ // TODO(mwhudson): the approach here will work OK when
+ // linking internally for notes that we want to be included
+ // in a loadable segment (e.g. the abihash note) but not for
+ // notes that we do not want to be mapped (e.g. the package
+ // list note). The real fix is probably to define new values
+ // for LSym.Type corresponding to mapped and unmapped notes
+ // and handle them in dodata().
+ Diag("sh.type_ == SHT_NOTE in elfshbits when linking internally")
+ }
+ sh.addralign = uint64(sect.Align)
+ sh.size = sect.Length
+ sh.off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr
+ return sh
+ }
if sh.type_ > 0 {
return sh
}
@@ -1490,13 +1495,16 @@ func elfshbits(sect *Section) *ElfShdr {
func elfshreloc(sect *Section) *ElfShdr {
// If main section is SHT_NOBITS, nothing to relocate.
- // Also nothing to relocate in .shstrtab.
+ // Also nothing to relocate in .shstrtab or notes.
if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
return nil
}
if sect.Name == ".shstrtab" || sect.Name == ".tbss" {
return nil
}
+ if sect.Elfsect.type_ == SHT_NOTE {
+ return nil
+ }
var prefix string
var typ int
@@ -1517,7 +1525,7 @@ func elfshreloc(sect *Section) *ElfShdr {
sh.entsize += uint64(Thearch.Regsize)
}
sh.link = uint32(elfshname(".symtab").shnum)
- sh.info = uint32((sect.Elfsect.(*ElfShdr)).shnum)
+ sh.info = uint32(sect.Elfsect.shnum)
sh.off = sect.Reloff
sh.size = sect.Rellen
sh.addralign = uint64(Thearch.Regsize)
@@ -1596,6 +1604,29 @@ func Elfemitreloc() {
}
}
+func addgonote(sectionName string, tag uint32, desc []byte) {
+ s := Linklookup(Ctxt, sectionName, 0)
+ s.Reachable = true
+ s.Type = obj.SELFROSECT
+ // namesz
+ Adduint32(Ctxt, s, uint32(len(ELF_NOTE_GO_NAME)))
+ // descsz
+ Adduint32(Ctxt, s, uint32(len(desc)))
+ // tag
+ Adduint32(Ctxt, s, tag)
+ // name + padding
+ s.P = append(s.P, ELF_NOTE_GO_NAME...)
+ for len(s.P)%4 != 0 {
+ s.P = append(s.P, 0)
+ }
+ // desc + padding
+ s.P = append(s.P, desc...)
+ for len(s.P)%4 != 0 {
+ s.P = append(s.P, 0)
+ }
+ s.Size = int64(len(s.P))
+}
+
func doelf() {
if !Iself {
return
@@ -1632,9 +1663,6 @@ func doelf() {
if len(buildinfo) > 0 {
Addstring(shstrtab, ".note.gnu.build-id")
}
- if Buildmode == BuildmodeShared {
- Addstring(shstrtab, ".note.go.pkg-list")
- }
Addstring(shstrtab, ".elfdata")
Addstring(shstrtab, ".rodata")
Addstring(shstrtab, ".typelink")
@@ -1668,6 +1696,12 @@ func doelf() {
// add a .note.GNU-stack section to mark the stack as non-executable
Addstring(shstrtab, ".note.GNU-stack")
+
+ if Buildmode == BuildmodeShared {
+ Addstring(shstrtab, ".note.go.abihash")
+ Addstring(shstrtab, ".note.go.pkg-list")
+ Addstring(shstrtab, ".note.go.deps")
+ }
}
hasinitarr := Linkshared
@@ -1856,6 +1890,30 @@ func doelf() {
// size of .rel(a).plt section.
Elfwritedynent(s, DT_DEBUG, 0)
}
+
+ if Buildmode == BuildmodeShared {
+ // The go.link.abihashbytes symbol will be pointed at the appropriate
+ // part of the .note.go.abihash section in data.go:func address().
+ s := Linklookup(Ctxt, "go.link.abihashbytes", 0)
+ s.Local = true
+ s.Type = obj.SRODATA
+ s.Special = 1
+ s.Reachable = true
+ s.Size = int64(sha1.Size)
+
+ sort.Sort(byPkg(Ctxt.Library))
+ h := sha1.New()
+ for _, l := range Ctxt.Library {
+ h.Write(l.hash)
+ }
+ addgonote(".note.go.abihash", ELF_NOTE_GOABIHASH_TAG, h.Sum([]byte{}))
+ addgonote(".note.go.pkg-list", ELF_NOTE_GOPKGLIST_TAG, []byte(pkglistfornote))
+ var deplist []string
+ for _, shlib := range Ctxt.Shlibs {
+ deplist = append(deplist, filepath.Base(shlib.Path))
+ }
+ addgonote(".note.go.deps", ELF_NOTE_GODEPS_TAG, []byte(strings.Join(deplist, "\n")))
+ }
}
// Do not write DT_NULL. elfdynhash will finish it.
@@ -1922,15 +1980,13 @@ func Asmbelf(symo int64) {
eh.phentsize = 0
if Buildmode == BuildmodeShared {
- // The package list note we make space for here can get quite
- // large. The external linker will re-layout all the sections
- // anyway, so making this larger just wastes a little space
- // in the intermediate object file, not the final shared
- // library.
- elfreserve *= 3
- resoff = elfreserve
sh := elfshname(".note.go.pkg-list")
- resoff -= int64(elfgopkgnote(sh, uint64(startva), uint64(resoff)))
+ sh.type_ = SHT_NOTE
+ sh = elfshname(".note.go.abihash")
+ sh.type_ = SHT_NOTE
+ sh.flags = SHF_ALLOC
+ sh = elfshname(".note.go.deps")
+ sh.type_ = SHT_NOTE
}
goto elfobj
}
@@ -2340,15 +2396,99 @@ elfobj:
a += int64(elfwritebuildinfo())
}
}
- if Buildmode == BuildmodeShared {
- a += int64(elfwritegopkgnote())
- }
if a > elfreserve {
Diag("ELFRESERVE too small: %d > %d", a, elfreserve)
}
}
+func Elfadddynsym(ctxt *Link, s *LSym) {
+ if elf64 {
+ s.Dynid = int32(Nelfsym)
+ Nelfsym++
+
+ d := Linklookup(ctxt, ".dynsym", 0)
+
+ name := s.Extname
+ Adduint32(ctxt, d, uint32(Addstring(Linklookup(ctxt, ".dynstr", 0), name)))
+
+ /* type */
+ t := STB_GLOBAL << 4
+
+ if s.Cgoexport != 0 && s.Type&obj.SMASK == obj.STEXT {
+ t |= STT_FUNC
+ } else {
+ t |= STT_OBJECT
+ }
+ Adduint8(ctxt, d, uint8(t))
+
+ /* reserved */
+ Adduint8(ctxt, d, 0)
+
+ /* section where symbol is defined */
+ if s.Type == obj.SDYNIMPORT {
+ Adduint16(ctxt, d, SHN_UNDEF)
+ } else {
+ Adduint16(ctxt, d, 1)
+ }
+
+ /* value */
+ if s.Type == obj.SDYNIMPORT {
+ Adduint64(ctxt, d, 0)
+ } else {
+ Addaddr(ctxt, d, s)
+ }
+
+ /* size of object */
+ Adduint64(ctxt, d, uint64(s.Size))
+
+ if Thearch.Thechar == '6' && s.Cgoexport&CgoExportDynamic == 0 && s.Dynimplib != "" && !seenlib[s.Dynimplib] {
+ Elfwritedynent(Linklookup(ctxt, ".dynamic", 0), DT_NEEDED, uint64(Addstring(Linklookup(ctxt, ".dynstr", 0), s.Dynimplib)))
+ }
+ } else {
+ s.Dynid = int32(Nelfsym)
+ Nelfsym++
+
+ d := Linklookup(ctxt, ".dynsym", 0)
+
+ /* name */
+ name := s.Extname
+
+ Adduint32(ctxt, d, uint32(Addstring(Linklookup(ctxt, ".dynstr", 0), name)))
+
+ /* value */
+ if s.Type == obj.SDYNIMPORT {
+ Adduint32(ctxt, d, 0)
+ } else {
+ Addaddr(ctxt, d, s)
+ }
+
+ /* size */
+ Adduint32(ctxt, d, 0)
+
+ /* type */
+ t := STB_GLOBAL << 4
+
+ // TODO(mwhudson): presumably the behaviour should actually be the same on both arm and 386.
+ if Thearch.Thechar == '8' && s.Cgoexport != 0 && s.Type&obj.SMASK == obj.STEXT {
+ t |= STT_FUNC
+ } else if Thearch.Thechar == '5' && s.Cgoexport&CgoExportDynamic != 0 && s.Type&obj.SMASK == obj.STEXT {
+ t |= STT_FUNC
+ } else {
+ t |= STT_OBJECT
+ }
+ Adduint8(ctxt, d, uint8(t))
+ Adduint8(ctxt, d, 0)
+
+ /* shndx */
+ if s.Type == obj.SDYNIMPORT {
+ Adduint16(ctxt, d, SHN_UNDEF)
+ } else {
+ Adduint16(ctxt, d, 1)
+ }
+ }
+}
+
func ELF32_R_SYM(info uint32) uint32 {
return info >> 8
}
diff --git a/src/cmd/internal/ld/go.go b/src/cmd/link/internal/ld/go.go
similarity index 93%
rename from src/cmd/internal/ld/go.go
rename to src/cmd/link/internal/ld/go.go
index 0223bfae9d..80a6c6ed7d 100644
--- a/src/cmd/internal/ld/go.go
+++ b/src/cmd/link/internal/ld/go.go
@@ -416,7 +416,11 @@ func loadcgo(file string, pkg string, p string) {
// to force a link of foo.so.
havedynamic = 1
- Thearch.Adddynlib(lib)
+ if HEADTYPE == obj.Hdarwin {
+ Machoadddynlib(lib)
+ } else {
+ dynlib = append(dynlib, lib)
+ }
continue
}
@@ -534,6 +538,41 @@ err:
nerrors++
}
+var seenlib = make(map[string]bool)
+
+func adddynlib(lib string) {
+ if seenlib[lib] || Linkmode == LinkExternal {
+ return
+ }
+ seenlib[lib] = true
+
+ if Iself {
+ s := Linklookup(Ctxt, ".dynstr", 0)
+ if s.Size == 0 {
+ Addstring(s, "")
+ }
+ Elfwritedynent(Linklookup(Ctxt, ".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib)))
+ } else {
+ Diag("adddynlib: unsupported binary format")
+ }
+}
+
+func Adddynsym(ctxt *Link, s *LSym) {
+ if s.Dynid >= 0 || Linkmode == LinkExternal {
+ return
+ }
+
+ if Iself {
+ Elfadddynsym(ctxt, s)
+ } else if HEADTYPE == obj.Hdarwin {
+ Diag("adddynsym: missed symbol %s (%s)", s.Name, s.Extname)
+ } else if HEADTYPE == obj.Hwindows {
+ // already taken care of
+ } else {
+ Diag("adddynsym: unsupported binary format")
+ }
+}
+
var markq *LSym
var emarkq *LSym
@@ -614,15 +653,14 @@ func deadcode() {
}
if Buildmode == BuildmodeShared {
- // Mark all symbols as reachable when building a
- // shared library.
+ // Mark all symbols defined in this library as reachable when
+ // building a shared library.
for s := Ctxt.Allsym; s != nil; s = s.Allsym {
- if s.Type != 0 {
+ if s.Type != 0 && s.Type != obj.SDYNIMPORT {
mark(s)
}
}
- mark(Linkrlookup(Ctxt, "main.main", 0))
- mark(Linkrlookup(Ctxt, "main.init", 0))
+ markflood()
} else {
mark(Linklookup(Ctxt, INITENTRY, 0))
if Linkshared && Buildmode == BuildmodeExe {
@@ -737,8 +775,11 @@ func addexport() {
return
}
- for i := 0; i < len(dynexp); i++ {
- Thearch.Adddynsym(Ctxt, dynexp[i])
+ for _, exp := range dynexp {
+ Adddynsym(Ctxt, exp)
+ }
+ for _, lib := range dynlib {
+ adddynlib(lib)
}
}
diff --git a/src/cmd/internal/ld/ld.go b/src/cmd/link/internal/ld/ld.go
similarity index 97%
rename from src/cmd/internal/ld/ld.go
rename to src/cmd/link/internal/ld/ld.go
index 7242301d0f..1068bdd767 100644
--- a/src/cmd/internal/ld/ld.go
+++ b/src/cmd/link/internal/ld/ld.go
@@ -109,8 +109,8 @@ func addlibpath(ctxt *Link, srcref string, objref string, file string, pkg strin
fmt.Fprintf(ctxt.Bso, "%5.2f addlibpath: srcref: %s objref: %s file: %s pkg: %s shlibnamefile: %s\n", obj.Cputime(), srcref, objref, file, pkg, shlibnamefile)
}
- ctxt.Library = append(ctxt.Library, Library{})
- l := &ctxt.Library[len(ctxt.Library)-1]
+ ctxt.Library = append(ctxt.Library, &Library{})
+ l := ctxt.Library[len(ctxt.Library)-1]
l.Objref = objref
l.Srcref = srcref
l.File = file
diff --git a/src/cmd/internal/ld/ldelf.go b/src/cmd/link/internal/ld/ldelf.go
similarity index 100%
rename from src/cmd/internal/ld/ldelf.go
rename to src/cmd/link/internal/ld/ldelf.go
diff --git a/src/cmd/internal/ld/ldmacho.go b/src/cmd/link/internal/ld/ldmacho.go
similarity index 100%
rename from src/cmd/internal/ld/ldmacho.go
rename to src/cmd/link/internal/ld/ldmacho.go
diff --git a/src/cmd/internal/ld/ldpe.go b/src/cmd/link/internal/ld/ldpe.go
similarity index 100%
rename from src/cmd/internal/ld/ldpe.go
rename to src/cmd/link/internal/ld/ldpe.go
diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
similarity index 88%
rename from src/cmd/internal/ld/lib.go
rename to src/cmd/link/internal/ld/lib.go
index 184175e026..d87f1801f0 100644
--- a/src/cmd/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -34,7 +34,9 @@ import (
"bufio"
"bytes"
"cmd/internal/obj"
+ "crypto/sha1"
"debug/elf"
+ "encoding/binary"
"fmt"
"io"
"io/ioutil"
@@ -86,15 +88,14 @@ type Arch struct {
Maxalign int
Minlc int
Dwarfregsp int
+ Dwarfreglr int
Linuxdynld string
Freebsddynld string
Netbsddynld string
Openbsddynld string
Dragonflydynld string
Solarisdynld string
- Adddynlib func(string)
Adddynrel func(*LSym, *Reloc)
- Adddynsym func(*Link, *LSym)
Archinit func()
Archreloc func(*Reloc, *LSym, *int64) int
Archrelocvariant func(*Reloc, *LSym, int64) int64
@@ -162,7 +163,7 @@ type Section struct {
Length uint64
Next *Section
Seg *Segment
- Elfsect interface{}
+ Elfsect *ElfShdr
Reloff uint64
Rellen uint64
}
@@ -178,6 +179,7 @@ var (
Thelinkarch *LinkArch
outfile string
dynexp []*LSym
+ dynlib []string
ldflag []string
havedynamic int
Funcalign int
@@ -473,7 +475,7 @@ func loadlib() {
if Ctxt.Library[i].Shlib != "" {
ldshlibsyms(Ctxt.Library[i].Shlib)
} else {
- objfile(Ctxt.Library[i].File, Ctxt.Library[i].Pkg)
+ objfile(Ctxt.Library[i])
}
}
@@ -519,7 +521,7 @@ func loadlib() {
if DynlinkingGo() {
Exitf("cannot implicitly include runtime/cgo in a shared library")
}
- objfile(Ctxt.Library[i].File, Ctxt.Library[i].Pkg)
+ objfile(Ctxt.Library[i])
}
}
}
@@ -630,18 +632,18 @@ func nextar(bp *obj.Biobuf, off int64, a *ArHdr) int64 {
return int64(arsize) + SAR_HDR
}
-func objfile(file string, pkg string) {
- pkg = pathtoprefix(pkg)
+func objfile(lib *Library) {
+ pkg := pathtoprefix(lib.Pkg)
if Debug['v'] > 1 {
- fmt.Fprintf(&Bso, "%5.2f ldobj: %s (%s)\n", obj.Cputime(), file, pkg)
+ fmt.Fprintf(&Bso, "%5.2f ldobj: %s (%s)\n", obj.Cputime(), lib.File, pkg)
}
Bso.Flush()
var err error
var f *obj.Biobuf
- f, err = obj.Bopenr(file)
+ f, err = obj.Bopenr(lib.File)
if err != nil {
- Exitf("cannot open file %s: %v", file, err)
+ Exitf("cannot open file %s: %v", lib.File, err)
}
magbuf := make([]byte, len(ARMAG))
@@ -650,7 +652,7 @@ func objfile(file string, pkg string) {
l := obj.Bseek(f, 0, 2)
obj.Bseek(f, 0, 0)
- ldobj(f, pkg, l, file, file, FileObj)
+ ldobj(f, pkg, l, lib.File, lib.File, FileObj)
obj.Bterm(f)
return
@@ -663,7 +665,7 @@ func objfile(file string, pkg string) {
l := nextar(f, off, &arhdr)
var pname string
if l <= 0 {
- Diag("%s: short read on archive file symbol header", file)
+ Diag("%s: short read on archive file symbol header", lib.File)
goto out
}
@@ -671,20 +673,29 @@ func objfile(file string, pkg string) {
off += l
l = nextar(f, off, &arhdr)
if l <= 0 {
- Diag("%s: short read on archive file symbol header", file)
+ 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", file)
+ Diag("%s: cannot find package header", lib.File)
goto out
}
+ if Buildmode == BuildmodeShared {
+ before := obj.Boffset(f)
+ pkgdefBytes := make([]byte, atolwhex(arhdr.size))
+ obj.Bread(f, pkgdefBytes)
+ hash := sha1.Sum(pkgdefBytes)
+ lib.hash = hash[:]
+ obj.Bseek(f, before, 0)
+ }
+
off += l
if Debug['u'] != 0 {
- ldpkg(f, pkg, atolwhex(arhdr.size), file, Pkgdef)
+ ldpkg(f, pkg, atolwhex(arhdr.size), lib.File, Pkgdef)
}
/*
@@ -705,14 +716,14 @@ func objfile(file string, pkg string) {
break
}
if l < 0 {
- Exitf("%s: malformed archive", file)
+ Exitf("%s: malformed archive", lib.File)
}
off += l
- pname = fmt.Sprintf("%s(%s)", file, arhdr.name)
+ pname = fmt.Sprintf("%s(%s)", lib.File, arhdr.name)
l = atolwhex(arhdr.size)
- ldobj(f, pkg, l, pname, file, ArchiveObj)
+ ldobj(f, pkg, l, pname, lib.File, ArchiveObj)
}
out:
@@ -912,7 +923,7 @@ func hostlink() {
}
if HEADTYPE == obj.Hdarwin {
- argv = append(argv, "-Wl,-no_pie,-pagezero_size,4000000")
+ argv = append(argv, "-Wl,-no_pie,-pagezero_size,4000000,-headerpad,1144")
}
if HEADTYPE == obj.Hopenbsd {
argv = append(argv, "-Wl,-nopie")
@@ -972,15 +983,35 @@ func hostlink() {
argv = append(argv, fmt.Sprintf("%s/go.o", tmpdir))
if Linkshared {
- for _, shlib := range Ctxt.Shlibs {
- dir, base := filepath.Split(shlib)
- argv = append(argv, "-L"+dir)
- if !rpath.set {
- argv = append(argv, "-Wl,-rpath="+dir)
+ seenDirs := make(map[string]bool)
+ seenLibs := make(map[string]bool)
+ addshlib := func(path string) {
+ dir, base := filepath.Split(path)
+ if !seenDirs[dir] {
+ argv = append(argv, "-L"+dir)
+ if !rpath.set {
+ argv = append(argv, "-Wl,-rpath="+dir)
+ }
+ seenDirs[dir] = true
}
base = strings.TrimSuffix(base, ".so")
base = strings.TrimPrefix(base, "lib")
- argv = append(argv, "-l"+base)
+ if !seenLibs[base] {
+ argv = append(argv, "-l"+base)
+ seenLibs[base] = true
+ }
+ }
+ for _, shlib := range Ctxt.Shlibs {
+ addshlib(shlib.Path)
+ for _, dep := range shlib.Deps {
+ if dep == "" {
+ continue
+ }
+ libpath := findshlib(dep)
+ if libpath != "" {
+ addshlib(libpath)
+ }
+ }
}
}
@@ -1018,6 +1049,31 @@ func hostlink() {
if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil {
Exitf("running %s failed: %v\n%s", argv[0], err, out)
+ } else if Debug['v'] != 0 && len(out) > 0 {
+ fmt.Fprintf(&Bso, "%s", out)
+ Bso.Flush()
+ }
+
+ if Debug['s'] == 0 && debug_s == 0 && HEADTYPE == obj.Hdarwin {
+ // Skip combining dwarf on arm.
+ if Thearch.Thechar != '5' && Thearch.Thechar != '7' {
+ dsym := fmt.Sprintf("%s/go.dwarf", tmpdir)
+ if out, err := exec.Command("dsymutil", "-f", outfile, "-o", dsym).CombinedOutput(); err != nil {
+ Ctxt.Cursym = nil
+ Exitf("%s: running dsymutil failed: %v\n%s", os.Args[0], err, out)
+ }
+ combinedOutput := fmt.Sprintf("%s/go.combined", tmpdir)
+ if err := machoCombineDwarf(outfile, dsym, combinedOutput); err != nil {
+ Ctxt.Cursym = nil
+ Exitf("%s: combining dwarf failed: %v", os.Args[0], err)
+ }
+ origOutput := fmt.Sprintf("%s/go.orig", tmpdir)
+ os.Rename(outfile, origOutput)
+ if err := os.Rename(combinedOutput, outfile); err != nil {
+ Ctxt.Cursym = nil
+ Exitf("%s: rename(%s, %s) failed: %v", os.Args[0], combinedOutput, outfile, err)
+ }
+ }
}
}
@@ -1119,22 +1175,86 @@ func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, when
ldobjfile(Ctxt, f, pkg, eof-obj.Boffset(f), pn)
}
-func ldshlibsyms(shlib string) {
- found := false
- libpath := ""
- for _, libdir := range Ctxt.Libdir {
- libpath = filepath.Join(libdir, shlib)
- if _, err := os.Stat(libpath); err == nil {
- found = true
- break
+func readelfsymboldata(f *elf.File, sym *elf.Symbol) []byte {
+ data := make([]byte, sym.Size)
+ sect := f.Sections[sym.Section]
+ if sect.Type != elf.SHT_PROGBITS && sect.Type != elf.SHT_NOTE {
+ Diag("reading %s from non-data section", sym.Name)
+ }
+ n, err := sect.ReadAt(data, int64(sym.Value-sect.Addr))
+ if uint64(n) != sym.Size {
+ Diag("reading contents of %s: %v", sym.Name, err)
+ }
+ return data
+}
+
+func readwithpad(r io.Reader, sz int32) ([]byte, error) {
+ data := make([]byte, Rnd(int64(sz), 4))
+ _, err := io.ReadFull(r, data)
+ if err != nil {
+ return nil, err
+ }
+ data = data[:sz]
+ return data, nil
+}
+
+func readnote(f *elf.File, name []byte, typ int32) ([]byte, error) {
+ for _, sect := range f.Sections {
+ if sect.Type != elf.SHT_NOTE {
+ continue
+ }
+ r := sect.Open()
+ for {
+ var namesize, descsize, noteType int32
+ err := binary.Read(r, f.ByteOrder, &namesize)
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ return nil, fmt.Errorf("read namesize failed:", err)
+ }
+ err = binary.Read(r, f.ByteOrder, &descsize)
+ if err != nil {
+ return nil, fmt.Errorf("read descsize failed:", err)
+ }
+ err = binary.Read(r, f.ByteOrder, ¬eType)
+ if err != nil {
+ return nil, fmt.Errorf("read type failed:", err)
+ }
+ noteName, err := readwithpad(r, namesize)
+ if err != nil {
+ return nil, fmt.Errorf("read name failed:", err)
+ }
+ desc, err := readwithpad(r, descsize)
+ if err != nil {
+ return nil, fmt.Errorf("read desc failed:", err)
+ }
+ if string(name) == string(noteName) && typ == noteType {
+ return desc, nil
+ }
}
}
- if !found {
- Diag("cannot find shared library: %s", shlib)
+ return nil, nil
+}
+
+func findshlib(shlib string) string {
+ for _, libdir := range Ctxt.Libdir {
+ libpath := filepath.Join(libdir, shlib)
+ if _, err := os.Stat(libpath); err == nil {
+ return libpath
+ }
+ }
+ Diag("cannot find shared library: %s", shlib)
+ return ""
+}
+
+func ldshlibsyms(shlib string) {
+ libpath := findshlib(shlib)
+ if libpath == "" {
return
}
- for _, processedname := range Ctxt.Shlibs {
- if processedname == libpath {
+ for _, processedlib := range Ctxt.Shlibs {
+ if processedlib.Path == libpath {
return
}
}
@@ -1148,85 +1268,48 @@ func ldshlibsyms(shlib string) {
Diag("cannot open shared library: %s", libpath)
return
}
- defer f.Close()
- syms, err := f.Symbols()
+
+ hash, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GOABIHASH_TAG)
+ if err != nil {
+ Diag("cannot read ABI hash from shared library %s: %v", libpath, err)
+ return
+ }
+
+ depsbytes, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GODEPS_TAG)
+ if err != nil {
+ Diag("cannot read dep list from shared library %s: %v", libpath, err)
+ return
+ }
+ deps := strings.Split(string(depsbytes), "\n")
+
+ syms, err := f.DynamicSymbols()
if err != nil {
Diag("cannot read symbols from shared library: %s", libpath)
return
}
- // If a package has a global variable of a type defined in another shared
- // library, we need to know the gcmask used by the type, if any. To support
- // this, we read all the runtime.gcbits.* symbols, keep a map of address to
- // gcmask, and after we're read all the symbols, read the addresses of the
- // gcmasks symbols out of the type data to look up the gcmask for each type.
- // This depends on the fact that the runtime.gcbits.* symbols are local (so
- // the address is actually present in the type data and we don't have to
- // search all relocations to find the ones which correspond to gcmasks) and
- // also that the shared library we are linking against has not had the symbol
- // table removed.
- gcmasks := make(map[uint64][]byte)
- types := []*LSym{}
for _, s := range syms {
if elf.ST_TYPE(s.Info) == elf.STT_NOTYPE || elf.ST_TYPE(s.Info) == elf.STT_SECTION {
continue
}
- if s.Section == elf.SHN_UNDEF {
- continue
- }
- if strings.HasPrefix(s.Name, "_") {
- continue
- }
- if strings.HasPrefix(s.Name, "runtime.gcbits.0x") {
- data := make([]byte, s.Size)
- sect := f.Sections[s.Section]
- if sect.Type == elf.SHT_PROGBITS {
- n, err := sect.ReadAt(data, int64(s.Value-sect.Offset))
- if uint64(n) != s.Size {
- Diag("Error reading contents of %s: %v", s.Name, err)
- }
- }
- gcmasks[s.Value] = data
- }
- if elf.ST_BIND(s.Info) != elf.STB_GLOBAL {
- continue
- }
lsym := Linklookup(Ctxt, s.Name, 0)
- if lsym.Type != 0 && lsym.Dupok == 0 {
+ if lsym.Type != 0 && lsym.Type != obj.SDYNIMPORT && lsym.Dupok == 0 {
Diag(
"Found duplicate symbol %s reading from %s, first found in %s",
s.Name, shlib, lsym.File)
}
lsym.Type = obj.SDYNIMPORT
lsym.ElfType = elf.ST_TYPE(s.Info)
- lsym.File = libpath
- if strings.HasPrefix(lsym.Name, "type.") {
- data := make([]byte, s.Size)
- sect := f.Sections[s.Section]
- if sect.Type == elf.SHT_PROGBITS {
- n, err := sect.ReadAt(data, int64(s.Value-sect.Offset))
- if uint64(n) != s.Size {
- Diag("Error reading contents of %s: %v", s.Name, err)
- }
- lsym.P = data
- }
- if !strings.HasPrefix(lsym.Name, "type..") {
- types = append(types, lsym)
+ if s.Section != elf.SHN_UNDEF {
+ // Set .File for the library that actually defines the symbol.
+ lsym.File = libpath
+ // The decodetype_* functions in decodetype.go need access to
+ // the type data.
+ if strings.HasPrefix(lsym.Name, "type.") && !strings.HasPrefix(lsym.Name, "type..") {
+ lsym.P = readelfsymboldata(f, &s)
}
}
}
- for _, t := range types {
- if decodetype_noptr(t) != 0 || decodetype_usegcprog(t) != 0 {
- continue
- }
- addr := decodetype_gcprog_shlib(t)
- tgcmask, ok := gcmasks[addr]
- if !ok {
- Diag("bits not found for %s at %d", t.Name, addr)
- }
- t.gcmask = tgcmask
- }
-
// We might have overwritten some functions above (this tends to happen for the
// autogenerated type equality/hashing functions) and we don't want to generated
// pcln table entries for these any more so unstitch them from the Textp linked
@@ -1254,7 +1337,7 @@ func ldshlibsyms(shlib string) {
Ctxt.Etextp = last
}
- Ctxt.Shlibs = append(Ctxt.Shlibs, libpath)
+ Ctxt.Shlibs = append(Ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f})
}
func mywhatsys() {
@@ -1572,12 +1655,11 @@ func Cflush() {
}
func Cpos() int64 {
- Cflush()
off, err := coutbuf.f.Seek(0, 1)
if err != nil {
Exitf("seeking in output [0, 1]: %v", err)
}
- return off
+ return off + int64(coutbuf.Buffered())
}
func Cseek(p int64) {
@@ -1596,7 +1678,7 @@ func Cput(c uint8) {
}
func usage() {
- fmt.Fprintf(os.Stderr, "usage: %cl [options] obj.%c\n", Thearch.Thechar, Thearch.Thechar)
+ fmt.Fprintf(os.Stderr, "usage: link [options] main.o\n")
obj.Flagprint(2)
Exit(2)
}
diff --git a/src/cmd/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
similarity index 94%
rename from src/cmd/internal/ld/link.go
rename to src/cmd/link/internal/ld/link.go
index 03da52a981..33b17c5985 100644
--- a/src/cmd/internal/ld/link.go
+++ b/src/cmd/link/internal/ld/link.go
@@ -34,6 +34,7 @@ import (
"cmd/internal/obj"
"debug/elf"
"encoding/binary"
+ "fmt"
)
type LSym struct {
@@ -77,13 +78,19 @@ type LSym struct {
File string
Dynimplib string
Dynimpvers string
- Sect interface{}
+ Sect *Section
Autom *Auto
Pcln *Pcln
P []byte
R []Reloc
Local bool
- gcmask []byte
+}
+
+func (s *LSym) String() string {
+ if s.Version == 0 {
+ return s.Name
+ }
+ return fmt.Sprintf("%s<%d>", s.Name, s.Version)
}
type Reloc struct {
@@ -106,6 +113,13 @@ type Auto struct {
Gotype *LSym
}
+type Shlib struct {
+ Path string
+ Hash []byte
+ Deps []string
+ File *elf.File
+}
+
type Link struct {
Thechar int32
Thestring string
@@ -122,8 +136,8 @@ type Link struct {
Nsymbol int32
Tlsg *LSym
Libdir []string
- Library []Library
- Shlibs []string
+ Library []*Library
+ Shlibs []Shlib
Tlsoffset int
Diag func(string, ...interface{})
Cursym *LSym
@@ -149,6 +163,7 @@ type Library struct {
File string
Pkg string
Shlib string
+ hash []byte
}
type Pcln struct {
diff --git a/src/cmd/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
similarity index 98%
rename from src/cmd/internal/ld/macho.go
rename to src/cmd/link/internal/ld/macho.go
index ceeb7b0f5d..3a8a881d97 100644
--- a/src/cmd/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -443,7 +443,8 @@ func Asmbmacho() {
ms = newMachoSeg("", 40)
ms.fileoffset = Segtext.Fileoff
- ms.filesize = Segdata.Fileoff + Segdata.Filelen - Segtext.Fileoff
+ ms.filesize = Segdwarf.Fileoff + Segdwarf.Filelen - Segtext.Fileoff
+ ms.vsize = ms.filesize
}
/* segment for zero page */
@@ -561,8 +562,8 @@ func Asmbmacho() {
}
// TODO: dwarf headers go in ms too
- if Debug['s'] == 0 && Linkmode != LinkExternal {
- dwarfaddmachoheaders()
+ if Debug['s'] == 0 {
+ dwarfaddmachoheaders(ms)
}
a := machowrite()
@@ -705,7 +706,7 @@ func machosymtab() {
Diag("missing section for %s", s.Name)
Adduint8(Ctxt, symtab, 0)
} else {
- Adduint8(Ctxt, symtab, uint8((o.Sect.(*Section)).Extnum))
+ Adduint8(Ctxt, symtab, uint8(o.Sect.Extnum))
}
Adduint16(Ctxt, symtab, 0) // desc
adduintxx(Ctxt, symtab, uint64(Symaddr(s)), Thearch.Ptrsize)
@@ -850,4 +851,5 @@ func Machoemitreloc() {
for sect := Segdata.Sect; sect != nil; sect = sect.Next {
machorelocsect(sect, datap)
}
+ dwarfemitreloc()
}
diff --git a/src/cmd/link/internal/ld/macho_combine_dwarf.go b/src/cmd/link/internal/ld/macho_combine_dwarf.go
new file mode 100644
index 0000000000..9134373a52
--- /dev/null
+++ b/src/cmd/link/internal/ld/macho_combine_dwarf.go
@@ -0,0 +1,369 @@
+// 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 ld
+
+import (
+ "bytes"
+ "debug/macho"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "unsafe"
+)
+
+var fakedwarf, realdwarf, linkseg *macho.Segment
+var dwarfstart, linkstart int64
+var linkoffset uint32
+var machHeader *macho.FileHeader
+var mappedHeader []byte
+
+const (
+ LC_LOAD_DYLINKER = 0xe
+ LC_PREBOUND_DYLIB = 0x10
+ LC_LOAD_WEAK_DYLIB = 0x18
+ LC_UUID = 0x1b
+ LC_RPATH = 0x8000001c
+ LC_CODE_SIGNATURE = 0x1d
+ LC_SEGMENT_SPLIT_INFO = 0x1e
+ LC_REEXPORT_DYLIB = 0x8000001f
+ LC_ENCRYPTION_INFO = 0x21
+ LC_DYLD_INFO = 0x22
+ LC_DYLD_INFO_ONLY = 0x80000022
+ LC_VERSION_MIN_MACOSX = 0x24
+ LC_VERSION_MIN_IPHONEOS = 0x25
+ LC_FUNCTION_STARTS = 0x26
+ LC_MAIN = 0x80000028
+ LC_DATA_IN_CODE = 0x29
+ LC_SOURCE_VERSION = 0x2A
+ LC_DYLIB_CODE_SIGN_DRS = 0x2B
+ LC_ENCRYPTION_INFO_64 = 0x2C
+
+ dwarfMinAlign = 6 // 64 = 1 << 6
+ pageAlign = 12 // 4096 = 1 << 12
+)
+
+type loadCmd struct {
+ Cmd macho.LoadCmd
+ Len uint32
+}
+
+type dyldInfoCmd struct {
+ Cmd macho.LoadCmd
+ Len uint32
+ RebaseOff, RebaseLen uint32
+ BindOff, BindLen uint32
+ WeakBindOff, WeakBindLen uint32
+ LazyBindOff, LazyBindLen uint32
+ ExportOff, ExportLen uint32
+}
+
+type linkEditDataCmd struct {
+ Cmd macho.LoadCmd
+ Len uint32
+ DataOff, DataLen uint32
+}
+
+type encryptionInfoCmd struct {
+ Cmd macho.LoadCmd
+ Len uint32
+ CryptOff, CryptLen uint32
+ CryptId uint32
+}
+
+type loadCmdReader struct {
+ offset, next int64
+ f *os.File
+ order binary.ByteOrder
+}
+
+func (r *loadCmdReader) Next() (cmd loadCmd, err error) {
+ r.offset = r.next
+ if _, err = r.f.Seek(r.offset, 0); err != nil {
+ return
+ }
+ if err = binary.Read(r.f, r.order, &cmd); err != nil {
+ return
+ }
+ r.next = r.offset + int64(cmd.Len)
+ return
+}
+
+func (r loadCmdReader) ReadAt(offset int64, data interface{}) error {
+ if _, err := r.f.Seek(r.offset+offset, 0); err != nil {
+ return err
+ }
+ return binary.Read(r.f, r.order, data)
+}
+
+func (r loadCmdReader) WriteAt(offset int64, data interface{}) error {
+ if _, err := r.f.Seek(r.offset+offset, 0); err != nil {
+ return err
+ }
+ return binary.Write(r.f, r.order, data)
+}
+
+// machoCombineDwarf merges dwarf info generated by dsymutil into a macho executable.
+// With internal linking, DWARF is embedded into the executable, this lets us do the
+// same for external linking.
+// inexe is the path to the executable with no DWARF. It must have enough room in the macho
+// header to add the DWARF sections. (Use ld's -headerpad option)
+// dsym is the path to the macho file containing DWARF from dsymutil.
+// outexe is the path where the combined executable should be saved.
+func machoCombineDwarf(inexe, dsym, outexe string) error {
+ exef, err := os.Open(inexe)
+ if err != nil {
+ return err
+ }
+ dwarff, err := os.Open(dsym)
+ if err != nil {
+ return err
+ }
+ outf, err := os.Create(outexe)
+ if err != nil {
+ return err
+ }
+ outf.Chmod(0755)
+
+ exem, err := macho.NewFile(exef)
+ if err != nil {
+ return err
+ }
+ dwarfm, err := macho.NewFile(dwarff)
+ if err != nil {
+ return err
+ }
+
+ // The string table needs to be the last thing in the file
+ // for code signing to work. So we'll need to move the
+ // linkedit section, but all the others can be copied directly.
+ linkseg = exem.Segment("__LINKEDIT")
+ if linkseg == nil {
+ return fmt.Errorf("missing __LINKEDIT segment")
+ }
+
+ if _, err = exef.Seek(0, 0); err != nil {
+ return err
+ }
+ if _, err := io.CopyN(outf, exef, int64(linkseg.Offset)); err != nil {
+ return err
+ }
+
+ realdwarf = dwarfm.Segment("__DWARF")
+ if realdwarf == nil {
+ return fmt.Errorf("missing __DWARF segment")
+ }
+
+ // Now copy the dwarf data into the output.
+ maxalign := uint32(dwarfMinAlign) //
+ for _, sect := range dwarfm.Sections {
+ if sect.Align > maxalign {
+ maxalign = sect.Align
+ }
+ }
+ dwarfstart = machoCalcStart(realdwarf.Offset, linkseg.Offset, maxalign)
+ if _, err = outf.Seek(dwarfstart, 0); err != nil {
+ return err
+ }
+
+ if _, err = dwarff.Seek(int64(realdwarf.Offset), 0); err != nil {
+ return err
+ }
+ if _, err := io.CopyN(outf, dwarff, int64(realdwarf.Filesz)); err != nil {
+ return err
+ }
+
+ // And finally the linkedit section.
+ if _, err = exef.Seek(int64(linkseg.Offset), 0); err != nil {
+ return err
+ }
+ linkstart = machoCalcStart(linkseg.Offset, uint64(dwarfstart)+realdwarf.Filesz, pageAlign)
+ linkoffset = uint32(linkstart - int64(linkseg.Offset))
+ if _, err = outf.Seek(linkstart, 0); err != nil {
+ return err
+ }
+ if _, err := io.Copy(outf, exef); err != nil {
+ return err
+ }
+
+ // Now we need to update the headers.
+ cmdOffset := unsafe.Sizeof(exem.FileHeader)
+ is64bit := exem.Magic == macho.Magic64
+ if is64bit {
+ // mach_header_64 has one extra uint32.
+ cmdOffset += unsafe.Sizeof(exem.Magic)
+ }
+
+ textsect := exem.Section("__text")
+ if linkseg == nil {
+ return fmt.Errorf("missing __text section")
+ }
+
+ dwarfCmdOffset := int64(cmdOffset) + int64(exem.FileHeader.Cmdsz)
+ availablePadding := int64(textsect.Offset) - dwarfCmdOffset
+ if availablePadding < int64(realdwarf.Len) {
+ return fmt.Errorf("No room to add dwarf info. Need at least %d padding bytes, found %d", realdwarf.Len, availablePadding)
+ }
+ // First, copy the dwarf load command into the header
+ if _, err = outf.Seek(dwarfCmdOffset, 0); err != nil {
+ return err
+ }
+ if _, err := io.CopyN(outf, bytes.NewReader(realdwarf.Raw()), int64(realdwarf.Len)); err != nil {
+ return err
+ }
+
+ if _, err = outf.Seek(int64(unsafe.Offsetof(exem.FileHeader.Ncmd)), 0); err != nil {
+ return err
+ }
+ if err = binary.Write(outf, exem.ByteOrder, exem.Ncmd+1); err != nil {
+ return err
+ }
+ if err = binary.Write(outf, exem.ByteOrder, exem.Cmdsz+realdwarf.Len); err != nil {
+ return err
+ }
+
+ reader := loadCmdReader{next: int64(cmdOffset), f: outf, order: exem.ByteOrder}
+ for i := uint32(0); i < exem.Ncmd; i++ {
+ cmd, err := reader.Next()
+ if err != nil {
+ return err
+ }
+ switch cmd.Cmd {
+ case macho.LoadCmdSegment64:
+ err = machoUpdateSegment(reader, &macho.Segment64{}, &macho.Section64{})
+ case macho.LoadCmdSegment:
+ err = machoUpdateSegment(reader, &macho.Segment32{}, &macho.Section32{})
+ case LC_DYLD_INFO, LC_DYLD_INFO_ONLY:
+ err = machoUpdateLoadCommand(reader, &dyldInfoCmd{}, "RebaseOff", "BindOff", "WeakBindOff", "LazyBindOff", "ExportOff")
+ case macho.LoadCmdSymtab:
+ err = machoUpdateLoadCommand(reader, &macho.SymtabCmd{}, "Symoff", "Stroff")
+ case macho.LoadCmdDysymtab:
+ err = machoUpdateLoadCommand(reader, &macho.DysymtabCmd{}, "Tocoffset", "Modtaboff", "Extrefsymoff", "Indirectsymoff", "Extreloff", "Locreloff")
+ case LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS:
+ err = machoUpdateLoadCommand(reader, &linkEditDataCmd{}, "DataOff")
+ case LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64:
+ err = machoUpdateLoadCommand(reader, &encryptionInfoCmd{}, "CryptOff")
+ case macho.LoadCmdDylib, macho.LoadCmdThread, macho.LoadCmdUnixThread, LC_PREBOUND_DYLIB, LC_UUID, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_SOURCE_VERSION, LC_MAIN, LC_LOAD_DYLINKER, LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_RPATH:
+ // Nothing to update
+ default:
+ err = fmt.Errorf("Unknown load command 0x%x (%s)\n", int(cmd.Cmd), cmd.Cmd)
+ }
+ if err != nil {
+ return err
+ }
+ }
+ return machoUpdateDwarfHeader(&reader)
+}
+
+// machoUpdateSegment updates the load command for a moved segment.
+// Only the linkedit segment should move, and it should have 0 sections.
+// seg should be a macho.Segment32 or macho.Segment64 as appropriate.
+// sect should be a macho.Section32 or macho.Section64 as appropriate.
+func machoUpdateSegment(r loadCmdReader, seg, sect interface{}) error {
+ if err := r.ReadAt(0, seg); err != nil {
+ return err
+ }
+ segValue := reflect.ValueOf(seg)
+ offset := reflect.Indirect(segValue).FieldByName("Offset")
+
+ // Only the linkedit segment moved, any thing before that is fine.
+ if offset.Uint() < linkseg.Offset {
+ return nil
+ }
+ offset.SetUint(offset.Uint() + uint64(linkoffset))
+ if err := r.WriteAt(0, seg); err != nil {
+ return err
+ }
+ // There shouldn't be any sections, but just to make sure...
+ return machoUpdateSections(r, segValue, reflect.ValueOf(sect), uint64(linkoffset))
+}
+
+func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, delta uint64) error {
+ iseg := reflect.Indirect(seg)
+ nsect := iseg.FieldByName("Nsect").Uint()
+ if nsect == 0 {
+ return nil
+ }
+ sectOffset := int64(iseg.Type().Size())
+
+ isect := reflect.Indirect(sect)
+ offsetField := isect.FieldByName("Offset")
+ reloffField := isect.FieldByName("Reloff")
+ sectSize := int64(isect.Type().Size())
+ for i := uint64(0); i < nsect; i++ {
+ if err := r.ReadAt(sectOffset, sect.Interface()); err != nil {
+ return err
+ }
+ if offsetField.Uint() != 0 {
+ offsetField.SetUint(offsetField.Uint() + delta)
+ }
+ if reloffField.Uint() != 0 {
+ reloffField.SetUint(reloffField.Uint() + delta)
+ }
+ if err := r.WriteAt(sectOffset, sect.Interface()); err != nil {
+ return err
+ }
+ sectOffset += sectSize
+ }
+ return nil
+}
+
+// machoUpdateDwarfHeader updates the DWARF segment load command.
+func machoUpdateDwarfHeader(r *loadCmdReader) error {
+ var seg, sect interface{}
+ cmd, err := r.Next()
+ if err != nil {
+ return err
+ }
+ if cmd.Cmd == macho.LoadCmdSegment64 {
+ seg = new(macho.Segment64)
+ sect = new(macho.Section64)
+ } else {
+ seg = new(macho.Segment32)
+ sect = new(macho.Section32)
+ }
+ if err := r.ReadAt(0, seg); err != nil {
+ return err
+ }
+ segValue := reflect.ValueOf(seg)
+ offset := reflect.Indirect(segValue).FieldByName("Offset")
+
+ delta := uint64(dwarfstart) - realdwarf.Offset
+ offset.SetUint(offset.Uint() + delta)
+ if err := r.WriteAt(0, seg); err != nil {
+ return err
+ }
+ return machoUpdateSections(*r, segValue, reflect.ValueOf(sect), delta)
+}
+
+func machoUpdateLoadCommand(r loadCmdReader, cmd interface{}, fields ...string) error {
+ if err := r.ReadAt(0, cmd); err != nil {
+ return err
+ }
+ value := reflect.Indirect(reflect.ValueOf(cmd))
+
+ for _, name := range fields {
+ field := value.FieldByName(name)
+ fieldval := field.Uint()
+ if fieldval >= linkseg.Offset {
+ field.SetUint(fieldval + uint64(linkoffset))
+ }
+ }
+ if err := r.WriteAt(0, cmd); err != nil {
+ return err
+ }
+ return nil
+}
+
+func machoCalcStart(origAddr, newAddr uint64, alignExp uint32) int64 {
+ align := uint64(1 << alignExp)
+ if (origAddr % align) == (newAddr % align) {
+ return int64(newAddr)
+ }
+ padding := (align - (newAddr % align))
+ padding += origAddr % align
+ return int64(padding + newAddr)
+}
diff --git a/src/cmd/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go
similarity index 99%
rename from src/cmd/internal/ld/objfile.go
rename to src/cmd/link/internal/ld/objfile.go
index 3d59323dba..613fcb2a40 100644
--- a/src/cmd/internal/ld/objfile.go
+++ b/src/cmd/link/internal/ld/objfile.go
@@ -347,7 +347,7 @@ func rdsym(ctxt *Link, f *obj.Biobuf, pkg string) *LSym {
s.Reachable = false
}
}
- if v == 0 && strings.HasPrefix(s.Name, "runtime.gcbits.0x") {
+ if v == 0 && strings.HasPrefix(s.Name, "runtime.gcbits.") {
s.Local = true
}
return s
diff --git a/src/cmd/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
similarity index 100%
rename from src/cmd/internal/ld/pcln.go
rename to src/cmd/link/internal/ld/pcln.go
diff --git a/src/cmd/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go
similarity index 100%
rename from src/cmd/internal/ld/pe.go
rename to src/cmd/link/internal/ld/pe.go
diff --git a/src/cmd/internal/ld/pobj.go b/src/cmd/link/internal/ld/pobj.go
similarity index 98%
rename from src/cmd/internal/ld/pobj.go
rename to src/cmd/link/internal/ld/pobj.go
index 8568744c3d..5b24428059 100644
--- a/src/cmd/internal/ld/pobj.go
+++ b/src/cmd/link/internal/ld/pobj.go
@@ -160,10 +160,9 @@ func Ldmain() {
}
if outfile == "" {
+ outfile = "a.out"
if HEADTYPE == obj.Hwindows {
- outfile = fmt.Sprintf("%c.out.exe", Thearch.Thechar)
- } else {
- outfile = fmt.Sprintf("%c.out", Thearch.Thechar)
+ outfile += ".exe"
}
}
diff --git a/src/cmd/internal/ld/sym.go b/src/cmd/link/internal/ld/sym.go
similarity index 100%
rename from src/cmd/internal/ld/sym.go
rename to src/cmd/link/internal/ld/sym.go
diff --git a/src/cmd/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
similarity index 87%
rename from src/cmd/internal/ld/symtab.go
rename to src/cmd/link/internal/ld/symtab.go
index d6e79dc00f..7ceb64f941 100644
--- a/src/cmd/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -32,6 +32,8 @@ package ld
import (
"cmd/internal/obj"
+ "fmt"
+ "path/filepath"
"strings"
)
@@ -128,12 +130,12 @@ func putelfsym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *L
Diag("missing section in putelfsym")
return
}
- if xo.Sect.(*Section).Elfsect == nil {
+ if xo.Sect.Elfsect == nil {
Ctxt.Cursym = x
Diag("missing ELF section in putelfsym")
return
}
- elfshnum = xo.Sect.(*Section).Elfsect.(*ElfShdr).shnum
+ elfshnum = xo.Sect.Elfsect.shnum
}
// One pass for each binding: STB_LOCAL, STB_GLOBAL,
@@ -159,7 +161,7 @@ func putelfsym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *L
off := putelfstr(s)
if Linkmode == LinkExternal && elfshnum != SHN_UNDEF {
- addr -= int64(xo.Sect.(*Section).Vaddr)
+ addr -= int64(xo.Sect.Vaddr)
}
other := STV_DEFAULT
if x.Type&obj.SHIDDEN != 0 {
@@ -294,6 +296,20 @@ func Vputl(v uint64) {
Lputl(uint32(v >> 32))
}
+type byPkg []*Library
+
+func (libs byPkg) Len() int {
+ return len(libs)
+}
+
+func (libs byPkg) Less(a, b int) bool {
+ return libs[a].Pkg < libs[b].Pkg
+}
+
+func (libs byPkg) Swap(a, b int) {
+ libs[a], libs[b] = libs[b], libs[a]
+}
+
func symtab() {
dosymtype()
@@ -410,6 +426,15 @@ func symtab() {
}
}
+ if Buildmode == BuildmodeShared {
+ abihashgostr := Linklookup(Ctxt, "go.link.abihash."+filepath.Base(outfile), 0)
+ abihashgostr.Reachable = true
+ abihashgostr.Type = obj.SRODATA
+ hashsym := Linklookup(Ctxt, "go.link.abihashbytes", 0)
+ Addaddr(Ctxt, abihashgostr, hashsym)
+ adduint(Ctxt, abihashgostr, uint64(hashsym.Size))
+ }
+
// Information about the layout of the executable image for the
// runtime to use. Any changes here must be matched by changes to
// the definition of moduledata in runtime/symtab.go.
@@ -454,6 +479,38 @@ func symtab() {
Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.typelink", 0))
adduint(Ctxt, moduledata, uint64(ntypelinks))
adduint(Ctxt, moduledata, uint64(ntypelinks))
+ if len(Ctxt.Shlibs) > 0 {
+ thismodulename := filepath.Base(outfile)
+ if Buildmode == BuildmodeExe {
+ // When linking an executable, outfile is just "a.out". Make
+ // it something slightly more comprehensible.
+ thismodulename = "the executable"
+ }
+ addgostring(moduledata, "go.link.thismodulename", thismodulename)
+
+ modulehashes := Linklookup(Ctxt, "go.link.abihashes", 0)
+ modulehashes.Reachable = true
+ modulehashes.Local = true
+ modulehashes.Type = obj.SRODATA
+
+ for i, shlib := range Ctxt.Shlibs {
+ // modulehashes[i].modulename
+ modulename := filepath.Base(shlib.Path)
+ addgostring(modulehashes, fmt.Sprintf("go.link.libname.%d", i), modulename)
+
+ // modulehashes[i].linktimehash
+ addgostring(modulehashes, fmt.Sprintf("go.link.linkhash.%d", i), string(shlib.Hash))
+
+ // modulehashes[i].runtimehash
+ abihash := Linklookup(Ctxt, "go.link.abihash."+modulename, 0)
+ abihash.Reachable = true
+ Addaddr(Ctxt, modulehashes, abihash)
+ }
+
+ Addaddr(Ctxt, moduledata, modulehashes)
+ adduint(Ctxt, moduledata, uint64(len(Ctxt.Shlibs)))
+ adduint(Ctxt, moduledata, uint64(len(Ctxt.Shlibs)))
+ }
// The rest of moduledata is zero initialized.
// When linking an object that does not contain the runtime we are
// creating the moduledata from scratch and it does not have a
diff --git a/src/cmd/internal/ld/textflag.go b/src/cmd/link/internal/ld/textflag.go
similarity index 100%
rename from src/cmd/internal/ld/textflag.go
rename to src/cmd/link/internal/ld/textflag.go
diff --git a/src/cmd/internal/ld/util.go b/src/cmd/link/internal/ld/util.go
similarity index 100%
rename from src/cmd/internal/ld/util.go
rename to src/cmd/link/internal/ld/util.go
diff --git a/src/cmd/9l/asm.go b/src/cmd/link/internal/ppc64/asm.go
similarity index 92%
rename from src/cmd/9l/asm.go
rename to src/cmd/link/internal/ppc64/asm.go
index 257f23e2ab..f070921ecf 100644
--- a/src/cmd/9l/asm.go
+++ b/src/cmd/link/internal/ppc64/asm.go
@@ -28,34 +28,16 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package ppc64
import (
- "cmd/internal/ld"
"cmd/internal/obj"
+ "cmd/link/internal/ld"
"encoding/binary"
"fmt"
"log"
)
-func needlib(name string) int {
- if name[0] == '\x00' {
- return 0
- }
-
- /* reuse hash code in symbol table */
- p := fmt.Sprintf(".dynlib.%s", name)
-
- s := ld.Linklookup(ld.Ctxt, p, 0)
-
- if s.Type == 0 {
- s.Type = 100 // avoid SDATA, etc.
- return 1
- }
-
- return 0
-}
-
func gentext() {
var s *ld.LSym
var stub *ld.LSym
@@ -240,7 +222,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
r.Type = obj.R_ADDR
if targ.Type == obj.SDYNIMPORT {
// These happen in .toc sections
- adddynsym(ld.Ctxt, targ)
+ ld.Adddynsym(ld.Ctxt, targ)
rela := ld.Linklookup(ld.Ctxt, ".rela", 0)
ld.Addaddrplus(ld.Ctxt, rela, s, int64(r.Off))
@@ -520,7 +502,7 @@ func addpltsym(ctxt *ld.Link, s *ld.LSym) {
return
}
- adddynsym(ctxt, s)
+ ld.Adddynsym(ctxt, s)
if ld.Iself {
plt := ld.Linklookup(ctxt, ".plt", 0)
@@ -622,70 +604,6 @@ func ensureglinkresolver() *ld.LSym {
return glink
}
-func adddynsym(ctxt *ld.Link, s *ld.LSym) {
- if s.Dynid >= 0 {
- return
- }
-
- if ld.Iself {
- s.Dynid = int32(ld.Nelfsym)
- ld.Nelfsym++
-
- d := ld.Linklookup(ctxt, ".dynsym", 0)
-
- name := s.Extname
- ld.Adduint32(ctxt, d, uint32(ld.Addstring(ld.Linklookup(ctxt, ".dynstr", 0), name)))
-
- /* type */
- t := ld.STB_GLOBAL << 4
-
- if s.Cgoexport != 0 && s.Type&obj.SMASK == obj.STEXT {
- t |= ld.STT_FUNC
- } else {
- t |= ld.STT_OBJECT
- }
- ld.Adduint8(ctxt, d, uint8(t))
-
- /* reserved */
- ld.Adduint8(ctxt, d, 0)
-
- /* section where symbol is defined */
- if s.Type == obj.SDYNIMPORT {
- ld.Adduint16(ctxt, d, ld.SHN_UNDEF)
- } else {
- ld.Adduint16(ctxt, d, 1)
- }
-
- /* value */
- if s.Type == obj.SDYNIMPORT {
- ld.Adduint64(ctxt, d, 0)
- } else {
- ld.Addaddr(ctxt, d, s)
- }
-
- /* size of object */
- ld.Adduint64(ctxt, d, uint64(s.Size))
- } else {
- ld.Diag("adddynsym: unsupported binary format")
- }
-}
-
-func adddynlib(lib string) {
- if needlib(lib) == 0 {
- return
- }
-
- if ld.Iself {
- s := ld.Linklookup(ld.Ctxt, ".dynstr", 0)
- if s.Size == 0 {
- ld.Addstring(s, "")
- }
- ld.Elfwritedynent(ld.Linklookup(ld.Ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(s, lib)))
- } else {
- ld.Diag("adddynlib: unsupported binary format")
- }
-}
-
func asmb() {
if ld.Debug['v'] != 0 {
fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
diff --git a/src/cmd/9l/l.go b/src/cmd/link/internal/ppc64/l.go
similarity index 98%
rename from src/cmd/9l/l.go
rename to src/cmd/link/internal/ppc64/l.go
index e7dc102af2..1275a34dbb 100644
--- a/src/cmd/9l/l.go
+++ b/src/cmd/link/internal/ppc64/l.go
@@ -28,7 +28,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package ppc64
// Writing object files.
@@ -71,7 +71,8 @@ const (
MINLC = 4
)
-/* Used by ../ld/dwarf.c */
+/* Used by ../internal/ld/dwarf.go */
const (
DWARFREGSP = 1
+ DWARFREGLR = 65
)
diff --git a/src/cmd/9l/obj.go b/src/cmd/link/internal/ppc64/obj.go
similarity index 97%
rename from src/cmd/9l/obj.go
rename to src/cmd/link/internal/ppc64/obj.go
index 46a92396e4..d663b6ebae 100644
--- a/src/cmd/9l/obj.go
+++ b/src/cmd/link/internal/ppc64/obj.go
@@ -28,18 +28,18 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package ppc64
import (
- "cmd/internal/ld"
"cmd/internal/obj"
+ "cmd/link/internal/ld"
"fmt"
"log"
)
// Reading object files.
-func main() {
+func Main() {
linkarchinit()
ld.Ldmain()
}
@@ -60,10 +60,9 @@ func linkarchinit() {
ld.Thearch.Maxalign = MaxAlign
ld.Thearch.Minlc = MINLC
ld.Thearch.Dwarfregsp = DWARFREGSP
+ ld.Thearch.Dwarfreglr = DWARFREGLR
- ld.Thearch.Adddynlib = adddynlib
ld.Thearch.Adddynrel = adddynrel
- ld.Thearch.Adddynsym = adddynsym
ld.Thearch.Archinit = archinit
ld.Thearch.Archreloc = archreloc
ld.Thearch.Archrelocvariant = archrelocvariant
diff --git a/src/cmd/8l/asm.go b/src/cmd/link/internal/x86/asm.go
similarity index 87%
rename from src/cmd/8l/asm.go
rename to src/cmd/link/internal/x86/asm.go
index 7231379108..d30bd48b4e 100644
--- a/src/cmd/8l/asm.go
+++ b/src/cmd/link/internal/x86/asm.go
@@ -28,33 +28,15 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package x86
import (
- "cmd/internal/ld"
"cmd/internal/obj"
+ "cmd/link/internal/ld"
"fmt"
"log"
)
-func needlib(name string) int {
- if name[0] == '\x00' {
- return 0
- }
-
- /* reuse hash code in symbol table */
- p := fmt.Sprintf(".dynlib.%s", name)
-
- s := ld.Linklookup(ld.Ctxt, p, 0)
-
- if s.Type == 0 {
- s.Type = 100 // avoid SDATA, etc.
- return 1
- }
-
- return 0
-}
-
func gentext() {
}
@@ -202,7 +184,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
break
}
if ld.Iself {
- adddynsym(ld.Ctxt, targ)
+ ld.Adddynsym(ld.Ctxt, targ)
rel := ld.Linklookup(ld.Ctxt, ".rel", 0)
ld.Addaddrplus(ld.Ctxt, rel, s, int64(r.Off))
ld.Adduint32(ld.Ctxt, rel, ld.ELF32_R_INFO(uint32(targ.Dynid), ld.R_386_32))
@@ -222,7 +204,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
// just in case the C code assigns to the variable,
// and of course it only works for single pointers,
// but we only need to support cgo and that's all it needs.
- adddynsym(ld.Ctxt, targ)
+ ld.Adddynsym(ld.Ctxt, targ)
got := ld.Linklookup(ld.Ctxt, ".got", 0)
s.Type = got.Type | obj.SSUB
@@ -294,9 +276,9 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
v = uint32(rs.Dynid)
v |= 1 << 27 // external relocation
} else {
- v = uint32((rs.Sect.(*ld.Section)).Extnum)
+ v = uint32(rs.Sect.Extnum)
if v == 0 {
- ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, (rs.Sect.(*ld.Section)).Name, rs.Type)
+ ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, rs.Sect.Name, rs.Type)
return -1
}
}
@@ -420,7 +402,7 @@ func addpltsym(ctxt *ld.Link, s *ld.LSym) {
return
}
- adddynsym(ctxt, s)
+ ld.Adddynsym(ctxt, s)
if ld.Iself {
plt := ld.Linklookup(ctxt, ".plt", 0)
@@ -480,7 +462,7 @@ func addgotsym(ctxt *ld.Link, s *ld.LSym) {
return
}
- adddynsym(ctxt, s)
+ ld.Adddynsym(ctxt, s)
got := ld.Linklookup(ctxt, ".got", 0)
s.Got = int32(got.Size)
ld.Adduint32(ctxt, got, 0)
@@ -496,76 +478,6 @@ func addgotsym(ctxt *ld.Link, s *ld.LSym) {
}
}
-func adddynsym(ctxt *ld.Link, s *ld.LSym) {
- if s.Dynid >= 0 {
- return
- }
-
- if ld.Iself {
- s.Dynid = int32(ld.Nelfsym)
- ld.Nelfsym++
-
- d := ld.Linklookup(ctxt, ".dynsym", 0)
-
- /* name */
- name := s.Extname
-
- ld.Adduint32(ctxt, d, uint32(ld.Addstring(ld.Linklookup(ctxt, ".dynstr", 0), name)))
-
- /* value */
- if s.Type == obj.SDYNIMPORT {
- ld.Adduint32(ctxt, d, 0)
- } else {
- ld.Addaddr(ctxt, d, s)
- }
-
- /* size */
- ld.Adduint32(ctxt, d, 0)
-
- /* type */
- t := ld.STB_GLOBAL << 4
-
- if s.Cgoexport != 0 && s.Type&obj.SMASK == obj.STEXT {
- t |= ld.STT_FUNC
- } else {
- t |= ld.STT_OBJECT
- }
- ld.Adduint8(ctxt, d, uint8(t))
- ld.Adduint8(ctxt, d, 0)
-
- /* shndx */
- if s.Type == obj.SDYNIMPORT {
- ld.Adduint16(ctxt, d, ld.SHN_UNDEF)
- } else {
- ld.Adduint16(ctxt, d, 1)
- }
- } else if ld.HEADTYPE == obj.Hdarwin {
- ld.Diag("adddynsym: missed symbol %s (%s)", s.Name, s.Extname)
- } else if ld.HEADTYPE == obj.Hwindows {
- } else // already taken care of
- {
- ld.Diag("adddynsym: unsupported binary format")
- }
-}
-
-func adddynlib(lib string) {
- if needlib(lib) == 0 {
- return
- }
-
- if ld.Iself {
- s := ld.Linklookup(ld.Ctxt, ".dynstr", 0)
- if s.Size == 0 {
- ld.Addstring(s, "")
- }
- ld.Elfwritedynent(ld.Linklookup(ld.Ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(s, lib)))
- } else if ld.HEADTYPE == obj.Hdarwin {
- ld.Machoadddynlib(lib)
- } else if ld.HEADTYPE != obj.Hwindows {
- ld.Diag("adddynlib: unsupported binary format")
- }
-}
-
func asmb() {
if ld.Debug['v'] != 0 {
fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
@@ -639,7 +551,7 @@ func asmb() {
symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
case obj.Hdarwin:
- symo = uint32(ld.Segdata.Fileoff + uint64(ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND))) + uint64(machlink))
+ symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
case obj.Hwindows:
symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
diff --git a/src/cmd/8l/l.go b/src/cmd/link/internal/x86/l.go
similarity index 96%
rename from src/cmd/8l/l.go
rename to src/cmd/link/internal/x86/l.go
index 60050857c4..8a811ff0a2 100644
--- a/src/cmd/8l/l.go
+++ b/src/cmd/link/internal/x86/l.go
@@ -28,7 +28,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package x86
const (
thechar = '8'
@@ -40,7 +40,8 @@ const (
MINLC = 1
)
-/* Used by ../ld/dwarf.c */
+/* Used by ../internal/ld/dwarf.go */
const (
DWARFREGSP = 4
+ DWARFREGLR = 8
)
diff --git a/src/cmd/8l/obj.go b/src/cmd/link/internal/x86/obj.go
similarity index 97%
rename from src/cmd/8l/obj.go
rename to src/cmd/link/internal/x86/obj.go
index 7b490ae87c..ee408f70c6 100644
--- a/src/cmd/8l/obj.go
+++ b/src/cmd/link/internal/x86/obj.go
@@ -28,18 +28,18 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-package main
+package x86
import (
- "cmd/internal/ld"
"cmd/internal/obj"
+ "cmd/link/internal/ld"
"fmt"
"log"
)
// Reading object files.
-func main() {
+func Main() {
linkarchinit()
ld.Ldmain()
}
@@ -56,10 +56,9 @@ func linkarchinit() {
ld.Thearch.Maxalign = MaxAlign
ld.Thearch.Minlc = MINLC
ld.Thearch.Dwarfregsp = DWARFREGSP
+ ld.Thearch.Dwarfreglr = DWARFREGLR
- ld.Thearch.Adddynlib = adddynlib
ld.Thearch.Adddynrel = adddynrel
- ld.Thearch.Adddynsym = adddynsym
ld.Thearch.Archinit = archinit
ld.Thearch.Archreloc = archreloc
ld.Thearch.Archrelocvariant = archrelocvariant
diff --git a/src/cmd/link/main.go b/src/cmd/link/main.go
index b23f3f87b0..0e6c34ee0a 100644
--- a/src/cmd/link/main.go
+++ b/src/cmd/link/main.go
@@ -1,9 +1,34 @@
-// Copyright 2014 The Go Authors. All rights reserved.
+// 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.
-// Placeholder to keep build building.
-
package main
-func main() {}
+import (
+ "cmd/internal/obj"
+ "cmd/link/internal/amd64"
+ "cmd/link/internal/arm"
+ "cmd/link/internal/arm64"
+ "cmd/link/internal/ppc64"
+ "cmd/link/internal/x86"
+ "fmt"
+ "os"
+)
+
+func main() {
+ switch obj.Getgoarch() {
+ default:
+ fmt.Fprintf(os.Stderr, "link: unknown architecture %q\n", obj.Getgoarch())
+ os.Exit(2)
+ case "386":
+ x86.Main()
+ case "amd64", "amd64p32":
+ amd64.Main()
+ case "arm":
+ arm.Main()
+ case "arm64":
+ arm64.Main()
+ case "ppc64", "ppc64le":
+ ppc64.Main()
+ }
+}
diff --git a/src/cmd/link/auto.go b/src/cmd/newlink/auto.go
similarity index 100%
rename from src/cmd/link/auto.go
rename to src/cmd/newlink/auto.go
diff --git a/src/cmd/link/auto_test.go b/src/cmd/newlink/auto_test.go
similarity index 100%
rename from src/cmd/link/auto_test.go
rename to src/cmd/newlink/auto_test.go
diff --git a/src/cmd/link/dead.go b/src/cmd/newlink/dead.go
similarity index 100%
rename from src/cmd/link/dead.go
rename to src/cmd/newlink/dead.go
diff --git a/src/cmd/link/dead_test.go b/src/cmd/newlink/dead_test.go
similarity index 100%
rename from src/cmd/link/dead_test.go
rename to src/cmd/newlink/dead_test.go
diff --git a/src/cmd/link/debug.go b/src/cmd/newlink/debug.go
similarity index 100%
rename from src/cmd/link/debug.go
rename to src/cmd/newlink/debug.go
diff --git a/src/cmd/link/hex_test.go b/src/cmd/newlink/hex_test.go
similarity index 100%
rename from src/cmd/link/hex_test.go
rename to src/cmd/newlink/hex_test.go
diff --git a/src/cmd/link/layout.go b/src/cmd/newlink/layout.go
similarity index 100%
rename from src/cmd/link/layout.go
rename to src/cmd/newlink/layout.go
diff --git a/src/cmd/link/layout_test.go b/src/cmd/newlink/layout_test.go
similarity index 100%
rename from src/cmd/link/layout_test.go
rename to src/cmd/newlink/layout_test.go
diff --git a/src/cmd/link/link_test.go b/src/cmd/newlink/link_test.go
similarity index 100%
rename from src/cmd/link/link_test.go
rename to src/cmd/newlink/link_test.go
diff --git a/src/cmd/link/load.go b/src/cmd/newlink/load.go
similarity index 100%
rename from src/cmd/link/load.go
rename to src/cmd/newlink/load.go
diff --git a/src/cmd/link/macho.go b/src/cmd/newlink/macho.go
similarity index 100%
rename from src/cmd/link/macho.go
rename to src/cmd/newlink/macho.go
diff --git a/src/cmd/link/macho_test.go b/src/cmd/newlink/macho_test.go
similarity index 100%
rename from src/cmd/link/macho_test.go
rename to src/cmd/newlink/macho_test.go
diff --git a/src/cmd/newlink/main.go b/src/cmd/newlink/main.go
new file mode 100644
index 0000000000..b23f3f87b0
--- /dev/null
+++ b/src/cmd/newlink/main.go
@@ -0,0 +1,9 @@
+// Copyright 2014 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.
+
+// Placeholder to keep build building.
+
+package main
+
+func main() {}
diff --git a/src/cmd/link/pclntab.go b/src/cmd/newlink/pclntab.go
similarity index 100%
rename from src/cmd/link/pclntab.go
rename to src/cmd/newlink/pclntab.go
diff --git a/src/cmd/link/pclntab_test.go b/src/cmd/newlink/pclntab_test.go
similarity index 100%
rename from src/cmd/link/pclntab_test.go
rename to src/cmd/newlink/pclntab_test.go
diff --git a/src/cmd/link/prog.go b/src/cmd/newlink/prog.go
similarity index 100%
rename from src/cmd/link/prog.go
rename to src/cmd/newlink/prog.go
diff --git a/src/cmd/link/prog_test.go b/src/cmd/newlink/prog_test.go
similarity index 100%
rename from src/cmd/link/prog_test.go
rename to src/cmd/newlink/prog_test.go
diff --git a/src/cmd/link/runtime.go b/src/cmd/newlink/runtime.go
similarity index 100%
rename from src/cmd/link/runtime.go
rename to src/cmd/newlink/runtime.go
diff --git a/src/cmd/link/scan.go b/src/cmd/newlink/scan.go
similarity index 100%
rename from src/cmd/link/scan.go
rename to src/cmd/newlink/scan.go
diff --git a/src/cmd/link/testdata/Makefile b/src/cmd/newlink/testdata/Makefile
similarity index 100%
rename from src/cmd/link/testdata/Makefile
rename to src/cmd/newlink/testdata/Makefile
diff --git a/src/cmd/link/testdata/autosection.6 b/src/cmd/newlink/testdata/autosection.6
similarity index 100%
rename from src/cmd/link/testdata/autosection.6
rename to src/cmd/newlink/testdata/autosection.6
diff --git a/src/cmd/link/testdata/autosection.s b/src/cmd/newlink/testdata/autosection.s
similarity index 100%
rename from src/cmd/link/testdata/autosection.s
rename to src/cmd/newlink/testdata/autosection.s
diff --git a/src/cmd/link/testdata/autoweak.6 b/src/cmd/newlink/testdata/autoweak.6
similarity index 100%
rename from src/cmd/link/testdata/autoweak.6
rename to src/cmd/newlink/testdata/autoweak.6
diff --git a/src/cmd/link/testdata/autoweak.s b/src/cmd/newlink/testdata/autoweak.s
similarity index 100%
rename from src/cmd/link/testdata/autoweak.s
rename to src/cmd/newlink/testdata/autoweak.s
diff --git a/src/cmd/link/testdata/dead.6 b/src/cmd/newlink/testdata/dead.6
similarity index 100%
rename from src/cmd/link/testdata/dead.6
rename to src/cmd/newlink/testdata/dead.6
diff --git a/src/cmd/link/testdata/dead.s b/src/cmd/newlink/testdata/dead.s
similarity index 100%
rename from src/cmd/link/testdata/dead.s
rename to src/cmd/newlink/testdata/dead.s
diff --git a/src/cmd/link/testdata/genpcln.go b/src/cmd/newlink/testdata/genpcln.go
similarity index 100%
rename from src/cmd/link/testdata/genpcln.go
rename to src/cmd/newlink/testdata/genpcln.go
diff --git a/src/cmd/link/testdata/hello.6 b/src/cmd/newlink/testdata/hello.6
similarity index 100%
rename from src/cmd/link/testdata/hello.6
rename to src/cmd/newlink/testdata/hello.6
diff --git a/src/cmd/link/testdata/hello.s b/src/cmd/newlink/testdata/hello.s
similarity index 100%
rename from src/cmd/link/testdata/hello.s
rename to src/cmd/newlink/testdata/hello.s
diff --git a/src/cmd/link/testdata/layout.6 b/src/cmd/newlink/testdata/layout.6
similarity index 100%
rename from src/cmd/link/testdata/layout.6
rename to src/cmd/newlink/testdata/layout.6
diff --git a/src/cmd/link/testdata/layout.s b/src/cmd/newlink/testdata/layout.s
similarity index 100%
rename from src/cmd/link/testdata/layout.s
rename to src/cmd/newlink/testdata/layout.s
diff --git a/src/cmd/link/testdata/link.hello.darwin.amd64 b/src/cmd/newlink/testdata/link.hello.darwin.amd64
similarity index 100%
rename from src/cmd/link/testdata/link.hello.darwin.amd64
rename to src/cmd/newlink/testdata/link.hello.darwin.amd64
diff --git a/src/cmd/link/testdata/macho.amd64.exit9 b/src/cmd/newlink/testdata/macho.amd64.exit9
similarity index 100%
rename from src/cmd/link/testdata/macho.amd64.exit9
rename to src/cmd/newlink/testdata/macho.amd64.exit9
diff --git a/src/cmd/link/testdata/macho.amd64.hello b/src/cmd/newlink/testdata/macho.amd64.hello
similarity index 100%
rename from src/cmd/link/testdata/macho.amd64.hello
rename to src/cmd/newlink/testdata/macho.amd64.hello
diff --git a/src/cmd/link/testdata/macho.amd64.helloro b/src/cmd/newlink/testdata/macho.amd64.helloro
similarity index 100%
rename from src/cmd/link/testdata/macho.amd64.helloro
rename to src/cmd/newlink/testdata/macho.amd64.helloro
diff --git a/src/cmd/link/testdata/pclntab.6 b/src/cmd/newlink/testdata/pclntab.6
similarity index 100%
rename from src/cmd/link/testdata/pclntab.6
rename to src/cmd/newlink/testdata/pclntab.6
diff --git a/src/cmd/link/testdata/pclntab.s b/src/cmd/newlink/testdata/pclntab.s
similarity index 100%
rename from src/cmd/link/testdata/pclntab.s
rename to src/cmd/newlink/testdata/pclntab.s
diff --git a/src/cmd/link/util.go b/src/cmd/newlink/util.go
similarity index 100%
rename from src/cmd/link/util.go
rename to src/cmd/newlink/util.go
diff --git a/src/cmd/link/write.go b/src/cmd/newlink/write.go
similarity index 100%
rename from src/cmd/link/write.go
rename to src/cmd/newlink/write.go
diff --git a/src/cmd/pack/pack_test.go b/src/cmd/pack/pack_test.go
index 9c33f4f98b..cd32020501 100644
--- a/src/cmd/pack/pack_test.go
+++ b/src/cmd/pack/pack_test.go
@@ -13,7 +13,6 @@ import (
"os"
"os/exec"
"path/filepath"
- "regexp"
"runtime"
"testing"
"time"
@@ -223,16 +222,14 @@ func TestHello(t *testing.T) {
t.Fatal(err)
}
- char := findChar(t, dir)
-
run := func(args ...string) string {
return doRun(t, dir, args...)
}
run("go", "build", "cmd/pack") // writes pack binary to dir
- run("go", "tool", char+"g", "hello.go")
- run("./pack", "grc", "hello.a", "hello."+char)
- run("go", "tool", char+"l", "-o", "a.out", "hello.a")
+ run("go", "tool", "compile", "hello.go")
+ run("./pack", "grc", "hello.a", "hello.o")
+ run("go", "tool", "link", "-o", "a.out", "hello.a")
out := run("./a.out")
if out != "hello world\n" {
t.Fatalf("incorrect output: %q, want %q", out, "hello world\n")
@@ -297,17 +294,15 @@ func TestLargeDefs(t *testing.T) {
t.Fatal(err)
}
- char := findChar(t, dir)
-
run := func(args ...string) string {
return doRun(t, dir, args...)
}
run("go", "build", "cmd/pack") // writes pack binary to dir
- run("go", "tool", char+"g", "large.go")
- run("./pack", "grc", "large.a", "large."+char)
- run("go", "tool", char+"g", "-I", ".", "main.go")
- run("go", "tool", char+"l", "-L", ".", "-o", "a.out", "main."+char)
+ run("go", "tool", "compile", "large.go")
+ run("./pack", "grc", "large.a", "large.o")
+ run("go", "tool", "compile", "-I", ".", "main.go")
+ run("go", "tool", "link", "-L", ".", "-o", "a.out", "main.o")
out := run("./a.out")
if out != "ok\n" {
t.Fatalf("incorrect output: %q, want %q", out, "ok\n")
@@ -325,20 +320,6 @@ func doRun(t *testing.T, dir string, args ...string) string {
return string(out)
}
-// findChar returns the architecture character for the go command.
-func findChar(t *testing.T, dir string) string {
- out := doRun(t, dir, "go", "env")
- re, err := regexp.Compile(`\s*GOCHAR=['"]?(\w)['"]?`)
- if err != nil {
- t.Fatal(err)
- }
- fields := re.FindStringSubmatch(out)
- if fields == nil {
- t.Fatal("cannot find GOCHAR in 'go env' output:\n", out)
- }
- return fields[1]
-}
-
// Fake implementation of files.
var helloFile = &FakeFile{
diff --git a/src/cmd/pprof/internal/profile/legacy_profile.go b/src/cmd/pprof/internal/profile/legacy_profile.go
index bfc8110e45..e4c92cdd19 100644
--- a/src/cmd/pprof/internal/profile/legacy_profile.go
+++ b/src/cmd/pprof/internal/profile/legacy_profile.go
@@ -554,9 +554,10 @@ func parseHeap(b []byte) (p *Profile, err error) {
}
}
- if l = strings.TrimSpace(l); l == "" {
+ if isSpaceOrComment(l) {
continue
}
+ l = strings.TrimSpace(l)
if sectionTrigger(l) != unrecognizedSection {
break
diff --git a/src/cmd/yacc/yacc.go b/src/cmd/yacc/yacc.go
index 5c7b0b71b1..53c0fab174 100644
--- a/src/cmd/yacc/yacc.go
+++ b/src/cmd/yacc/yacc.go
@@ -128,6 +128,7 @@ const (
TYPEDEF
TYPENAME
UNION
+ ERROR
)
const ENDFILE = 0
@@ -325,8 +326,24 @@ var resrv = []Resrv{
{"type", TYPEDEF},
{"union", UNION},
{"struct", UNION},
+ {"error", ERROR},
}
+type Error struct {
+ lineno int
+ tokens []string
+ msg string
+}
+
+var errors []Error
+
+type Row struct {
+ actions []int
+ defaultAction int
+}
+
+var stateTable []Row
+
var zznewstate = 0
const EOF = -1
@@ -402,6 +419,27 @@ outer:
}
start = chfind(1, tokname)
+ case ERROR:
+ lno := lineno
+ var tokens []string
+ for {
+ t := gettok()
+ if t == ':' {
+ break
+ }
+ if t != IDENTIFIER && t != IDENTCOLON {
+ errorf("bad syntax in %%error")
+ }
+ tokens = append(tokens, tokname)
+ if t == IDENTCOLON {
+ break
+ }
+ }
+ if gettok() != IDENTIFIER {
+ errorf("bad syntax in %%error")
+ }
+ errors = append(errors, Error{lno, tokens, tokname})
+
case TYPEDEF:
t = gettok()
if t != TYPENAME {
@@ -2155,6 +2193,10 @@ func output() {
}
fmt.Fprintf(ftable, "\nvar %sExca = [...]int{\n", prefix)
+ if len(errors) > 0 {
+ stateTable = make([]Row, nstate)
+ }
+
noset := mkset()
// output the stuff for state i
@@ -2368,6 +2410,15 @@ func wrstate(i int) {
var j0, j1, u int
var pp, qq int
+ if len(errors) > 0 {
+ actions := append([]int(nil), temp1...)
+ defaultAction := ERRCODE
+ if lastred != 0 {
+ defaultAction = -lastred
+ }
+ stateTable[i] = Row{actions, defaultAction}
+ }
+
if foutput == nil {
return
}
@@ -2914,6 +2965,20 @@ func others() {
}
fmt.Fprintf(ftable, "%d,\n}\n", 0)
+ // Custom error messages.
+ fmt.Fprintf(ftable, "\n")
+ fmt.Fprintf(ftable, "var %sErrorMessages = [...]struct {\n", prefix)
+ fmt.Fprintf(ftable, "\tstate int\n")
+ fmt.Fprintf(ftable, "\ttoken int\n")
+ fmt.Fprintf(ftable, "\tmsg string\n")
+ fmt.Fprintf(ftable, "}{\n")
+ for _, error := range errors {
+ lineno = error.lineno
+ state, token := runMachine(error.tokens)
+ fmt.Fprintf(ftable, "\t{%v, %v, %s},\n", state, token, error.msg)
+ }
+ fmt.Fprintf(ftable, "}\n")
+
// copy parser text
ch := getrune(finput)
for ch != EOF {
@@ -2932,6 +2997,59 @@ func others() {
fmt.Fprintf(ftable, "%v", parts[1])
}
+func runMachine(tokens []string) (state, token int) {
+ var stack []int
+ i := 0
+ token = -1
+
+Loop:
+ if token < 0 {
+ token = chfind(2, tokens[i])
+ i++
+ }
+
+ row := stateTable[state]
+
+ c := token
+ if token >= NTBASE {
+ c = token - NTBASE + ntokens
+ }
+ action := row.actions[c]
+ if action == 0 {
+ action = row.defaultAction
+ }
+
+ switch {
+ case action == ACCEPTCODE:
+ errorf("tokens are accepted")
+ return
+ case action == ERRCODE:
+ if token >= NTBASE {
+ errorf("error at non-terminal token %s", symnam(token))
+ }
+ return
+ case action > 0:
+ // Shift to state action.
+ stack = append(stack, state)
+ state = action
+ token = -1
+ goto Loop
+ default:
+ // Reduce by production -action.
+ prod := prdptr[-action]
+ if rhsLen := len(prod) - 2; rhsLen > 0 {
+ n := len(stack) - rhsLen
+ state = stack[n]
+ stack = stack[:n]
+ }
+ if token >= 0 {
+ i--
+ }
+ token = prod[0]
+ goto Loop
+ }
+}
+
func arout(s string, v []int, n int) {
s = prefix + s
fmt.Fprintf(ftable, "var %v = [...]int{\n", s)
@@ -3212,7 +3330,6 @@ type $$Parser interface {
type $$ParserImpl struct {
lookahead func() int
- state func() int
}
func (p *$$ParserImpl) Lookahead() int {
@@ -3222,7 +3339,6 @@ func (p *$$ParserImpl) Lookahead() int {
func $$NewParser() $$Parser {
p := &$$ParserImpl{
lookahead: func() int { return -1 },
- state: func() int { return -1 },
}
return p
}
@@ -3253,6 +3369,13 @@ func $$ErrorMessage(state, lookAhead int) string {
if !$$ErrorVerbose {
return "syntax error"
}
+
+ for _, e := range $$ErrorMessages {
+ if e.state == state && e.token == lookAhead {
+ return "syntax error: " + e.msg
+ }
+ }
+
res := "syntax error: unexpected " + $$Tokname(lookAhead)
// To match Bison, suggest at most four expected tokens.
@@ -3355,7 +3478,6 @@ func ($$rcvr *$$ParserImpl) Parse($$lex $$Lexer) int {
$$state := 0
$$char := -1
$$token := -1 // $$char translated into internal numbering
- $$rcvr.state = func() int { return $$state }
$$rcvr.lookahead = func() int { return $$char }
defer func() {
// Make sure we report no lookahead when not parsing.
diff --git a/src/debug/gosym/pclntab_test.go b/src/debug/gosym/pclntab_test.go
index 6855a65bbe..c6943a631a 100644
--- a/src/debug/gosym/pclntab_test.go
+++ b/src/debug/gosym/pclntab_test.go
@@ -49,7 +49,7 @@ func dotest(self bool) bool {
// the resulting binary looks like it was built from pclinetest.s,
// but we have renamed it to keep it away from the go tool.
pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
- command := fmt.Sprintf("go tool asm -o %s.6 pclinetest.asm && go tool 6l -H linux -E main -o %s %s.6",
+ command := fmt.Sprintf("go tool asm -o %s.o pclinetest.asm && go tool link -H linux -E main -o %s %s.o",
pclinetestBinary, pclinetestBinary, pclinetestBinary)
cmd := exec.Command("sh", "-c", command)
cmd.Stdout = os.Stdout
diff --git a/src/encoding/gob/doc.go b/src/encoding/gob/doc.go
index d0acaba1ad..31223b6d43 100644
--- a/src/encoding/gob/doc.go
+++ b/src/encoding/gob/doc.go
@@ -6,7 +6,7 @@
Package gob manages streams of gobs - binary values exchanged between an
Encoder (transmitter) and a Decoder (receiver). A typical use is transporting
arguments and results of remote procedure calls (RPCs) such as those provided by
-package "rpc".
+package "net/rpc".
The implementation compiles a custom codec for each data type in the stream and
is most efficient when a single Encoder is used to transmit a stream of values,
@@ -83,7 +83,7 @@ allocated. Regardless, the length of the resulting slice reports the number of
elements decoded.
Functions and channels will not be sent in a gob. Attempting to encode such a value
-at top the level will fail. A struct field of chan or func type is treated exactly
+at the top level will fail. A struct field of chan or func type is treated exactly
like an unexported field and is ignored.
Gob can encode a value of any type implementing the GobEncoder or
@@ -111,11 +111,11 @@ A signed integer, i, is encoded within an unsigned integer, u. Within u, bits 1
upward contain the value; bit 0 says whether they should be complemented upon
receipt. The encode algorithm looks like this:
- uint u;
+ var u uint
if i < 0 {
- u = (^i << 1) | 1 // complement i, bit 0 is 1
+ u = (^uint(i) << 1) | 1 // complement i, bit 0 is 1
} else {
- u = (i << 1) // do not complement i, bit 0 is 0
+ u = (uint(i) << 1) // do not complement i, bit 0 is 0
}
encodeUnsigned(u)
@@ -137,9 +137,9 @@ All other slices and arrays are sent as an unsigned count followed by that many
elements using the standard gob encoding for their type, recursively.
Maps are sent as an unsigned count followed by that many key, element
-pairs. Empty but non-nil maps are sent, so if the sender has allocated
-a map, the receiver will allocate a map even if no elements are
-transmitted.
+pairs. Empty but non-nil maps are sent, so if the receiver has not allocated
+one already, one will always be allocated on receipt unless the transmitted map
+is nil and not at the top level.
Structs are sent as a sequence of (field number, field value) pairs. The field
value is sent using the standard gob encoding for its type, recursively. If a
@@ -246,7 +246,7 @@ where * signifies zero or more repetitions and the type id of a value must
be predefined or be defined before the value in the stream.
See "Gobs of data" for a design discussion of the gob wire format:
-http://golang.org/doc/articles/gobs_of_data.html
+http://blog.golang.org/gobs-of-data
*/
package gob
diff --git a/src/encoding/gob/encoder.go b/src/encoding/gob/encoder.go
index a340e47b5e..62d0f42e81 100644
--- a/src/encoding/gob/encoder.go
+++ b/src/encoding/gob/encoder.go
@@ -5,6 +5,7 @@
package gob
import (
+ "errors"
"io"
"reflect"
"sync"
@@ -65,6 +66,11 @@ func (enc *Encoder) writeMessage(w io.Writer, b *encBuffer) {
// it by hand.
message := b.Bytes()
messageLen := len(message) - maxLength
+ // Length cannot be bigger than the decoder can handle.
+ if messageLen >= tooBig {
+ enc.setError(errors.New("gob: encoder: message too big"))
+ return
+ }
// Encode the length.
enc.countState.b.Reset()
enc.countState.encodeUint(uint64(messageLen))
diff --git a/src/encoding/gob/encoder_test.go b/src/encoding/gob/encoder_test.go
index c0bd379c93..8a72a3118c 100644
--- a/src/encoding/gob/encoder_test.go
+++ b/src/encoding/gob/encoder_test.go
@@ -976,3 +976,21 @@ func TestBadData(t *testing.T) {
}
}
}
+
+// TestHugeWriteFails tests that enormous messages trigger an error.
+func TestHugeWriteFails(t *testing.T) {
+ if testing.Short() {
+ // Requires allocating a monster, so don't do this from all.bash.
+ t.Skip("skipping huge allocation in short mode")
+ }
+ huge := make([]byte, tooBig)
+ huge[0] = 7 // Make sure it's not all zeros.
+ buf := new(bytes.Buffer)
+ err := NewEncoder(buf).Encode(huge)
+ if err == nil {
+ t.Fatalf("expected error for huge slice")
+ }
+ if !strings.Contains(err.Error(), "message too big") {
+ t.Fatalf("expected 'too big' error; got %s\n", err.Error())
+ }
+}
diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go
index f26a7d49f0..613641afbb 100644
--- a/src/encoding/json/decode.go
+++ b/src/encoding/json/decode.go
@@ -739,7 +739,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
default:
d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
case reflect.Slice:
- if v.Type() != byteSliceType {
+ if v.Type().Elem().Kind() != reflect.Uint8 {
d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
break
}
diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go
index 7ecc8f4402..f208ee8a7c 100644
--- a/src/encoding/json/decode_test.go
+++ b/src/encoding/json/decode_test.go
@@ -1207,7 +1207,28 @@ func TestStringKind(t *testing.T) {
if !reflect.DeepEqual(m1, m2) {
t.Error("Items should be equal after encoding and then decoding")
}
+}
+// Custom types with []byte as underlying type could not be marshalled
+// and then unmarshalled.
+// Issue 8962.
+func TestByteKind(t *testing.T) {
+ type byteKind []byte
+
+ a := byteKind("hello")
+
+ data, err := Marshal(a)
+ if err != nil {
+ t.Error(err)
+ }
+ var b byteKind
+ err = Unmarshal(data, &b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(a, b) {
+ t.Errorf("expected %v == %v", a, b)
+ }
}
var decodeTypeErrorTests = []struct {
diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go
index 4db9f35e69..7789bb5141 100644
--- a/src/encoding/json/encode.go
+++ b/src/encoding/json/encode.go
@@ -275,8 +275,6 @@ func (e *encodeState) error(err error) {
panic(err)
}
-var byteSliceType = reflect.TypeOf([]byte(nil))
-
func isEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
diff --git a/src/encoding/json/scanner_test.go b/src/encoding/json/scanner_test.go
index 7880342902..66383ef0ef 100644
--- a/src/encoding/json/scanner_test.go
+++ b/src/encoding/json/scanner_test.go
@@ -209,6 +209,7 @@ var benchScan scanner
func BenchmarkSkipValue(b *testing.B) {
initBig()
+ b.ResetTimer()
for i := 0; i < b.N; i++ {
nextValue(jsonBig, &benchScan)
}
diff --git a/src/flag/flag.go b/src/flag/flag.go
index 4e4279069f..060660248e 100644
--- a/src/flag/flag.go
+++ b/src/flag/flag.go
@@ -31,7 +31,7 @@
fmt.Println("ip has value ", *ip)
fmt.Println("flagvar has value ", flagvar)
- After parsing, the arguments after the flag are available as the
+ After parsing, the arguments following the flags are available as the
slice flag.Args() or individually as flag.Arg(i).
The arguments are indexed from 0 through flag.NArg()-1.
diff --git a/src/fmt/fmt_test.go b/src/fmt/fmt_test.go
index ba99cb0f6a..93121bb3d0 100644
--- a/src/fmt/fmt_test.go
+++ b/src/fmt/fmt_test.go
@@ -557,10 +557,13 @@ var fmtTests = []struct {
{"%0.100f", 1.0, zeroFill("1.", 100, "")},
{"%0.100f", -1.0, zeroFill("-1.", 100, "")},
- // Used to panic: integer function didn't look at f.prec or f.unicode.
+ // Used to panic: integer function didn't look at f.prec or f.unicode or f.width.
{"%#.80x", 42, "0x0000000000000000000000000000000000000000000000000000000000000000000000000000002a"},
{"%.80U", 42, "U+0000000000000000000000000000000000000000000000000000000000000000000000000000002A"},
{"%#.80U", 'æ—¥', "U+000000000000000000000000000000000000000000000000000000000000000000000000000065E5 'æ—¥'"},
+ {"%+.65d", 44, "+00000000000000000000000000000000000000000000000000000000000000044"},
+ {"% .65d", 44, " 00000000000000000000000000000000000000000000000000000000000000044"},
+ {"% +.65d", 44, "+00000000000000000000000000000000000000000000000000000000000000044"},
// Comparison of padding rules with C printf.
/*
@@ -949,11 +952,13 @@ var mallocTest = []struct {
var _ bytes.Buffer
func TestCountMallocs(t *testing.T) {
- if testing.Short() {
+ switch {
+ case testing.Short():
t.Skip("skipping malloc count in short mode")
- }
- if runtime.GOMAXPROCS(0) > 1 {
+ case runtime.GOMAXPROCS(0) > 1:
t.Skip("skipping; GOMAXPROCS>1")
+ case raceenabled:
+ t.Skip("skipping malloc count under race detector")
}
for _, mt := range mallocTest {
mallocs := testing.AllocsPerRun(100, mt.fn)
diff --git a/src/fmt/format.go b/src/fmt/format.go
index 099f8a5e00..ba984cf84f 100644
--- a/src/fmt/format.go
+++ b/src/fmt/format.go
@@ -163,7 +163,7 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) {
}
var buf []byte = f.intbuf[0:]
- if f.widPresent || f.precPresent {
+ if f.widPresent || f.precPresent || f.plus || f.space {
width := f.wid + f.prec // Only one will be set, both are positive; this provides the maximum.
if base == 16 && f.sharp {
// Also adds "0x".
@@ -177,6 +177,11 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) {
width += 1 + 1 + utf8.UTFMax + 1
}
}
+ if f.plus {
+ width++
+ } else if f.space {
+ width++
+ }
if width > nByte {
// We're going to need a bigger boat.
buf = make([]byte, width)
diff --git a/src/fmt/norace_test.go b/src/fmt/norace_test.go
new file mode 100644
index 0000000000..1267cc34ee
--- /dev/null
+++ b/src/fmt/norace_test.go
@@ -0,0 +1,9 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !race
+
+package fmt_test
+
+const raceenabled = false
diff --git a/src/fmt/race_test.go b/src/fmt/race_test.go
new file mode 100644
index 0000000000..ae3147a5b0
--- /dev/null
+++ b/src/fmt/race_test.go
@@ -0,0 +1,9 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build race
+
+package fmt_test
+
+const raceenabled = true
diff --git a/src/go/build/build.go b/src/go/build/build.go
index 124da40d3b..db6bdcf923 100644
--- a/src/go/build/build.go
+++ b/src/go/build/build.go
@@ -296,11 +296,7 @@ func defaultContext() Context {
// in all releases >= Go 1.x. Code that requires Go 1.x or later should
// say "+build go1.x", and code that should only be built before Go 1.x
// (perhaps it is the stub to use in that case) should say "+build !go1.x".
- //
- // When we reach Go 1.5 the line will read
- // c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4", "go1.5"}
- // and so on.
- c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4"}
+ c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4", "go1.5"}
switch os.Getenv("CGO_ENABLED") {
case "1":
@@ -1328,7 +1324,7 @@ func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
// build tag "linux" in that file. For Go 1.4 and beyond, we require this
// auto-tagging to apply only to files with a non-empty prefix, so
// "foo_linux.go" is tagged but "linux.go" is not. This allows new operating
- // sytems, such as android, to arrive without breaking existing code with
+ // systems, such as android, to arrive without breaking existing code with
// innocuous source code in "android.go". The easiest fix: cut everything
// in the name before the initial _.
i := strings.Index(name, "_")
@@ -1395,20 +1391,11 @@ func IsLocalImport(path string) bool {
strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
}
-// ArchChar returns the architecture character for the given goarch.
-// For example, ArchChar("amd64") returns "6".
+// ArchChar returns "?" and an error.
+// In earlier versions of Go, the returned string was used to derive
+// the compiler and linker tool names, the default object file suffix,
+// and the default linker output name. As of Go 1.5, those strings
+// no longer vary by architecture; they are compile, link, .o, and a.out, respectively.
func ArchChar(goarch string) (string, error) {
- switch goarch {
- case "386":
- return "8", nil
- case "amd64", "amd64p32":
- return "6", nil
- case "arm":
- return "5", nil
- case "arm64":
- return "7", nil
- case "ppc64", "ppc64le":
- return "9", nil
- }
- return "", errors.New("unsupported GOARCH " + goarch)
+ return "?", errors.New("architecture letter no longer used")
}
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index 84c1e2ab31..8e985aa05b 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -184,42 +184,43 @@ var pkgDeps = map[string][]string{
},
// One of a kind.
- "archive/tar": {"L4", "OS", "syscall"},
- "archive/zip": {"L4", "OS", "compress/flate"},
- "compress/bzip2": {"L4"},
- "compress/flate": {"L4"},
- "compress/gzip": {"L4", "compress/flate"},
- "compress/lzw": {"L4"},
- "compress/zlib": {"L4", "compress/flate"},
- "database/sql": {"L4", "container/list", "database/sql/driver"},
- "database/sql/driver": {"L4", "time"},
- "debug/dwarf": {"L4"},
- "debug/elf": {"L4", "OS", "debug/dwarf"},
- "debug/gosym": {"L4"},
- "debug/macho": {"L4", "OS", "debug/dwarf"},
- "debug/pe": {"L4", "OS", "debug/dwarf"},
- "encoding": {"L4"},
- "encoding/ascii85": {"L4"},
- "encoding/asn1": {"L4", "math/big"},
- "encoding/csv": {"L4"},
- "encoding/gob": {"L4", "OS", "encoding"},
- "encoding/hex": {"L4"},
- "encoding/json": {"L4", "encoding"},
- "encoding/pem": {"L4"},
- "encoding/xml": {"L4", "encoding"},
- "flag": {"L4", "OS"},
- "go/build": {"L4", "OS", "GOPARSER"},
- "html": {"L4"},
- "image/draw": {"L4", "image/internal/imageutil"},
- "image/gif": {"L4", "compress/lzw", "image/color/palette", "image/draw"},
- "image/jpeg": {"L4", "image/internal/imageutil"},
- "image/png": {"L4", "compress/zlib"},
- "index/suffixarray": {"L4", "regexp"},
- "math/big": {"L4"},
- "mime": {"L4", "OS", "syscall", "internal/syscall/windows/registry"},
- "net/url": {"L4"},
- "text/scanner": {"L4", "OS"},
- "text/template/parse": {"L4"},
+ "archive/tar": {"L4", "OS", "syscall"},
+ "archive/zip": {"L4", "OS", "compress/flate"},
+ "compress/bzip2": {"L4"},
+ "compress/flate": {"L4"},
+ "compress/gzip": {"L4", "compress/flate"},
+ "compress/lzw": {"L4"},
+ "compress/zlib": {"L4", "compress/flate"},
+ "database/sql": {"L4", "container/list", "database/sql/driver"},
+ "database/sql/driver": {"L4", "time"},
+ "debug/dwarf": {"L4"},
+ "debug/elf": {"L4", "OS", "debug/dwarf"},
+ "debug/gosym": {"L4"},
+ "debug/macho": {"L4", "OS", "debug/dwarf"},
+ "debug/pe": {"L4", "OS", "debug/dwarf"},
+ "encoding": {"L4"},
+ "encoding/ascii85": {"L4"},
+ "encoding/asn1": {"L4", "math/big"},
+ "encoding/csv": {"L4"},
+ "encoding/gob": {"L4", "OS", "encoding"},
+ "encoding/hex": {"L4"},
+ "encoding/json": {"L4", "encoding"},
+ "encoding/pem": {"L4"},
+ "encoding/xml": {"L4", "encoding"},
+ "flag": {"L4", "OS"},
+ "go/build": {"L4", "OS", "GOPARSER"},
+ "html": {"L4"},
+ "image/draw": {"L4", "image/internal/imageutil"},
+ "image/gif": {"L4", "compress/lzw", "image/color/palette", "image/draw"},
+ "image/jpeg": {"L4", "image/internal/imageutil"},
+ "image/png": {"L4", "compress/zlib"},
+ "index/suffixarray": {"L4", "regexp"},
+ "math/big": {"L4"},
+ "mime": {"L4", "OS", "syscall", "internal/syscall/windows/registry"},
+ "mime/quotedprintable": {"L4"},
+ "net/url": {"L4"},
+ "text/scanner": {"L4", "OS"},
+ "text/template/parse": {"L4"},
"html/template": {
"L4", "OS", "encoding/json", "html", "text/template",
@@ -243,7 +244,7 @@ var pkgDeps = map[string][]string{
// Basic networking.
// Because net must be used by any package that wants to
// do networking portably, it must have a small dependency set: just L1+basic os.
- "net": {"L1", "CGO", "os", "syscall", "time", "internal/syscall/windows", "internal/singleflight"},
+ "net": {"L1", "CGO", "os", "syscall", "time", "internal/syscall/unix", "internal/syscall/windows", "internal/singleflight"},
// NET enables use of basic network-related packages.
"NET": {
@@ -255,7 +256,7 @@ var pkgDeps = map[string][]string{
// Uses of networking.
"log/syslog": {"L4", "OS", "net"},
- "net/mail": {"L4", "NET", "OS", "internal/mime"},
+ "net/mail": {"L4", "NET", "OS", "mime"},
"net/textproto": {"L4", "OS", "net"},
// Core crypto.
@@ -340,20 +341,18 @@ var pkgDeps = map[string][]string{
// dependencies. Do not simply update them in situ.
"container/heap": {"sort"},
"debug/plan9obj": {"encoding/binary", "errors", "fmt", "io", "os"},
- "go/constants": {"fmt", "go/token", "math/big", "strconv"},
+ "go/constant": {"fmt", "go/token", "math/big", "strconv"},
"go/format": {"bytes", "fmt", "go/ast", "go/parser", "go/printer", "go/token", "internal/format", "io"},
"go/importer": {"go/internal/gcimporter", "go/types", "io", "runtime"},
- "go/internal/gcimporter": {"bufio", "errors", "fmt", "go/build", "go/constants", "go/token", "go/types", "io", "os", "path/filepath", "strconv", "strings", "text/scanner"},
- "go/types": {"bytes", "container/heap", "fmt", "go/ast", "go/constants", "go/parser", "go/token", "io", "math", "path", "sort", "strconv", "strings", "sync", "unicode"},
+ "go/internal/gcimporter": {"bufio", "errors", "fmt", "go/build", "go/constant", "go/token", "go/types", "io", "os", "path/filepath", "strconv", "strings", "text/scanner"},
+ "go/types": {"bytes", "container/heap", "fmt", "go/ast", "go/constant", "go/parser", "go/token", "io", "math", "path", "sort", "strconv", "strings", "sync", "unicode"},
"image/internal/imageutil": {"image"},
"internal/format": {"bytes", "go/ast", "go/parser", "go/printer", "go/token", "strings"},
- "internal/mime": {"bytes", "encoding/base64", "errors", "fmt", "io", "io/ioutil", "strconv", "strings", "unicode"},
"internal/singleflight": {"sync"},
"internal/syscall/unix": {"runtime", "sync/atomic", "syscall", "unsafe"},
"internal/syscall/windows": {"syscall", "unsafe"},
"internal/syscall/windows/registry": {"errors", "io", "syscall", "unicode/utf16", "unsafe"},
"internal/trace": {"bufio", "bytes", "fmt", "io", "os", "os/exec", "sort", "strconv", "strings"},
- "mime/quotedprintable": {"bufio", "bytes", "fmt", "io"},
"net/http/cookiejar": {"errors", "fmt", "net", "net/http", "net/url", "sort", "strings", "sync", "time", "unicode/utf8"},
"net/http/internal": {"bufio", "bytes", "errors", "fmt", "io"},
"net/internal/socktest": {"fmt", "sync", "syscall"},
diff --git a/src/go/build/doc.go b/src/go/build/doc.go
index 78e17b220a..233f8b989d 100644
--- a/src/go/build/doc.go
+++ b/src/go/build/doc.go
@@ -101,6 +101,7 @@
// - "go1.2", from Go version 1.2 onward
// - "go1.3", from Go version 1.3 onward
// - "go1.4", from Go version 1.4 onward
+// - "go1.5", from Go version 1.5 onward
// - any additional words listed in ctxt.BuildTags
//
// If a file's name, after stripping the extension and a possible _test suffix,
diff --git a/src/go/constants/go13.go b/src/go/constant/go13.go
similarity index 96%
rename from src/go/constants/go13.go
rename to src/go/constant/go13.go
index f445b82154..a4a838a290 100644
--- a/src/go/constants/go13.go
+++ b/src/go/constant/go13.go
@@ -4,7 +4,7 @@
// +build !go1.4
-package constants
+package constant
import (
"math"
diff --git a/src/go/constants/go14.go b/src/go/constant/go14.go
similarity index 93%
rename from src/go/constants/go14.go
rename to src/go/constant/go14.go
index c698fa6de9..2ab6da02f6 100644
--- a/src/go/constants/go14.go
+++ b/src/go/constant/go14.go
@@ -4,7 +4,7 @@
// +build go1.4
-package constants
+package constant
import "math/big"
diff --git a/src/go/constants/value.go b/src/go/constant/value.go
similarity index 99%
rename from src/go/constants/value.go
rename to src/go/constant/value.go
index ad4533c900..79a80af1ab 100644
--- a/src/go/constants/value.go
+++ b/src/go/constant/value.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package constants implements Values representing untyped
+// Package constant implements Values representing untyped
// Go constants and the corresponding operations. Values
// and operations may have arbitrary or unlimited precision.
//
@@ -11,7 +11,7 @@
// values produce unknown values unless specified
// otherwise.
//
-package constants // import "go/constants"
+package constant // import "go/constant"
import (
"fmt"
diff --git a/src/go/constants/value_test.go b/src/go/constant/value_test.go
similarity index 99%
rename from src/go/constants/value_test.go
rename to src/go/constant/value_test.go
index 6a74e2d13c..08cdd5e625 100644
--- a/src/go/constants/value_test.go
+++ b/src/go/constant/value_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package constants
+package constant
import (
"go/token"
diff --git a/src/go/internal/gcimporter/gcimporter.go b/src/go/internal/gcimporter/gcimporter.go
index ee83a725fa..7278c0c0a0 100644
--- a/src/go/internal/gcimporter/gcimporter.go
+++ b/src/go/internal/gcimporter/gcimporter.go
@@ -18,14 +18,14 @@ import (
"strings"
"text/scanner"
- exact "go/constants"
+ exact "go/constant"
"go/types"
)
// debugging/development support
const debug = false
-var pkgExts = [...]string{".a", ".5", ".6", ".7", ".8", ".9"}
+var pkgExts = [...]string{".a", ".o"}
// FindPkg returns the filename and unique package id for an import
// path based on package information provided by build.Import (using
diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go
index 5d4de39712..fe4a758cd4 100644
--- a/src/go/internal/gcimporter/gcimporter_test.go
+++ b/src/go/internal/gcimporter/gcimporter_test.go
@@ -50,9 +50,8 @@ func compile(t *testing.T, dirname, filename string) string {
t.Logf("%s", out)
t.Fatalf("%s %s failed: %s", gcPath, filename, err)
}
- archCh, _ := build.ArchChar(runtime.GOARCH)
// filename should end with ".go"
- return filepath.Join(dirname, filename[:len(filename)-2]+archCh)
+ return filepath.Join(dirname, filename[:len(filename)-2]+"o")
}
// Use the same global imports map for all tests. The effect is
diff --git a/src/go/parser/interface.go b/src/go/parser/interface.go
index 49103058b5..f3bc4b9cc8 100644
--- a/src/go/parser/interface.go
+++ b/src/go/parser/interface.go
@@ -91,7 +91,10 @@ func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode)
var p parser
defer func() {
if e := recover(); e != nil {
- _ = e.(bailout) // re-panics if it's not a bailout
+ // resume same panic if it's not a bailout
+ if _, ok := e.(bailout); !ok {
+ panic(e)
+ }
}
// set result values
diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go
index 0095d7facf..18278ba4b7 100644
--- a/src/go/parser/parser.go
+++ b/src/go/parser/parser.go
@@ -412,14 +412,17 @@ func (p *parser) expectSemi() {
}
}
-func (p *parser) atComma(context string) bool {
+func (p *parser) atComma(context string, follow token.Token) bool {
if p.tok == token.COMMA {
return true
}
- if p.tok == token.SEMICOLON && p.lit == "\n" {
- p.error(p.pos, "missing ',' before newline in "+context)
- return true // "insert" the comma and continue
-
+ if p.tok != follow {
+ msg := "missing ','"
+ if p.tok == token.SEMICOLON && p.lit == "\n" {
+ msg += " before newline"
+ }
+ p.error(p.pos, msg+" in "+context)
+ return true // "insert" comma and continue
}
return false
}
@@ -825,7 +828,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
// parameter or result variable is the function body.
p.declare(field, nil, scope, ast.Var, idents...)
p.resolve(typ)
- if !p.atComma("parameter list") {
+ if !p.atComma("parameter list", token.RPAREN) {
return
}
p.next()
@@ -838,7 +841,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
// parameter or result variable is the function body.
p.declare(field, nil, scope, ast.Var, idents...)
p.resolve(typ)
- if !p.atComma("parameter list") {
+ if !p.atComma("parameter list", token.RPAREN) {
break
}
p.next()
@@ -1248,7 +1251,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
ellipsis = p.pos
p.next()
}
- if !p.atComma("argument list") {
+ if !p.atComma("argument list", token.RPAREN) {
break
}
p.next()
@@ -1323,7 +1326,7 @@ func (p *parser) parseElementList() (list []ast.Expr) {
for p.tok != token.RBRACE && p.tok != token.EOF {
list = append(list, p.parseElement())
- if !p.atComma("composite literal") {
+ if !p.atComma("composite literal", token.RBRACE) {
break
}
p.next()
@@ -1469,7 +1472,8 @@ L:
pos := p.pos
p.errorExpected(pos, "selector or type assertion")
p.next() // make progress
- x = &ast.BadExpr{From: pos, To: p.pos}
+ sel := &ast.Ident{NamePos: pos, Name: "_"}
+ x = &ast.SelectorExpr{X: x, Sel: sel}
}
case token.LBRACK:
if lhs {
diff --git a/src/go/parser/parser_test.go b/src/go/parser/parser_test.go
index 4b960d9e57..c7bb36d789 100644
--- a/src/go/parser/parser_test.go
+++ b/src/go/parser/parser_test.go
@@ -492,3 +492,42 @@ func TestIssue9979(t *testing.T) {
})
}
}
+
+// TestIncompleteSelection ensures that an incomplete selector
+// expression is parsed as a (blank) *ast.SelectorExpr, not a
+// *ast.BadExpr.
+func TestIncompleteSelection(t *testing.T) {
+ for _, src := range []string{
+ "package p; var _ = fmt.", // at EOF
+ "package p; var _ = fmt.\ntype X int", // not at EOF
+ } {
+ fset := token.NewFileSet()
+ f, err := ParseFile(fset, "", src, 0)
+ if err == nil {
+ t.Errorf("ParseFile(%s) succeeded unexpectedly", src)
+ continue
+ }
+
+ const wantErr = "expected selector or type assertion"
+ if !strings.Contains(err.Error(), wantErr) {
+ t.Errorf("ParseFile returned wrong error %q, want %q", err, wantErr)
+ }
+
+ var sel *ast.SelectorExpr
+ ast.Inspect(f, func(n ast.Node) bool {
+ if n, ok := n.(*ast.SelectorExpr); ok {
+ sel = n
+ }
+ return true
+ })
+ if sel == nil {
+ t.Error("found no *ast.SelectorExpr")
+ continue
+ }
+ const wantSel = "&{fmt _}"
+ if fmt.Sprint(sel) != wantSel {
+ t.Errorf("found selector %s, want %s", sel, wantSel)
+ continue
+ }
+ }
+}
diff --git a/src/go/parser/short_test.go b/src/go/parser/short_test.go
index 970ef2d3fa..ef2ffadbd9 100644
--- a/src/go/parser/short_test.go
+++ b/src/go/parser/short_test.go
@@ -99,8 +99,9 @@ var invalids = []string{
`package p; func f() { for i /* ERROR "boolean or range expression" */ , x := []string {} }`,
`package p; func f() { go f /* ERROR HERE "function must be invoked" */ }`,
`package p; func f() { defer func() {} /* ERROR HERE "function must be invoked" */ }`,
- `package p; func f() { go func() { func() { f(x func /* ERROR "expected '\)'" */ (){}) } } }`,
- `package p; func f() (a b string /* ERROR "expected '\)'" */ , ok bool)`, // issue 8656
+ `package p; func f() { go func() { func() { f(x func /* ERROR "missing ','" */ (){}) } } }`,
+ `package p; func f(x func(), u v func /* ERROR "missing ','" */ ()){}`,
+ `package p; func f() (a b string /* ERROR "missing ','" */ , ok bool)`, // issue 8656
`package p; var x /* ERROR "missing variable type or initialization" */ , y, z;`, // issue 9639
`package p; const x /* ERROR "missing constant value" */ ;`, // issue 9639
`package p; const x /* ERROR "missing constant value" */ int;`, // issue 9639
diff --git a/src/go/scanner/scanner.go b/src/go/scanner/scanner.go
index cec82ea10e..e9476c4dee 100644
--- a/src/go/scanner/scanner.go
+++ b/src/go/scanner/scanner.go
@@ -706,13 +706,14 @@ scanAgain:
s.insertSemi = false // newline consumed
return pos, token.SEMICOLON, "\n"
}
- lit = s.scanComment()
+ comment := s.scanComment()
if s.mode&ScanComments == 0 {
// skip comment
s.insertSemi = false // newline consumed
goto scanAgain
}
tok = token.COMMENT
+ lit = comment
} else {
tok = s.switch2(token.QUO, token.QUO_ASSIGN)
}
diff --git a/src/go/scanner/scanner_test.go b/src/go/scanner/scanner_test.go
index fc450d8a6e..0d21905166 100644
--- a/src/go/scanner/scanner_test.go
+++ b/src/go/scanner/scanner_test.go
@@ -734,6 +734,41 @@ func TestScanErrors(t *testing.T) {
}
}
+// Verify that no comments show up as literal values when skipping comments.
+func TestIssue10213(t *testing.T) {
+ var src = `
+ var (
+ A = 1 // foo
+ )
+
+ var (
+ B = 2
+ // foo
+ )
+
+ var C = 3 // foo
+
+ var D = 4
+ // foo
+
+ func anycode() {
+ // foo
+ }
+ `
+ var s Scanner
+ s.Init(fset.AddFile("", fset.Base(), len(src)), []byte(src), nil, 0)
+ for {
+ pos, tok, lit := s.Scan()
+ class := tokenclass(tok)
+ if lit != "" && class != keyword && class != literal && tok != token.SEMICOLON {
+ t.Errorf("%s: tok = %s, lit = %q", fset.Position(pos), tok, lit)
+ }
+ if tok <= token.EOF {
+ break
+ }
+ }
+}
+
func BenchmarkScan(b *testing.B) {
b.StopTimer()
fset := token.NewFileSet()
diff --git a/src/go/types.bash b/src/go/types.bash
deleted file mode 100644
index 1a384d410a..0000000000
--- a/src/go/types.bash
+++ /dev/null
@@ -1,97 +0,0 @@
-#!/bin/bash
-
-# 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.
-
-# Run this script to update the packages ./exact and ./types
-# in the $GOROOT/src/go directory. They are vendored from the
-# original sources in x/tools. Imports are renamed as needed.
-#
-# Delete this script once go/exact and go/types don't exist anymore in x/tools.
-#
-# NOTE(adonovan): the standard packages have intentionally diverged
-# from x/tools, so this script is a unlikely to be useful. Upstream
-# changes should be cherry-picked in to the standard library.
-
-set -e
-
-### Safety first.
-if [ ! -d "$GOPATH" ]; then
- echo 2>&1 '$GOPATH must be set.'
- exit 1
-fi
-if [ ! -d "$GOROOT" ]; then
- echo 2>&1 '$GOROOT must be set.'
- exit 1
-fi
-
-GODIR=$GOROOT/src/go
-
-function vendor() (
- SRCDIR=$GOPATH/src/golang.org/x/tools/$1
- DSTDIR=$GODIR/$2
-
- echo 2>&1 "vendoring $SRCDIR => $DSTDIR"
-
- # create directory
- rm -rf $DSTDIR
- mkdir -p $DSTDIR
- cd $DSTDIR
-
- # copy go sources and update import paths
- for f in $SRCDIR/*.go; do
- # copy $f and update imports
- sed -e 's|"golang.org/x/tools/go/exact"|"go/exact"|' \
- -e 's|"golang.org/x/tools/go/types"|"go/types"|' \
- -e 's|"golang.org/x/tools/go/gcimporter"|"go/internal/gcimporter"|' \
- $f | gofmt > tmp.go
- mv -f tmp.go `basename $f`
- done
-
- # copy testdata, if any
- if [ -e $SRCDIR/testdata ]; then
- cp -R $SRCDIR/testdata/ $DSTDIR/testdata/
- fi
-)
-
-function install() (
- PKG=$GODIR/$1
-
- echo 2>&1 "installing $PKG"
- cd $PKG
- go install
-)
-
-function test() (
- PKG=$GODIR/$1
-
- echo 2>&1 "testing $PKG"
- cd $PKG
- if ! go test; then
- echo 2>&1 "TESTING $PKG FAILED"
- exit 1
- fi
-)
-
-### go/exact
-vendor go/exact exact
-test exact
-install exact
-
-### go/types
-vendor go/types types
-# cannot test w/o gcimporter
-install types
-
-### go/gcimporter
-vendor go/gcimporter internal/gcimporter
-test internal/gcimporter
-install internal/gcimporter
-
-### test go/types (requires gcimporter)
-test types
-
-# All done.
-echo 2>&1 "DONE"
-exit 0
diff --git a/src/go/types/api.go b/src/go/types/api.go
index a2a55e31e7..ad9baa9527 100644
--- a/src/go/types/api.go
+++ b/src/go/types/api.go
@@ -28,7 +28,7 @@ import (
"bytes"
"fmt"
"go/ast"
- exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove
"go/token"
)
diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go
index 203a9c196d..c224699e3c 100644
--- a/src/go/types/builtins.go
+++ b/src/go/types/builtins.go
@@ -8,7 +8,7 @@ package types
import (
"go/ast"
- exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove
"go/token"
)
diff --git a/src/go/types/check.go b/src/go/types/check.go
index b4c356a6ed..7ae81eb2d0 100644
--- a/src/go/types/check.go
+++ b/src/go/types/check.go
@@ -8,7 +8,7 @@ package types
import (
"go/ast"
- exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove
"go/token"
)
diff --git a/src/go/types/conversions.go b/src/go/types/conversions.go
index 0cf9953c4f..da65f4276e 100644
--- a/src/go/types/conversions.go
+++ b/src/go/types/conversions.go
@@ -6,7 +6,7 @@
package types
-import exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+import exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove
// Conversion type-checks the conversion T(x).
// The result is in x.
diff --git a/src/go/types/decl.go b/src/go/types/decl.go
index c2c18ecd06..4af5b57798 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -6,7 +6,7 @@ package types
import (
"go/ast"
- exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove
"go/token"
)
diff --git a/src/go/types/eval_test.go b/src/go/types/eval_test.go
index bc27a8bb23..36e1cb954e 100644
--- a/src/go/types/eval_test.go
+++ b/src/go/types/eval_test.go
@@ -14,7 +14,6 @@ import (
"strings"
"testing"
- _ "go/internal/gcimporter"
. "go/types"
)
diff --git a/src/go/types/expr.go b/src/go/types/expr.go
index f91d89e8b8..425ae91bb4 100644
--- a/src/go/types/expr.go
+++ b/src/go/types/expr.go
@@ -9,7 +9,7 @@ package types
import (
"fmt"
"go/ast"
- exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove
"go/token"
"math"
)
diff --git a/src/go/types/object.go b/src/go/types/object.go
index 2404753b36..829e7a96b3 100644
--- a/src/go/types/object.go
+++ b/src/go/types/object.go
@@ -8,7 +8,7 @@ import (
"bytes"
"fmt"
"go/ast"
- exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove
"go/token"
)
diff --git a/src/go/types/operand.go b/src/go/types/operand.go
index 88c387058e..8d167067d5 100644
--- a/src/go/types/operand.go
+++ b/src/go/types/operand.go
@@ -9,7 +9,7 @@ package types
import (
"bytes"
"go/ast"
- exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove
"go/token"
)
diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go
index be46b59f11..64dcebe216 100644
--- a/src/go/types/resolver.go
+++ b/src/go/types/resolver.go
@@ -7,7 +7,7 @@ package types
import (
"fmt"
"go/ast"
- exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove
"go/token"
pathLib "path"
"strconv"
diff --git a/src/go/types/self_test.go b/src/go/types/self_test.go
index 85dc6ae0ec..4ff4e4d4a8 100644
--- a/src/go/types/self_test.go
+++ b/src/go/types/self_test.go
@@ -15,7 +15,6 @@ import (
"testing"
"time"
- _ "go/internal/gcimporter"
. "go/types"
)
@@ -31,7 +30,7 @@ func TestSelf(t *testing.T) {
conf := Config{Importer: importer.Default()}
_, err = conf.Check("go/types", fset, files, nil)
if err != nil {
- // Importing go/constants doesn't work in the
+ // Importing go/constant doesn't work in the
// build dashboard environment. Don't report an error
// for now so that the build remains green.
// TODO(gri) fix this
diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go
index d04dd71e4f..28a66cebe3 100644
--- a/src/go/types/stdlib_test.go
+++ b/src/go/types/stdlib_test.go
@@ -146,6 +146,7 @@ func TestStdFixed(t *testing.T) {
"bug459.go", // possibly incorrect test - see issue 6703 (pending spec clarification)
"issue3924.go", // possibly incorrect test - see issue 6671 (pending spec clarification)
"issue6889.go", // gc-specific test
+ "issue7746.go", // large constants - consumes too much memory
)
}
diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go
index 8b59df3eb6..586f6cc15c 100644
--- a/src/go/types/stmt.go
+++ b/src/go/types/stmt.go
@@ -9,7 +9,7 @@ package types
import (
"fmt"
"go/ast"
- exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove
"go/token"
)
diff --git a/src/go/types/testdata/issues.src b/src/go/types/testdata/issues.src
index d08e0fd878..595a6342b7 100644
--- a/src/go/types/testdata/issues.src
+++ b/src/go/types/testdata/issues.src
@@ -71,3 +71,27 @@ func issue9473(a []int, b ...int) {
append_(f0(), f1()... /* ERROR cannot use */ )
append_(f0(), f2()... /* ERROR cannot use */ )
}
+
+// Check that embedding a non-interface type in an interface results in a good error message.
+func issue10979() {
+ type _ interface {
+ int /* ERROR int is not an interface */
+ }
+ type T struct{}
+ type _ interface {
+ T /* ERROR T is not an interface */
+ }
+ type _ interface {
+ nosuchtype /* ERROR undeclared name: nosuchtype */
+ }
+ type _ interface {
+ fmt /* ERROR Nosuchtype not declared by package fmt */ .Nosuchtype
+ }
+ type _ interface {
+ nosuchpkg /* ERROR undeclared name: nosuchpkg */ .Nosuchtype
+ }
+ type I interface {
+ I /* ERROR I\.m \(value of type func\(I\)\) is not a type */ .m
+ m()
+ }
+}
diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go
index afd1dabc06..3fc1574e80 100644
--- a/src/go/types/typexpr.go
+++ b/src/go/types/typexpr.go
@@ -8,7 +8,7 @@ package types
import (
"go/ast"
- exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove
"go/token"
"sort"
"strconv"
@@ -525,20 +525,14 @@ func (check *Checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, d
for _, e := range embedded {
pos := e.Pos()
typ := check.typExpr(e, nil, path)
+ // Determine underlying embedded (possibly incomplete) type
+ // by following its forward chain.
named, _ := typ.(*Named)
- if named == nil {
- if typ != Typ[Invalid] {
- check.invalidAST(pos, "%s is not named type", typ)
- }
- continue
- }
- // determine underlying (possibly incomplete) type
- // by following its forward chain
- u := underlying(named)
- embed, _ := u.(*Interface)
+ under := underlying(named)
+ embed, _ := under.(*Interface)
if embed == nil {
- if u != Typ[Invalid] {
- check.errorf(pos, "%s is not an interface", named)
+ if typ != Typ[Invalid] {
+ check.errorf(pos, "%s is not an interface", typ)
}
continue
}
diff --git a/src/go/types/universe.go b/src/go/types/universe.go
index c02543e951..5e445e2838 100644
--- a/src/go/types/universe.go
+++ b/src/go/types/universe.go
@@ -7,7 +7,7 @@
package types
import (
- exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove
"go/token"
"strings"
)
diff --git a/src/html/escape.go b/src/html/escape.go
index dd5dfa7cd7..f50a4b937a 100644
--- a/src/html/escape.go
+++ b/src/html/escape.go
@@ -6,7 +6,6 @@
package html
import (
- "bytes"
"strings"
"unicode/utf8"
)
@@ -187,52 +186,20 @@ func unescape(b []byte) []byte {
return b
}
-const escapedChars = `&'<>"`
-
-func escape(w writer, s string) error {
- i := strings.IndexAny(s, escapedChars)
- for i != -1 {
- if _, err := w.WriteString(s[:i]); err != nil {
- return err
- }
- var esc string
- switch s[i] {
- case '&':
- esc = "&"
- case '\'':
- // "'" is shorter than "'" and apos was not in HTML until HTML5.
- esc = "'"
- case '<':
- esc = "<"
- case '>':
- esc = ">"
- case '"':
- // """ is shorter than """.
- esc = """
- default:
- panic("unrecognized escape character")
- }
- s = s[i+1:]
- if _, err := w.WriteString(esc); err != nil {
- return err
- }
- i = strings.IndexAny(s, escapedChars)
- }
- _, err := w.WriteString(s)
- return err
-}
+var htmlEscaper = strings.NewReplacer(
+ `&`, "&",
+ `'`, "'", // "'" is shorter than "'" and apos was not in HTML until HTML5.
+ `<`, "<",
+ `>`, ">",
+ `"`, """, // """ is shorter than """.
+)
// EscapeString escapes special characters like "<" to become "<". It
// escapes only five such characters: <, >, &, ' and ".
// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
// always true.
func EscapeString(s string) string {
- if strings.IndexAny(s, escapedChars) == -1 {
- return s
- }
- var buf bytes.Buffer
- escape(&buf, s)
- return buf.String()
+ return htmlEscaper.Replace(s)
}
// UnescapeString unescapes entities like "<" to become "<". It unescapes a
@@ -241,10 +208,8 @@ func EscapeString(s string) string {
// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
// always true.
func UnescapeString(s string) string {
- for _, c := range s {
- if c == '&' {
- return string(unescape([]byte(s)))
- }
+ if !strings.Contains(s, "&") {
+ return s
}
- return s
+ return string(unescape([]byte(s)))
}
diff --git a/src/html/escape_test.go b/src/html/escape_test.go
index 2d7ad8ac26..3702626a3d 100644
--- a/src/html/escape_test.go
+++ b/src/html/escape_test.go
@@ -4,7 +4,10 @@
package html
-import "testing"
+import (
+ "strings"
+ "testing"
+)
type unescapeTest struct {
// A short description of the test case.
@@ -113,3 +116,38 @@ func TestUnescapeEscape(t *testing.T) {
}
}
}
+
+var (
+ benchEscapeData = strings.Repeat("AAAAA < BBBBB > CCCCC & DDDDD ' EEEEE \" ", 100)
+ benchEscapeNone = strings.Repeat("AAAAA x BBBBB x CCCCC x DDDDD x EEEEE x ", 100)
+)
+
+func BenchmarkEscape(b *testing.B) {
+ n := 0
+ for i := 0; i < b.N; i++ {
+ n += len(EscapeString(benchEscapeData))
+ }
+}
+
+func BenchmarkEscapeNone(b *testing.B) {
+ n := 0
+ for i := 0; i < b.N; i++ {
+ n += len(EscapeString(benchEscapeNone))
+ }
+}
+
+func BenchmarkUnescape(b *testing.B) {
+ s := EscapeString(benchEscapeData)
+ n := 0
+ for i := 0; i < b.N; i++ {
+ n += len(UnescapeString(s))
+ }
+}
+
+func BenchmarkUnescapeNone(b *testing.B) {
+ s := EscapeString(benchEscapeNone)
+ n := 0
+ for i := 0; i < b.N; i++ {
+ n += len(UnescapeString(s))
+ }
+}
diff --git a/src/html/template/css.go b/src/html/template/css.go
index 634f183f79..318464835f 100644
--- a/src/html/template/css.go
+++ b/src/html/template/css.go
@@ -157,56 +157,20 @@ func isCSSSpace(b byte) bool {
func cssEscaper(args ...interface{}) string {
s, _ := stringify(args...)
var b bytes.Buffer
- written := 0
- for i, r := range s {
+ r, w, written := rune(0), 0, 0
+ for i := 0; i < len(s); i += w {
+ // See comment in htmlEscaper.
+ r, w = utf8.DecodeRuneInString(s[i:])
var repl string
- switch r {
- case 0:
- repl = `\0`
- case '\t':
- repl = `\9`
- case '\n':
- repl = `\a`
- case '\f':
- repl = `\c`
- case '\r':
- repl = `\d`
- // Encode HTML specials as hex so the output can be embedded
- // in HTML attributes without further encoding.
- case '"':
- repl = `\22`
- case '&':
- repl = `\26`
- case '\'':
- repl = `\27`
- case '(':
- repl = `\28`
- case ')':
- repl = `\29`
- case '+':
- repl = `\2b`
- case '/':
- repl = `\2f`
- case ':':
- repl = `\3a`
- case ';':
- repl = `\3b`
- case '<':
- repl = `\3c`
- case '>':
- repl = `\3e`
- case '\\':
- repl = `\\`
- case '{':
- repl = `\7b`
- case '}':
- repl = `\7d`
+ switch {
+ case int(r) < len(cssReplacementTable) && cssReplacementTable[r] != "":
+ repl = cssReplacementTable[r]
default:
continue
}
b.WriteString(s[written:i])
b.WriteString(repl)
- written = i + utf8.RuneLen(r)
+ written = i + w
if repl != `\\` && (written == len(s) || isHex(s[written]) || isCSSSpace(s[written])) {
b.WriteByte(' ')
}
@@ -218,6 +182,30 @@ func cssEscaper(args ...interface{}) string {
return b.String()
}
+var cssReplacementTable = []string{
+ 0: `\0`,
+ '\t': `\9`,
+ '\n': `\a`,
+ '\f': `\c`,
+ '\r': `\d`,
+ // Encode HTML specials as hex so the output can be embedded
+ // in HTML attributes without further encoding.
+ '"': `\22`,
+ '&': `\26`,
+ '\'': `\27`,
+ '(': `\28`,
+ ')': `\29`,
+ '+': `\2b`,
+ '/': `\2f`,
+ ':': `\3a`,
+ ';': `\3b`,
+ '<': `\3c`,
+ '>': `\3e`,
+ '\\': `\\`,
+ '{': `\7b`,
+ '}': `\7d`,
+}
+
var expressionBytes = []byte("expression")
var mozBindingBytes = []byte("mozbinding")
diff --git a/src/html/template/escape.go b/src/html/template/escape.go
index ee01fb12ab..a9529446dd 100644
--- a/src/html/template/escape.go
+++ b/src/html/template/escape.go
@@ -297,9 +297,9 @@ var redundantFuncs = map[string]map[string]bool{
// unless it is redundant with the last command.
func appendCmd(cmds []*parse.CommandNode, cmd *parse.CommandNode) []*parse.CommandNode {
if n := len(cmds); n != 0 {
- last, ok := cmds[n-1].Args[0].(*parse.IdentifierNode)
- next, _ := cmd.Args[0].(*parse.IdentifierNode)
- if ok && redundantFuncs[last.Ident][next.Ident] {
+ last, okLast := cmds[n-1].Args[0].(*parse.IdentifierNode)
+ next, okNext := cmd.Args[0].(*parse.IdentifierNode)
+ if okLast && okNext && redundantFuncs[last.Ident][next.Ident] {
return cmds
}
}
diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go
index 9c9502a617..6729ebf4a7 100644
--- a/src/html/template/escape_test.go
+++ b/src/html/template/escape_test.go
@@ -1547,6 +1547,16 @@ func TestEnsurePipelineContains(t *testing.T) {
"($).X | urlquery | html | print",
[]string{"urlquery", "html"},
},
+ {
+ "{{.X | print 2 | .f 3}}",
+ ".X | print 2 | .f 3 | urlquery | html",
+ []string{"urlquery", "html"},
+ },
+ {
+ "{{.X | html | print 2 | .f 3}}",
+ ".X | urlquery | html | print 2 | .f 3",
+ []string{"urlquery", "html"},
+ },
}
for i, test := range tests {
tmpl := template.Must(template.New("test").Parse(test.input))
diff --git a/src/html/template/html.go b/src/html/template/html.go
index 9c069efd1d..de4aa4abb2 100644
--- a/src/html/template/html.go
+++ b/src/html/template/html.go
@@ -138,21 +138,24 @@ var htmlNospaceNormReplacementTable = []string{
// and when badRunes is true, certain bad runes are allowed through unescaped.
func htmlReplacer(s string, replacementTable []string, badRunes bool) string {
written, b := 0, new(bytes.Buffer)
- for i, r := range s {
+ r, w := rune(0), 0
+ for i := 0; i < len(s); i += w {
+ // Cannot use 'for range s' because we need to preserve the width
+ // of the runes in the input. If we see a decoding error, the input
+ // width will not be utf8.Runelen(r) and we will overrun the buffer.
+ r, w = utf8.DecodeRuneInString(s[i:])
if int(r) < len(replacementTable) {
if repl := replacementTable[r]; len(repl) != 0 {
b.WriteString(s[written:i])
b.WriteString(repl)
- // Valid as long as replacementTable doesn't
- // include anything above 0x7f.
- written = i + utf8.RuneLen(r)
+ written = i + w
}
} else if badRunes {
// No-op.
// IE does not allow these ranges in unquoted attrs.
} else if 0xfdd0 <= r && r <= 0xfdef || 0xfff0 <= r && r <= 0xffff {
fmt.Fprintf(b, "%s%x;", s[written:i], r)
- written = i + utf8.RuneLen(r)
+ written = i + w
}
}
if written == 0 {
diff --git a/src/html/template/html_test.go b/src/html/template/html_test.go
index b9b9703875..f04ee04c27 100644
--- a/src/html/template/html_test.go
+++ b/src/html/template/html_test.go
@@ -19,7 +19,8 @@ func TestHTMLNospaceEscaper(t *testing.T) {
`PQRSTUVWXYZ[\]^_` +
"`abcdefghijklmno" +
"pqrstuvwxyz{|}~\x7f" +
- "\u00A0\u0100\u2028\u2029\ufeff\ufdec\U0001D11E")
+ "\u00A0\u0100\u2028\u2029\ufeff\ufdec\U0001D11E" +
+ "erroneous\x960") // keep at the end
want := ("�\x01\x02\x03\x04\x05\x06\x07" +
"\x08
\x0E\x0F" +
@@ -31,14 +32,16 @@ func TestHTMLNospaceEscaper(t *testing.T) {
`PQRSTUVWXYZ[\]^_` +
``abcdefghijklmno` +
`pqrstuvwxyz{|}~` + "\u007f" +
- "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E")
+ "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E" +
+ "erroneous�0") // keep at the end
got := htmlNospaceEscaper(input)
if got != want {
t.Errorf("encode: want\n\t%q\nbut got\n\t%q", want, got)
}
- got, want = html.UnescapeString(got), strings.Replace(input, "\x00", "\ufffd", 1)
+ r := strings.NewReplacer("\x00", "\ufffd", "\x96", "\ufffd")
+ got, want = html.UnescapeString(got), r.Replace(input)
if want != got {
t.Errorf("decode: want\n\t%q\nbut got\n\t%q", want, got)
}
diff --git a/src/html/template/js.go b/src/html/template/js.go
index 999a61ed07..f6d166b311 100644
--- a/src/html/template/js.go
+++ b/src/html/template/js.go
@@ -246,8 +246,10 @@ func jsRegexpEscaper(args ...interface{}) string {
// `\u2029`.
func replace(s string, replacementTable []string) string {
var b bytes.Buffer
- written := 0
- for i, r := range s {
+ r, w, written := rune(0), 0, 0
+ for i := 0; i < len(s); i += w {
+ // See comment in htmlEscaper.
+ r, w = utf8.DecodeRuneInString(s[i:])
var repl string
switch {
case int(r) < len(replacementTable) && replacementTable[r] != "":
@@ -261,7 +263,7 @@ func replace(s string, replacementTable []string) string {
}
b.WriteString(s[written:i])
b.WriteString(repl)
- written = i + utf8.RuneLen(r)
+ written = i + w
}
if written == 0 {
return s
diff --git a/src/internal/mime/header.go b/src/internal/mime/header.go
deleted file mode 100644
index 9bc3e5e576..0000000000
--- a/src/internal/mime/header.go
+++ /dev/null
@@ -1,122 +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 mime
-
-import (
- "bytes"
- "encoding/base64"
- "errors"
- "fmt"
- "io"
- "io/ioutil"
- "strconv"
- "strings"
- "unicode"
-)
-
-// EncodeWord encodes a string into an RFC 2047 encoded-word.
-func EncodeWord(s string) string {
- // UTF-8 "Q" encoding
- b := bytes.NewBufferString("=?utf-8?q?")
- for i := 0; i < len(s); i++ {
- switch c := s[i]; {
- case c == ' ':
- b.WriteByte('_')
- case isVchar(c) && c != '=' && c != '?' && c != '_':
- b.WriteByte(c)
- default:
- fmt.Fprintf(b, "=%02X", c)
- }
- }
- b.WriteString("?=")
- return b.String()
-}
-
-// DecodeWord decodes an RFC 2047 encoded-word.
-func DecodeWord(s string) (string, error) {
- fields := strings.Split(s, "?")
- if len(fields) != 5 || fields[0] != "=" || fields[4] != "=" {
- return "", errors.New("address not RFC 2047 encoded")
- }
- charset, enc := strings.ToLower(fields[1]), strings.ToLower(fields[2])
- if charset != "us-ascii" && charset != "iso-8859-1" && charset != "utf-8" {
- return "", fmt.Errorf("charset not supported: %q", charset)
- }
-
- in := bytes.NewBufferString(fields[3])
- var r io.Reader
- switch enc {
- case "b":
- r = base64.NewDecoder(base64.StdEncoding, in)
- case "q":
- r = qDecoder{r: in}
- default:
- return "", fmt.Errorf("RFC 2047 encoding not supported: %q", enc)
- }
-
- dec, err := ioutil.ReadAll(r)
- if err != nil {
- return "", err
- }
-
- switch charset {
- case "us-ascii":
- b := new(bytes.Buffer)
- for _, c := range dec {
- if c >= 0x80 {
- b.WriteRune(unicode.ReplacementChar)
- } else {
- b.WriteRune(rune(c))
- }
- }
- return b.String(), nil
- case "iso-8859-1":
- b := new(bytes.Buffer)
- for _, c := range dec {
- b.WriteRune(rune(c))
- }
- return b.String(), nil
- case "utf-8":
- return string(dec), nil
- }
- panic("unreachable")
-}
-
-type qDecoder struct {
- r io.Reader
- scratch [2]byte
-}
-
-func (qd qDecoder) Read(p []byte) (n int, err error) {
- // This method writes at most one byte into p.
- if len(p) == 0 {
- return 0, nil
- }
- if _, err := qd.r.Read(qd.scratch[:1]); err != nil {
- return 0, err
- }
- switch c := qd.scratch[0]; {
- case c == '=':
- if _, err := io.ReadFull(qd.r, qd.scratch[:2]); err != nil {
- return 0, err
- }
- x, err := strconv.ParseInt(string(qd.scratch[:2]), 16, 64)
- if err != nil {
- return 0, fmt.Errorf("mime: invalid RFC 2047 encoding: %q", qd.scratch[:2])
- }
- p[0] = byte(x)
- case c == '_':
- p[0] = ' '
- default:
- p[0] = c
- }
- return 1, nil
-}
-
-// isVchar returns true if c is an RFC 5322 VCHAR character.
-func isVchar(c byte) bool {
- // Visible (printing) characters.
- return '!' <= c && c <= '~'
-}
diff --git a/src/internal/syscall/unix/socket.go b/src/internal/syscall/unix/socket.go
new file mode 100644
index 0000000000..d7a9b9cb1d
--- /dev/null
+++ b/src/internal/syscall/unix/socket.go
@@ -0,0 +1,39 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+
+package unix
+
+// Getsockname copies the binary encoding of the current address for s
+// into addr.
+func Getsockname(s int, addr []byte) error {
+ return getsockname(s, addr)
+}
+
+// Getpeername copies the binary encoding of the peer address for s
+// into addr.
+func Getpeername(s int, addr []byte) error {
+ return getpeername(s, addr)
+}
+
+var emptyPayload uintptr
+
+// Recvfrom receives a message from s, copying the message into b.
+// The socket address addr must be large enough for storing the source
+// address of the message.
+// Flags must be operation control flags or 0.
+// It retunrs the number of bytes copied into b.
+func Recvfrom(s int, b []byte, flags int, addr []byte) (int, error) {
+ return recvfrom(s, b, flags, addr)
+}
+
+// Sendto sends a message to the socket address addr, copying the
+// message from b.
+// The socket address addr must be suitable for s.
+// Flags must be operation control flags or 0.
+// It retunrs the number of bytes copied from b.
+func Sendto(s int, b []byte, flags int, addr []byte) (int, error) {
+ return sendto(s, b, flags, addr)
+}
diff --git a/src/internal/syscall/unix/socket_linux_386.go b/src/internal/syscall/unix/socket_linux_386.go
new file mode 100644
index 0000000000..47105e0b1d
--- /dev/null
+++ b/src/internal/syscall/unix/socket_linux_386.go
@@ -0,0 +1,67 @@
+// 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 unix
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+const (
+ sysGETSOCKNAME = 0x6
+ sysGETPEERNAME = 0x7
+ sysSENDTO = 0xb
+ sysRECVFROM = 0xc
+)
+
+func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)
+func rawsocketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)
+
+func getsockname(s int, addr []byte) error {
+ l := uint32(len(addr))
+ _, errno := rawsocketcall(sysGETSOCKNAME, uintptr(s), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&l)), 0, 0, 0)
+ if errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
+
+func getpeername(s int, addr []byte) error {
+ l := uint32(len(addr))
+ _, errno := rawsocketcall(sysGETPEERNAME, uintptr(s), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&l)), 0, 0, 0)
+ if errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
+
+func recvfrom(s int, b []byte, flags int, from []byte) (int, error) {
+ var p unsafe.Pointer
+ if len(b) > 0 {
+ p = unsafe.Pointer(&b[0])
+ } else {
+ p = unsafe.Pointer(&emptyPayload)
+ }
+ l := uint32(len(from))
+ n, errno := socketcall(sysRECVFROM, uintptr(s), uintptr(p), uintptr(len(b)), uintptr(flags), uintptr(unsafe.Pointer(&from[0])), uintptr(unsafe.Pointer(&l)))
+ if errno != 0 {
+ return int(n), error(errno)
+ }
+ return int(n), nil
+}
+
+func sendto(s int, b []byte, flags int, to []byte) (int, error) {
+ var p unsafe.Pointer
+ if len(b) > 0 {
+ p = unsafe.Pointer(&b[0])
+ } else {
+ p = unsafe.Pointer(&emptyPayload)
+ }
+ n, errno := socketcall(sysSENDTO, uintptr(s), uintptr(p), uintptr(len(b)), uintptr(flags), uintptr(unsafe.Pointer(&to[0])), uintptr(len(to)))
+ if errno != 0 {
+ return int(n), error(errno)
+ }
+ return int(n), nil
+}
diff --git a/src/internal/syscall/unix/socket_linux_386.s b/src/internal/syscall/unix/socket_linux_386.s
new file mode 100644
index 0000000000..48e2094db5
--- /dev/null
+++ b/src/internal/syscall/unix/socket_linux_386.s
@@ -0,0 +1,11 @@
+// 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.
+
+#include "textflag.h"
+
+TEXT ·socketcall(SB),NOSPLIT,$0-36
+ JMP syscall·socketcall(SB)
+
+TEXT ·rawsocketcall(SB),NOSPLIT,$0-36
+ JMP syscall·socketcall(SB)
diff --git a/src/internal/syscall/unix/socket_stub.go b/src/internal/syscall/unix/socket_stub.go
new file mode 100644
index 0000000000..1c89ed1820
--- /dev/null
+++ b/src/internal/syscall/unix/socket_stub.go
@@ -0,0 +1,25 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build nacl solaris
+
+package unix
+
+import "syscall"
+
+func getsockname(s int, addr []byte) error {
+ return syscall.EOPNOTSUPP
+}
+
+func getpeername(s int, addr []byte) error {
+ return syscall.EOPNOTSUPP
+}
+
+func recvfrom(s int, b []byte, flags int, from []byte) (int, error) {
+ return 0, syscall.EOPNOTSUPP
+}
+
+func sendto(s int, b []byte, flags int, to []byte) (int, error) {
+ return 0, syscall.EOPNOTSUPP
+}
diff --git a/src/internal/syscall/unix/socket_unix.go b/src/internal/syscall/unix/socket_unix.go
new file mode 100644
index 0000000000..a769bb38b6
--- /dev/null
+++ b/src/internal/syscall/unix/socket_unix.go
@@ -0,0 +1,59 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux,!386 netbsd openbsd
+
+package unix
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func getsockname(s int, addr []byte) error {
+ l := uint32(len(addr))
+ _, _, errno := syscall.RawSyscall(syscall.SYS_GETSOCKNAME, uintptr(s), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&l)))
+ if errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
+
+func getpeername(s int, addr []byte) error {
+ l := uint32(len(addr))
+ _, _, errno := syscall.RawSyscall(syscall.SYS_GETPEERNAME, uintptr(s), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&l)))
+ if errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
+
+func recvfrom(s int, b []byte, flags int, from []byte) (int, error) {
+ var p unsafe.Pointer
+ if len(b) > 0 {
+ p = unsafe.Pointer(&b[0])
+ } else {
+ p = unsafe.Pointer(&emptyPayload)
+ }
+ l := uint32(len(from))
+ n, _, errno := syscall.Syscall6(syscall.SYS_RECVFROM, uintptr(s), uintptr(p), uintptr(len(b)), uintptr(flags), uintptr(unsafe.Pointer(&from[0])), uintptr(unsafe.Pointer(&l)))
+ if errno != 0 {
+ return int(n), error(errno)
+ }
+ return int(n), nil
+}
+
+func sendto(s int, b []byte, flags int, to []byte) (int, error) {
+ var p unsafe.Pointer
+ if len(b) > 0 {
+ p = unsafe.Pointer(&b[0])
+ } else {
+ p = unsafe.Pointer(&emptyPayload)
+ }
+ n, _, errno := syscall.Syscall6(syscall.SYS_SENDTO, uintptr(s), uintptr(p), uintptr(len(b)), uintptr(flags), uintptr(unsafe.Pointer(&to[0])), uintptr(len(to)))
+ if errno != 0 {
+ return int(n), error(errno)
+ }
+ return int(n), nil
+}
diff --git a/src/internal/syscall/windows/registry/export_test.go b/src/internal/syscall/windows/registry/export_test.go
new file mode 100644
index 0000000000..8badf6fdcf
--- /dev/null
+++ b/src/internal/syscall/windows/registry/export_test.go
@@ -0,0 +1,11 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build windows
+
+package registry
+
+func (k Key) SetValue(name string, valtype uint32, data []byte) error {
+ return k.setValue(name, valtype, data)
+}
diff --git a/src/internal/syscall/windows/registry/registry_test.go b/src/internal/syscall/windows/registry/registry_test.go
index 5f75febd27..07eccb23d8 100644
--- a/src/internal/syscall/windows/registry/registry_test.go
+++ b/src/internal/syscall/windows/registry/registry_test.go
@@ -611,3 +611,68 @@ func TestExpandString(t *testing.T) {
t.Errorf("want %q string expanded, got %q", want, got)
}
}
+
+func TestInvalidValues(t *testing.T) {
+ softwareK, err := registry.OpenKey(registry.CURRENT_USER, "Software", registry.QUERY_VALUE)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer softwareK.Close()
+
+ testKName := randKeyName("TestInvalidValues_")
+
+ k, exist, err := registry.CreateKey(softwareK, testKName, registry.CREATE_SUB_KEY|registry.QUERY_VALUE|registry.SET_VALUE)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer k.Close()
+
+ if exist {
+ t.Fatalf("key %q already exists", testKName)
+ }
+
+ defer registry.DeleteKey(softwareK, testKName)
+
+ var tests = []struct {
+ Type uint32
+ Name string
+ Data []byte
+ }{
+ {registry.DWORD, "Dword1", nil},
+ {registry.DWORD, "Dword2", []byte{1, 2, 3}},
+ {registry.QWORD, "Qword1", nil},
+ {registry.QWORD, "Qword2", []byte{1, 2, 3}},
+ {registry.QWORD, "Qword3", []byte{1, 2, 3, 4, 5, 6, 7}},
+ {registry.MULTI_SZ, "MultiString1", nil},
+ {registry.MULTI_SZ, "MultiString2", []byte{0}},
+ {registry.MULTI_SZ, "MultiString3", []byte{'a', 'b', 0}},
+ {registry.MULTI_SZ, "MultiString4", []byte{'a', 0, 0, 'b', 0}},
+ {registry.MULTI_SZ, "MultiString5", []byte{'a', 0, 0}},
+ }
+
+ for _, test := range tests {
+ err := k.SetValue(test.Name, test.Type, test.Data)
+ if err != nil {
+ t.Fatalf("SetValue for %q failed: %v", test.Name, err)
+ }
+ }
+
+ for _, test := range tests {
+ switch test.Type {
+ case registry.DWORD, registry.QWORD:
+ value, valType, err := k.GetIntegerValue(test.Name)
+ if err == nil {
+ t.Errorf("GetIntegerValue(%q) succeeded. Returns type=%d value=%v", test.Name, valType, value)
+ }
+ case registry.MULTI_SZ:
+ value, valType, err := k.GetStringsValue(test.Name)
+ if err == nil {
+ if len(value) != 0 {
+ t.Errorf("GetStringsValue(%q) succeeded. Returns type=%d value=%v", test.Name, valType, value)
+ }
+ }
+ default:
+ t.Errorf("unsupported type %d for %s value", test.Type, test.Name)
+ }
+ }
+}
diff --git a/src/internal/syscall/windows/registry/value.go b/src/internal/syscall/windows/registry/value.go
index 1c1771d30e..bb45a23643 100644
--- a/src/internal/syscall/windows/registry/value.go
+++ b/src/internal/syscall/windows/registry/value.go
@@ -130,7 +130,7 @@ func ExpandString(value string) (string, error) {
return "", err
}
if n <= uint32(len(r)) {
- u := (*[1 << 10]uint16)(unsafe.Pointer(&r[0]))[:]
+ u := (*[1 << 15]uint16)(unsafe.Pointer(&r[0]))[:]
return syscall.UTF16ToString(u), nil
}
r = make([]uint16, n)
@@ -150,9 +150,17 @@ func (k Key) GetStringsValue(name string) (val []string, valtype uint32, err err
if typ != MULTI_SZ {
return nil, typ, ErrUnexpectedType
}
- val = make([]string, 0, 5)
+ if len(data) == 0 {
+ return nil, typ, nil
+ }
p := (*[1 << 24]uint16)(unsafe.Pointer(&data[0]))[:len(data)/2]
- p = p[:len(p)-1] // remove terminating nil
+ if len(p) == 0 {
+ return nil, typ, nil
+ }
+ if p[len(p)-1] == 0 {
+ p = p[:len(p)-1] // remove terminating null
+ }
+ val = make([]string, 0, 5)
from := 0
for i, c := range p {
if c == 0 {
@@ -175,8 +183,14 @@ func (k Key) GetIntegerValue(name string) (val uint64, valtype uint32, err error
}
switch typ {
case DWORD:
+ if len(data) != 4 {
+ return 0, typ, errors.New("DWORD value is not 4 bytes long")
+ }
return uint64(*(*uint32)(unsafe.Pointer(&data[0]))), DWORD, nil
case QWORD:
+ if len(data) != 8 {
+ return 0, typ, errors.New("QWORD value is not 8 bytes long")
+ }
return uint64(*(*uint64)(unsafe.Pointer(&data[0]))), QWORD, nil
default:
return 0, typ, ErrUnexpectedType
diff --git a/src/math/big/arith.go b/src/math/big/arith.go
index 1ff6349d9d..d7ea8381e7 100644
--- a/src/math/big/arith.go
+++ b/src/math/big/arith.go
@@ -107,11 +107,26 @@ func log2(x Word) int {
return bitLen(x) - 1
}
-// Number of leading zeros in x.
-func leadingZeros(x Word) uint {
+// nlz returns the number of leading zeros in x.
+func nlz(x Word) uint {
return uint(_W - bitLen(x))
}
+// nlz64 returns the number of leading zeros in x.
+func nlz64(x uint64) uint {
+ switch _W {
+ case 32:
+ w := x >> 32
+ if w == 0 {
+ return 32 + nlz(Word(x))
+ }
+ return nlz(Word(w))
+ case 64:
+ return nlz(Word(x))
+ }
+ panic("unreachable")
+}
+
// q = (u1<<_W + u0 - r)/y
// Adapted from Warren, Hacker's Delight, p. 152.
func divWW_g(u1, u0, v Word) (q, r Word) {
@@ -119,7 +134,7 @@ func divWW_g(u1, u0, v Word) (q, r Word) {
return 1<<_W - 1, 1<<_W - 1
}
- s := leadingZeros(v)
+ s := nlz(v)
v <<= s
vn1 := v >> _W2
diff --git a/src/math/big/arith_amd64.s b/src/math/big/arith_amd64.s
index d2d5187a48..b69a2c616a 100644
--- a/src/math/big/arith_amd64.s
+++ b/src/math/big/arith_amd64.s
@@ -351,6 +351,34 @@ TEXT ·addMulVVW(SB),NOSPLIT,$0
MOVQ z_len+8(FP), R11
MOVQ $0, BX // i = 0
MOVQ $0, CX // c = 0
+ MOVQ R11, R12
+ ANDQ $-2, R12
+ CMPQ R11, $2
+ JAE A6
+ JMP E6
+
+A6:
+ MOVQ (R8)(BX*8), AX
+ MULQ R9
+ ADDQ (R10)(BX*8), AX
+ ADCQ $0, DX
+ ADDQ CX, AX
+ ADCQ $0, DX
+ MOVQ DX, CX
+ MOVQ AX, (R10)(BX*8)
+
+ MOVQ (8)(R8)(BX*8), AX
+ MULQ R9
+ ADDQ (8)(R10)(BX*8), AX
+ ADCQ $0, DX
+ ADDQ CX, AX
+ ADCQ $0, DX
+ MOVQ DX, CX
+ MOVQ AX, (8)(R10)(BX*8)
+
+ ADDQ $2, BX
+ CMPQ BX, R12
+ JL A6
JMP E6
L6: MOVQ (R8)(BX*8), AX
diff --git a/src/math/big/bits_test.go b/src/math/big/bits_test.go
index 3ce24222d7..14ecab5909 100644
--- a/src/math/big/bits_test.go
+++ b/src/math/big/bits_test.go
@@ -203,18 +203,18 @@ func TestFromBits(t *testing.T) {
}{
// all different bit numbers
{nil, "0"},
- {Bits{0}, "0x.8p1"},
- {Bits{1}, "0x.8p2"},
- {Bits{-1}, "0x.8p0"},
- {Bits{63}, "0x.8p64"},
- {Bits{33, -30}, "0x.8000000000000001p34"},
- {Bits{255, 0}, "0x.8000000000000000000000000000000000000000000000000000000000000001p256"},
+ {Bits{0}, "0x.8p+1"},
+ {Bits{1}, "0x.8p+2"},
+ {Bits{-1}, "0x.8p+0"},
+ {Bits{63}, "0x.8p+64"},
+ {Bits{33, -30}, "0x.8000000000000001p+34"},
+ {Bits{255, 0}, "0x.8000000000000000000000000000000000000000000000000000000000000001p+256"},
// multiple equal bit numbers
- {Bits{0, 0}, "0x.8p2"},
- {Bits{0, 0, 0, 0}, "0x.8p3"},
- {Bits{0, 1, 0}, "0x.8p3"},
- {append(Bits{2, 1, 0} /* 7 */, Bits{3, 1} /* 10 */ ...), "0x.88p5" /* 17 */},
+ {Bits{0, 0}, "0x.8p+2"},
+ {Bits{0, 0, 0, 0}, "0x.8p+3"},
+ {Bits{0, 1, 0}, "0x.8p+3"},
+ {append(Bits{2, 1, 0} /* 7 */, Bits{3, 1} /* 10 */ ...), "0x.88p+5" /* 17 */},
} {
f := test.bits.Float()
if got := f.Format('p', 0); got != test.want {
diff --git a/src/math/big/decimal.go b/src/math/big/decimal.go
index 3d024dce68..2595e5f8c1 100644
--- a/src/math/big/decimal.go
+++ b/src/math/big/decimal.go
@@ -19,12 +19,14 @@
package big
-// A decimal represents a floating-point number in decimal representation.
-// The value of a decimal x is x.mant * 10 ** x.exp with 0.5 <= x.mant < 1,
-// with the most-significant mantissa digit at index 0.
+// A decimal represents an unsigned floating-point number in decimal representation.
+// The value of a non-zero decimal x is x.mant * 10 ** x.exp with 0.5 <= x.mant < 1,
+// with the most-significant mantissa digit at index 0. For the zero decimal, the
+// mantissa length and exponent are 0.
+// The zero value for decimal represents a ready-to-use 0.0.
type decimal struct {
mant []byte // mantissa ASCII digits, big-endian
- exp int // exponent, valid if len(mant) > 0
+ exp int // exponent
}
// Maximum shift amount that can be done in one pass without overflow.
@@ -46,6 +48,7 @@ func (x *decimal) init(m nat, shift int) {
// special case 0
if len(m) == 0 {
x.mant = x.mant[:0]
+ x.exp = 0
return
}
@@ -255,4 +258,7 @@ func trim(x *decimal) {
i--
}
x.mant = x.mant[:i]
+ if i == 0 {
+ x.exp = 0
+ }
}
diff --git a/src/math/big/float.go b/src/math/big/float.go
index d46c046e67..1563528797 100644
--- a/src/math/big/float.go
+++ b/src/math/big/float.go
@@ -525,25 +525,6 @@ func (z *Float) round(sbit uint) {
return
}
-// nlz returns the number of leading zero bits in x.
-func nlz(x Word) uint {
- return _W - uint(bitLen(x))
-}
-
-func nlz64(x uint64) uint {
- // TODO(gri) this can be done more nicely
- if _W == 32 {
- if x>>32 == 0 {
- return 32 + nlz(Word(x))
- }
- return nlz(Word(x >> 32))
- }
- if _W == 64 {
- return nlz(Word(x))
- }
- panic("unreachable")
-}
-
func (z *Float) setBits64(neg bool, x uint64) *Float {
if z.prec == 0 {
z.prec = 64
@@ -732,25 +713,44 @@ func (z *Float) Copy(x *Float) *Float {
return z
}
-func high32(x nat) uint32 {
- // TODO(gri) This can be done more efficiently on 32bit platforms.
- return uint32(high64(x) >> 32)
-}
-
-func high64(x nat) uint64 {
- i := len(x)
- if i == 0 {
+// msb32 returns the 32 most significant bits of x.
+func msb32(x nat) uint32 {
+ i := len(x) - 1
+ if i < 0 {
return 0
}
- // i > 0
- v := uint64(x[i-1])
- if _W == 32 {
- v <<= 32
- if i > 1 {
- v |= uint64(x[i-2])
- }
+ if debugFloat && x[i]&(1<<(_W-1)) == 0 {
+ panic("x not normalized")
}
- return v
+ switch _W {
+ case 32:
+ return uint32(x[i])
+ case 64:
+ return uint32(x[i] >> 32)
+ }
+ panic("unreachable")
+}
+
+// msb64 returns the 64 most significant bits of x.
+func msb64(x nat) uint64 {
+ i := len(x) - 1
+ if i < 0 {
+ return 0
+ }
+ if debugFloat && x[i]&(1<<(_W-1)) == 0 {
+ panic("x not normalized")
+ }
+ switch _W {
+ case 32:
+ v := uint64(x[i]) << 32
+ if i > 0 {
+ v |= uint64(x[i-1])
+ }
+ return v
+ case 64:
+ return uint64(x[i])
+ }
+ panic("unreachable")
}
// Uint64 returns the unsigned integer resulting from truncating x
@@ -776,7 +776,7 @@ func (x *Float) Uint64() (uint64, Accuracy) {
// 1 <= x < Inf
if x.exp <= 64 {
// u = trunc(x) fits into a uint64
- u := high64(x.mant) >> (64 - uint32(x.exp))
+ u := msb64(x.mant) >> (64 - uint32(x.exp))
if x.MinPrec() <= 64 {
return u, Exact
}
@@ -821,7 +821,7 @@ func (x *Float) Int64() (int64, Accuracy) {
// 1 <= |x| < +Inf
if x.exp <= 63 {
// i = trunc(x) fits into an int64 (excluding math.MinInt64)
- i := int64(high64(x.mant) >> (64 - uint32(x.exp)))
+ i := int64(msb64(x.mant) >> (64 - uint32(x.exp)))
if x.neg {
i = -i
}
@@ -853,9 +853,6 @@ func (x *Float) Int64() (int64, Accuracy) {
panic("unreachable")
}
-// TODO(gri) Float32 and Float64 are very similar internally but for the
-// floatxx parameters and some conversions. Should factor out shared code.
-
// Float32 returns the float32 value nearest to x. If x is too small to be
// represented by a float32 (|x| < math.SmallestNonzeroFloat32), the result
// is (0, Below) or (-0, Above), respectively, depending on the sign of x.
@@ -880,64 +877,71 @@ func (x *Float) Float32() (float32, Accuracy) {
emax = bias // 127 largest unbiased exponent (normal)
)
- // Float mantissae m have an explicit msb and are in the range 0.5 <= m < 1.0.
- // floatxx mantissae have an implicit msb and are in the range 1.0 <= m < 2.0.
- // For a given mantissa m, we need to add 1 to a floatxx exponent to get the
- // corresponding Float exponent.
- // (see also implementation of math.Ldexp for similar code)
+ // Float mantissa m is 0.5 <= m < 1.0; compute exponent for floatxx mantissa.
+ e := x.exp - 1 // exponent for mantissa m with 1.0 <= m < 2.0
+ p := mbits + 1 // precision of normal float
- if x.exp < dmin+1 {
- // underflow
- if x.neg {
- var z float32
- return -z, Above
+ // If the exponent is too small, we may have a denormal number
+ // in which case we have fewer mantissa bits available: reduce
+ // precision accordingly.
+ if e < emin {
+ p -= emin - int(e)
+ // Make sure we have at least 1 bit so that we don't
+ // lose numbers rounded up to the smallest denormal.
+ if p < 1 {
+ p = 1
}
- return 0.0, Below
}
- // x.exp >= dmin+1
+ // round
var r Float
- r.prec = mbits + 1 // +1 for implicit msb
- if x.exp < emin+1 {
- // denormal number - round to fewer bits
- r.prec = uint32(x.exp - dmin)
- }
+ r.prec = uint32(p)
r.Set(x)
+ e = r.exp - 1
// Rounding may have caused r to overflow to ±Inf
// (rounding never causes underflows to 0).
if r.form == inf {
- r.exp = emax + 2 // cause overflow below
+ e = emax + 1 // cause overflow below
}
- if r.exp > emax+1 {
+ // If the exponent is too large, overflow to ±Inf.
+ if e > emax {
// overflow
if x.neg {
return float32(math.Inf(-1)), Below
}
return float32(math.Inf(+1)), Above
}
- // dmin+1 <= r.exp <= emax+1
+ // e <= emax
- var s uint32
- if r.neg {
- s = 1 << (fbits - 1)
+ // Determine sign, biased exponent, and mantissa.
+ var sign, bexp, mant uint32
+ if x.neg {
+ sign = 1 << (fbits - 1)
}
- m := high32(r.mant) >> ebits & (1<> (fbits - r.prec)
+ } else {
+ // normal number: emin <= e <= emax
+ bexp = uint32(e+bias) << mbits
+ mant = msb32(r.mant) >> ebits & (1<= dmin+1
+ // round
var r Float
- r.prec = mbits + 1 // +1 for implicit msb
- if x.exp < emin+1 {
- // denormal number - round to fewer bits
- r.prec = uint32(x.exp - dmin)
- }
+ r.prec = uint32(p)
r.Set(x)
+ e = r.exp - 1
// Rounding may have caused r to overflow to ±Inf
// (rounding never causes underflows to 0).
if r.form == inf {
- r.exp = emax + 2 // cause overflow below
+ e = emax + 1 // cause overflow below
}
- if r.exp > emax+1 {
+ // If the exponent is too large, overflow to ±Inf.
+ if e > emax {
// overflow
if x.neg {
return math.Inf(-1), Below
}
return math.Inf(+1), Above
}
- // dmin+1 <= r.exp <= emax+1
+ // e <= emax
- var s uint64
- if r.neg {
- s = 1 << (fbits - 1)
+ // Determine sign, biased exponent, and mantissa.
+ var sign, bexp, mant uint64
+ if x.neg {
+ sign = 1 << (fbits - 1)
}
- m := high64(r.mant) >> ebits & (1<> (fbits - r.prec)
+ } else {
+ // normal number: emin <= e <= emax
+ bexp = uint64(e+bias) << mbits
+ mant = msb64(r.mant) >> ebits & (1< x.prec:
- m = nat(nil).shr(m, uint(w-x.prec))
- }
-
- buf = append(buf, m.decimalString()...)
- buf = append(buf, 'p')
- e := int64(x.exp) - int64(x.prec)
- if e >= 0 {
- buf = append(buf, '+')
- }
- return strconv.AppendInt(buf, e, 10)
-}
-
-// pstring appends the string of x in the format ["-"] "0x." mantissa "p" exponent
-// with a hexadecimal mantissa and a binary exponent, or ["-"] "0" if x is zero,
-// ad returns the extended buffer.
-// The mantissa is normalized such that 0.5 <= 0.mantissa < 1.0.
-func (x *Float) pstring(buf []byte) []byte {
- if x.neg {
- buf = append(buf, '-')
- }
- if x.form == zero {
- return append(buf, '0')
- }
-
- if debugFloat && x.form != finite {
- panic("non-finite float")
- }
- // x != 0
-
- // remove trailing 0 words early
- // (no need to convert to hex 0's and trim later)
- m := x.mant
- i := 0
- for i < len(m) && m[i] == 0 {
- i++
- }
- m = m[i:]
-
- buf = append(buf, "0x."...)
- buf = append(buf, strings.TrimRight(x.mant.hexString(), "0")...)
- buf = append(buf, 'p')
- return strconv.AppendInt(buf, int64(x.exp), 10)
-}
diff --git a/src/math/big/floatconv_test.go b/src/math/big/floatconv_test.go
index 96c01eed81..9fc2b89fb9 100644
--- a/src/math/big/floatconv_test.go
+++ b/src/math/big/floatconv_test.go
@@ -125,12 +125,16 @@ func TestFloat64Format(t *testing.T) {
{1, 'f', 0, "1"},
{-1, 'f', 0, "-1"},
+ {0.001, 'e', 0, "1e-03"},
+ {0.459, 'e', 0, "5e-01"},
{1.459, 'e', 0, "1e+00"},
{2.459, 'e', 1, "2.5e+00"},
{3.459, 'e', 2, "3.46e+00"},
{4.459, 'e', 3, "4.459e+00"},
{5.459, 'e', 4, "5.4590e+00"},
+ {0.001, 'f', 0, "0"},
+ {0.459, 'f', 0, "0"},
{1.459, 'f', 0, "1"},
{2.459, 'f', 1, "2.5"},
{3.459, 'f', 2, "3.46"},
@@ -145,8 +149,8 @@ func TestFloat64Format(t *testing.T) {
{0, 'p', 0, "0"},
{math.Copysign(0, -1), 'p', 0, "-0"},
- {1024.0, 'p', 0, "0x.8p11"},
- {-1024.0, 'p', 0, "-0x.8p11"},
+ {1024.0, 'p', 0, "0x.8p+11"},
+ {-1024.0, 'p', 0, "-0x.8p+11"},
// all test cases below from strconv/ftoa_test.go
{1, 'e', 5, "1.00000e+00"},
@@ -331,8 +335,8 @@ func TestFloatFormat(t *testing.T) {
{"3e40", 100, 'g', 40, "3e+40"},
// make sure "stupid" exponents don't stall the machine
- {"1e1000000", 64, 'p', 0, "0x.88b3a28a05eade3ap3321929"},
- {"1e1000000000", 64, 'p', 0, "0x.ecc5f45aa573d3p1538481529"},
+ {"1e1000000", 64, 'p', 0, "0x.88b3a28a05eade3ap+3321929"},
+ {"1e1000000000", 64, 'p', 0, "0x.ecc5f45aa573d3p+1538481529"},
{"1e-1000000", 64, 'p', 0, "0x.efb4542cc8ca418ap-3321928"},
{"1e-1000000000", 64, 'p', 0, "0x.8a64dd983a4c7dabp-1538481528"},
@@ -352,20 +356,21 @@ func TestFloatFormat(t *testing.T) {
{"3.00", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"},
{"3.000", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"},
- {"3", 350, 'p', 0, "0x.cp2"},
- {"03", 350, 'p', 0, "0x.cp2"},
- {"3.", 350, 'p', 0, "0x.cp2"},
- {"3.0", 350, 'p', 0, "0x.cp2"},
- {"3.00", 350, 'p', 0, "0x.cp2"},
- {"3.000", 350, 'p', 0, "0x.cp2"},
+ {"3", 350, 'p', 0, "0x.cp+2"},
+ {"03", 350, 'p', 0, "0x.cp+2"},
+ {"3.", 350, 'p', 0, "0x.cp+2"},
+ {"3.0", 350, 'p', 0, "0x.cp+2"},
+ {"3.00", 350, 'p', 0, "0x.cp+2"},
+ {"3.000", 350, 'p', 0, "0x.cp+2"},
{"0", 64, 'p', 0, "0"},
{"-0", 64, 'p', 0, "-0"},
- {"1024.0", 64, 'p', 0, "0x.8p11"},
- {"-1024.0", 64, 'p', 0, "-0x.8p11"},
+ {"1024.0", 64, 'p', 0, "0x.8p+11"},
+ {"-1024.0", 64, 'p', 0, "-0x.8p+11"},
// unsupported format
{"3.14", 64, 'x', 0, "%x"},
+ {"-3.14", 64, 'x', 0, "%x"},
} {
f, _, err := ParseFloat(test.x, 0, test.prec, ToNearestEven)
if err != nil {
diff --git a/src/math/big/floatexample_test.go b/src/math/big/floatexample_test.go
index 7db10238bc..d9d39ed365 100644
--- a/src/math/big/floatexample_test.go
+++ b/src/math/big/floatexample_test.go
@@ -21,9 +21,9 @@ func ExampleFloat_Add() {
fmt.Printf("y = %s (%s, prec = %d, acc = %s)\n", &y, y.Format('p', 0), y.Prec(), y.Acc())
fmt.Printf("z = %s (%s, prec = %d, acc = %s)\n", &z, z.Format('p', 0), z.Prec(), z.Acc())
// Output:
- // x = 1000 (0x.fap10, prec = 64, acc = Exact)
- // y = 2.718281828 (0x.adf85458248cd8p2, prec = 53, acc = Exact)
- // z = 1002.718282 (0x.faadf854p10, prec = 32, acc = Below)
+ // x = 1000 (0x.fap+10, prec = 64, acc = Exact)
+ // y = 2.718281828 (0x.adf85458248cd8p+2, prec = 53, acc = Exact)
+ // z = 1002.718282 (0x.faadf854p+10, prec = 32, acc = Below)
}
func Example_Shift() {
diff --git a/src/math/big/ftoa.go b/src/math/big/ftoa.go
index 0a9edfd7b2..4c3e743d6c 100644
--- a/src/math/big/ftoa.go
+++ b/src/math/big/ftoa.go
@@ -2,34 +2,87 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// This file implements the 'e', 'f', 'g' floating-point formats.
-// It is closely following the corresponding implementation in
-// strconv/ftoa.go, but modified and simplified for big.Float.
-
-// Algorithm:
-// 1) convert Float to multiprecision decimal
-// 2) round to desired precision
-// 3) read digits out and format
+// This file implements Float-to-string conversion functions.
+// It is closely following the corresponding implementation
+// in strconv/ftoa.go, but modified and simplified for Float.
package big
-import "strconv"
+import (
+ "strconv"
+ "strings"
+)
-// TODO(gri) Consider moving sign into decimal - could make the signatures below cleaner.
+// Format converts the floating-point number x to a string according
+// to the given format and precision prec. The format is one of:
+//
+// 'e' -d.dddde±dd, decimal exponent, at least two (possibly 0) exponent digits
+// 'E' -d.ddddE±dd, decimal exponent, at least two (possibly 0) exponent digits
+// 'f' -ddddd.dddd, no exponent
+// 'g' like 'e' for large exponents, like 'f' otherwise
+// 'G' like 'E' for large exponents, like 'f' otherwise
+// 'b' -ddddddp±dd, binary exponent
+// 'p' -0x.dddp±dd, binary exponent, hexadecimal mantissa
+//
+// For the binary exponent formats, the mantissa is printed in normalized form:
+//
+// 'b' decimal integer mantissa using x.Prec() bits, or -0
+// 'p' hexadecimal fraction with 0.5 <= 0.mantissa < 1.0, or -0
+//
+// The precision prec controls the number of digits (excluding the exponent)
+// printed by the 'e', 'E', 'f', 'g', and 'G' formats. For 'e', 'E', and 'f'
+// it is the number of digits after the decimal point. For 'g' and 'G' it is
+// the total number of digits. A negative precision selects the smallest
+// number of digits necessary such that ParseFloat will return f exactly.
+// The prec value is ignored for the 'b' or 'p' format.
+//
+// BUG(gri) Float.Format does not accept negative precisions.
+// BUG(gri) The Float.Format signature conflicts with Format(f fmt.State, c rune).
+// (https://github.com/golang/go/issues/10938)
+func (x *Float) Format(format byte, prec int) string {
+ const extra = 10 // TODO(gri) determine a good/better value here
+ return string(x.Append(make([]byte, 0, prec+extra), format, prec))
+}
-// bigFtoa formats a float for the %e, %E, %f, %g, and %G formats.
-func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte {
- if debugFloat && f.IsInf() {
- panic("non-finite float")
+// String formats x like x.Format('g', 10).
+func (x *Float) String() string {
+ return x.Format('g', 10)
+}
+
+// Append appends to buf the string form of the floating-point number x,
+// as generated by x.Format, and returns the extended buffer.
+func (x *Float) Append(buf []byte, fmt byte, prec int) []byte {
+ // sign
+ if x.neg {
+ buf = append(buf, '-')
}
+ // Inf
+ if x.form == inf {
+ if !x.neg {
+ buf = append(buf, '+')
+ }
+ return append(buf, "Inf"...)
+ }
+
+ // pick off easy formats
+ switch fmt {
+ case 'b':
+ return x.fmtB(buf)
+ case 'p':
+ return x.fmtP(buf)
+ }
+
+ // Algorithm:
+ // 1) convert Float to multiprecision decimal
+ // 2) round to desired precision
+ // 3) read digits out and format
+
// 1) convert Float to multiprecision decimal
- var mant nat
- if f.form == finite {
- mant = f.mant
+ var d decimal // == 0.0
+ if x.form == finite {
+ d.init(x.mant, int(x.exp)-x.mant.bitLen())
}
- var d decimal
- d.init(mant, int(f.exp)-f.mant.bitLen())
// 2) round to desired precision
shortest := false
@@ -67,9 +120,9 @@ func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte {
// 3) read digits out and format
switch fmt {
case 'e', 'E':
- return fmtE(buf, fmt, prec, f.neg, d)
+ return fmtE(buf, fmt, prec, d)
case 'f':
- return fmtF(buf, prec, f.neg, d)
+ return fmtF(buf, prec, d)
case 'g', 'G':
// trim trailing fractional zeros in %e format
eprec := prec
@@ -88,25 +141,23 @@ func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte {
if prec > len(d.mant) {
prec = len(d.mant)
}
- return fmtE(buf, fmt+'e'-'g', prec-1, f.neg, d)
+ return fmtE(buf, fmt+'e'-'g', prec-1, d)
}
if prec > d.exp {
prec = len(d.mant)
}
- return fmtF(buf, max(prec-d.exp, 0), f.neg, d)
+ return fmtF(buf, max(prec-d.exp, 0), d)
}
// unknown format
+ if x.neg {
+ buf = buf[:len(buf)-1] // sign was added prematurely - remove it again
+ }
return append(buf, '%', fmt)
}
-// %e: -d.ddddde±dd
-func fmtE(buf []byte, fmt byte, prec int, neg bool, d decimal) []byte {
- // sign
- if neg {
- buf = append(buf, '-')
- }
-
+// %e: d.ddddde±dd
+func fmtE(buf []byte, fmt byte, prec int, d decimal) []byte {
// first digit
ch := byte('0')
if len(d.mant) > 0 {
@@ -149,13 +200,8 @@ func fmtE(buf []byte, fmt byte, prec int, neg bool, d decimal) []byte {
return strconv.AppendInt(buf, exp, 10)
}
-// %f: -ddddddd.ddddd
-func fmtF(buf []byte, prec int, neg bool, d decimal) []byte {
- // sign
- if neg {
- buf = append(buf, '-')
- }
-
+// %f: ddddddd.ddddd
+func fmtF(buf []byte, prec int, d decimal) []byte {
// integer, padded with zeros as needed
if d.exp > 0 {
m := min(len(d.mant), d.exp)
@@ -182,6 +228,73 @@ func fmtF(buf []byte, prec int, neg bool, d decimal) []byte {
return buf
}
+// fmtB appends the string of x in the format mantissa "p" exponent
+// with a decimal mantissa and a binary exponent, or 0" if x is zero,
+// and returns the extended buffer.
+// The mantissa is normalized such that is uses x.Prec() bits in binary
+// representation.
+// The sign of x is ignored, and x must not be an Inf.
+func (x *Float) fmtB(buf []byte) []byte {
+ if x.form == zero {
+ return append(buf, '0')
+ }
+
+ if debugFloat && x.form != finite {
+ panic("non-finite float")
+ }
+ // x != 0
+
+ // adjust mantissa to use exactly x.prec bits
+ m := x.mant
+ switch w := uint32(len(x.mant)) * _W; {
+ case w < x.prec:
+ m = nat(nil).shl(m, uint(x.prec-w))
+ case w > x.prec:
+ m = nat(nil).shr(m, uint(w-x.prec))
+ }
+
+ buf = append(buf, m.decimalString()...)
+ buf = append(buf, 'p')
+ e := int64(x.exp) - int64(x.prec)
+ if e >= 0 {
+ buf = append(buf, '+')
+ }
+ return strconv.AppendInt(buf, e, 10)
+}
+
+// fmtP appends the string of x in the format 0x." mantissa "p" exponent
+// with a hexadecimal mantissa and a binary exponent, or 0" if x is zero,
+// ad returns the extended buffer.
+// The mantissa is normalized such that 0.5 <= 0.mantissa < 1.0.
+// The sign of x is ignored, and x must not be an Inf.
+func (x *Float) fmtP(buf []byte) []byte {
+ if x.form == zero {
+ return append(buf, '0')
+ }
+
+ if debugFloat && x.form != finite {
+ panic("non-finite float")
+ }
+ // x != 0
+
+ // remove trailing 0 words early
+ // (no need to convert to hex 0's and trim later)
+ m := x.mant
+ i := 0
+ for i < len(m) && m[i] == 0 {
+ i++
+ }
+ m = m[i:]
+
+ buf = append(buf, "0x."...)
+ buf = append(buf, strings.TrimRight(x.mant.hexString(), "0")...)
+ buf = append(buf, 'p')
+ if x.exp >= 0 {
+ buf = append(buf, '+')
+ }
+ return strconv.AppendInt(buf, int64(x.exp), 10)
+}
+
func min(x, y int) int {
if x < y {
return x
diff --git a/src/math/big/nat.go b/src/math/big/nat.go
index 7157a5487b..6545bc17ed 100644
--- a/src/math/big/nat.go
+++ b/src/math/big/nat.go
@@ -216,6 +216,34 @@ func basicMul(z, x, y nat) {
}
}
+// montgomery computes x*y*2^(-n*_W) mod m,
+// assuming k = -1/m mod 2^_W.
+// z is used for storing the result which is returned;
+// z must not alias x, y or m.
+func (z nat) montgomery(x, y, m nat, k Word, n int) nat {
+ var c1, c2 Word
+ z = z.make(n)
+ z.clear()
+ for i := 0; i < n; i++ {
+ d := y[i]
+ c1 += addMulVVW(z, x, d)
+ t := z[0] * k
+ c2 = addMulVVW(z, m, t)
+
+ copy(z, z[1:])
+ z[n-1] = c1 + c2
+ if z[n-1] < c1 {
+ c1 = 1
+ } else {
+ c1 = 0
+ }
+ }
+ if c1 != 0 {
+ subVV(z, z, m)
+ }
+ return z
+}
+
// Fast version of z[0:n+n>>1].add(z[0:n+n>>1], x[0:n]) w/o bounds checks.
// Factored out for readability - do not use outside karatsuba.
func karatsubaAdd(z, x nat, n int) {
@@ -544,7 +572,7 @@ func (z nat) divLarge(u, uIn, v nat) (q, r nat) {
u.clear() // TODO(gri) no need to clear if we allocated a new u
// D1.
- shift := leadingZeros(v[n-1])
+ shift := nlz(v[n-1])
if shift > 0 {
// do not modify v, it may be used by another goroutine simultaneously
v1 := make(nat, n)
@@ -905,13 +933,16 @@ func (z nat) expNN(x, y, m nat) nat {
// 4-bit, windowed exponentiation. This involves precomputing 14 values
// (x^2...x^15) but then reduces the number of multiply-reduces by a
// third. Even for a 32-bit exponent, this reduces the number of
- // operations.
+ // operations. Uses Montgomery method for odd moduli.
if len(x) > 1 && len(y) > 1 && len(m) > 0 {
+ if m[0]&1 == 1 {
+ return z.expNNMontgomery(x, y, m)
+ }
return z.expNNWindowed(x, y, m)
}
v := y[len(y)-1] // v > 0 because y is normalized and y > 0
- shift := leadingZeros(v) + 1
+ shift := nlz(v) + 1
v <<= shift
var q nat
@@ -1029,6 +1060,87 @@ func (z nat) expNNWindowed(x, y, m nat) nat {
return z.norm()
}
+// expNNMontgomery calculates x**y mod m using a fixed, 4-bit window.
+// Uses Montgomery representation.
+func (z nat) expNNMontgomery(x, y, m nat) nat {
+ var zz, one, rr, RR nat
+
+ numWords := len(m)
+
+ // We want the lengths of x and m to be equal.
+ if len(x) > numWords {
+ _, rr = rr.div(rr, x, m)
+ } else if len(x) < numWords {
+ rr = rr.make(numWords)
+ rr.clear()
+ for i := range x {
+ rr[i] = x[i]
+ }
+ } else {
+ rr = x
+ }
+ x = rr
+
+ // Ideally the precomputations would be performed outside, and reused
+ // k0 = -mˆ-1 mod 2ˆ_W. Algorithm from: Dumas, J.G. "On Newton–Raphson
+ // Iteration for Multiplicative Inverses Modulo Prime Powers".
+ k0 := 2 - m[0]
+ t := m[0] - 1
+ for i := 1; i < _W; i <<= 1 {
+ t *= t
+ k0 *= (t + 1)
+ }
+ k0 = -k0
+
+ // RR = 2ˆ(2*_W*len(m)) mod m
+ RR = RR.setWord(1)
+ zz = zz.shl(RR, uint(2*numWords*_W))
+ _, RR = RR.div(RR, zz, m)
+ if len(RR) < numWords {
+ zz = zz.make(numWords)
+ copy(zz, RR)
+ RR = zz
+ }
+ // one = 1, with equal length to that of m
+ one = one.make(numWords)
+ one.clear()
+ one[0] = 1
+
+ const n = 4
+ // powers[i] contains x^i
+ var powers [1 << n]nat
+ powers[0] = powers[0].montgomery(one, RR, m, k0, numWords)
+ powers[1] = powers[1].montgomery(x, RR, m, k0, numWords)
+ for i := 2; i < 1<= 0; i-- {
+ yi := y[i]
+ for j := 0; j < _W; j += n {
+ if i != len(y)-1 || j != 0 {
+ zz = zz.montgomery(z, z, m, k0, numWords)
+ z = z.montgomery(zz, zz, m, k0, numWords)
+ zz = zz.montgomery(z, z, m, k0, numWords)
+ z = z.montgomery(zz, zz, m, k0, numWords)
+ }
+ zz = zz.montgomery(z, powers[yi>>(_W-n)], m, k0, numWords)
+ z, zz = zz, z
+ yi <<= n
+ }
+ }
+ // convert to regular number
+ zz = zz.montgomery(z, one, m, k0, numWords)
+ return zz.norm()
+}
+
// probablyPrime performs reps Miller-Rabin tests to check whether n is prime.
// If it returns true, n is prime with probability 1 - 1/4^reps.
// If it returns false, n is not prime.
diff --git a/src/math/big/nat_test.go b/src/math/big/nat_test.go
index b25a89f731..7ac3cb8a84 100644
--- a/src/math/big/nat_test.go
+++ b/src/math/big/nat_test.go
@@ -205,11 +205,11 @@ func BenchmarkMul(b *testing.B) {
}
}
-func TestLeadingZeros(t *testing.T) {
+func TestNLZ(t *testing.T) {
var x Word = _B >> 1
for i := 0; i <= _W; i++ {
- if int(leadingZeros(x)) != i {
- t.Errorf("failed at %x: got %d want %d", x, leadingZeros(x), i)
+ if int(nlz(x)) != i {
+ t.Errorf("failed at %x: got %d want %d", x, nlz(x), i)
}
x >>= 1
}
@@ -332,6 +332,67 @@ func TestTrailingZeroBits(t *testing.T) {
}
}
+var montgomeryTests = []struct {
+ x, y, m string
+ k0 uint64
+ out32, out64 string
+}{
+ {
+ "0xffffffffffffffffffffffffffffffffffffffffffffffffe",
+ "0xffffffffffffffffffffffffffffffffffffffffffffffffe",
+ "0xfffffffffffffffffffffffffffffffffffffffffffffffff",
+ 0x0000000000000000,
+ "0xffffffffffffffffffffffffffffffffffffffffff",
+ "0xffffffffffffffffffffffffffffffffff",
+ },
+ {
+ "0x0000000080000000",
+ "0x00000000ffffffff",
+ "0x0000000010000001",
+ 0xff0000000fffffff,
+ "0x0000000088000000",
+ "0x0000000007800001",
+ },
+ {
+ "0xffffffffffffffffffffffffffffffff00000000000022222223333333333444444444",
+ "0xffffffffffffffffffffffffffffffff999999999999999aaabbbbbbbbcccccccccccc",
+ "0x33377fffffffffffffffffffffffffffffffffffffffffffff0000000000022222eee1",
+ 0xdecc8f1249812adf,
+ "0x22bb05b6d95eaaeca2bb7c05e51f807bce9064b5fbad177161695e4558f9474e91cd79",
+ "0x14beb58d230f85b6d95eaaeca2bb7c05e51f807bce9064b5fb45669afa695f228e48cd",
+ },
+ {
+ "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000022222223333333333444444444",
+ "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff999999999999999aaabbbbbbbbcccccccccccc",
+ "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff33377fffffffffffffffffffffffffffffffffffffffffffff0000000000022222eee1",
+ 0xdecc8f1249812adf,
+ "0x5c0d52f451aec609b15da8e5e5626c4eaa88723bdeac9d25ca9b961269400410ca208a16af9c2fb07d7a11c7772cba02c22f9711078d51a3797eb18e691295293284d988e349fa6deba46b25a4ecd9f715",
+ "0x92fcad4b5c0d52f451aec609b15da8e5e5626c4eaa88723bdeac9d25ca9b961269400410ca208a16af9c2fb07d799c32fe2f3cc5422f9711078d51a3797eb18e691295293284d8f5e69caf6decddfe1df6",
+ },
+}
+
+func TestMontgomery(t *testing.T) {
+ for i, test := range montgomeryTests {
+ x := natFromString(test.x)
+ y := natFromString(test.y)
+ m := natFromString(test.m)
+
+ var out nat
+ if _W == 32 {
+ out = natFromString(test.out32)
+ } else {
+ out = natFromString(test.out64)
+ }
+
+ k0 := Word(test.k0 & _M) // mask k0 to ensure that it fits for 32-bit systems.
+ z := nat(nil).montgomery(x, y, m, k0, len(m))
+ z = z.norm()
+ if z.cmp(out) != 0 {
+ t.Errorf("#%d got %s want %s", i, z.decimalString(), out.decimalString())
+ }
+ }
+}
+
var expNNTests = []struct {
x, y, m string
out string
diff --git a/src/math/rand/rand_test.go b/src/math/rand/rand_test.go
index ab0dc49b41..c61494f8eb 100644
--- a/src/math/rand/rand_test.go
+++ b/src/math/rand/rand_test.go
@@ -8,6 +8,8 @@ import (
"errors"
"fmt"
"math"
+ "os"
+ "runtime"
"testing"
)
@@ -322,10 +324,17 @@ func TestExpTables(t *testing.T) {
}
}
-// For issue 6721, the problem came after 7533753 calls, so check 10e6.
func TestFloat32(t *testing.T) {
+ // For issue 6721, the problem came after 7533753 calls, so check 10e6.
+ num := int(10e6)
+ // But ARM5 floating point emulation is slow (Issue 10749), so
+ // do less for that builder:
+ if testing.Short() && runtime.GOARCH == "arm" && os.Getenv("GOARM") == "5" {
+ num /= 100 // 1.72 seconds instead of 172 seconds
+ }
+
r := New(NewSource(1))
- for ct := 0; ct < 10e6; ct++ {
+ for ct := 0; ct < num; ct++ {
f := r.Float32()
if f >= 1 {
t.Fatal("Float32() should be in range [0,1). ct:", ct, "f:", f)
diff --git a/src/mime/encodedword.go b/src/mime/encodedword.go
new file mode 100644
index 0000000000..9796f506dc
--- /dev/null
+++ b/src/mime/encodedword.go
@@ -0,0 +1,329 @@
+// 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 mime
+
+import (
+ "bytes"
+ "encoding/base64"
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+ "sync"
+ "unicode"
+ "unicode/utf8"
+)
+
+// A WordEncoder is a RFC 2047 encoded-word encoder.
+type WordEncoder byte
+
+const (
+ // BEncoding represents Base64 encoding scheme as defined by RFC 2045.
+ BEncoding = WordEncoder('b')
+ // QEncoding represents the Q-encoding scheme as defined by RFC 2047.
+ QEncoding = WordEncoder('q')
+)
+
+var (
+ errInvalidWord = errors.New("mime: invalid RFC 2047 encoded-word")
+)
+
+// Encode returns the encoded-word form of s. If s is ASCII without special
+// characters, it is returned unchanged. The provided charset is the IANA
+// charset name of s. It is case insensitive.
+func (e WordEncoder) Encode(charset, s string) string {
+ if !needsEncoding(s) {
+ return s
+ }
+ return e.encodeWord(charset, s)
+}
+
+func needsEncoding(s string) bool {
+ for _, b := range s {
+ if (b < ' ' || b > '~') && b != '\t' {
+ return true
+ }
+ }
+ return false
+}
+
+// encodeWord encodes a string into an encoded-word.
+func (e WordEncoder) encodeWord(charset, s string) string {
+ buf := getBuffer()
+ defer putBuffer(buf)
+
+ buf.WriteString("=?")
+ buf.WriteString(charset)
+ buf.WriteByte('?')
+ buf.WriteByte(byte(e))
+ buf.WriteByte('?')
+
+ if e == BEncoding {
+ w := base64.NewEncoder(base64.StdEncoding, buf)
+ io.WriteString(w, s)
+ w.Close()
+ } else {
+ enc := make([]byte, 3)
+ for i := 0; i < len(s); i++ {
+ b := s[i]
+ switch {
+ case b == ' ':
+ buf.WriteByte('_')
+ case b <= '~' && b >= '!' && b != '=' && b != '?' && b != '_':
+ buf.WriteByte(b)
+ default:
+ enc[0] = '='
+ enc[1] = upperhex[b>>4]
+ enc[2] = upperhex[b&0x0f]
+ buf.Write(enc)
+ }
+ }
+ }
+ buf.WriteString("?=")
+ return buf.String()
+}
+
+const upperhex = "0123456789ABCDEF"
+
+// A WordDecoder decodes MIME headers containing RFC 2047 encoded-words.
+type WordDecoder struct {
+ // CharsetReader, if non-nil, defines a function to generate
+ // charset-conversion readers, converting from the provided
+ // charset into UTF-8.
+ // Charsets are always lower-case. utf-8, iso-8859-1 and us-ascii charsets
+ // are handled by default.
+ // One of the the CharsetReader's result values must be non-nil.
+ CharsetReader func(charset string, input io.Reader) (io.Reader, error)
+}
+
+// Decode decodes an encoded-word. If word is not a valid RFC 2047 encoded-word,
+// word is returned unchanged.
+func (d *WordDecoder) Decode(word string) (string, error) {
+ fields := strings.Split(word, "?") // TODO: remove allocation?
+ if len(fields) != 5 || fields[0] != "=" || fields[4] != "=" || len(fields[2]) != 1 {
+ return "", errInvalidWord
+ }
+
+ content, err := decode(fields[2][0], fields[3])
+ if err != nil {
+ return "", err
+ }
+
+ buf := getBuffer()
+ defer putBuffer(buf)
+
+ if err := d.convert(buf, fields[1], content); err != nil {
+ return "", err
+ }
+
+ return buf.String(), nil
+}
+
+// DecodeHeader decodes all encoded-words of the given string. It returns an
+// error if and only if CharsetReader of d returns an error.
+func (d *WordDecoder) DecodeHeader(header string) (string, error) {
+ // If there is no encoded-word, returns before creating a buffer.
+ i := strings.Index(header, "=?")
+ if i == -1 {
+ return header, nil
+ }
+
+ buf := getBuffer()
+ defer putBuffer(buf)
+
+ buf.WriteString(header[:i])
+ header = header[i:]
+
+ betweenWords := false
+ for {
+ start := strings.Index(header, "=?")
+ if start == -1 {
+ break
+ }
+ cur := start + len("=?")
+
+ i := strings.Index(header[cur:], "?")
+ if i == -1 {
+ break
+ }
+ charset := header[cur : cur+i]
+ cur += i + len("?")
+
+ if len(header) < cur+len("Q??=") {
+ break
+ }
+ encoding := header[cur]
+ cur++
+
+ if header[cur] != '?' {
+ break
+ }
+ cur++
+
+ j := strings.Index(header[cur:], "?=")
+ if j == -1 {
+ break
+ }
+ text := header[cur : cur+j]
+ end := cur + j + len("?=")
+
+ content, err := decode(encoding, text)
+ if err != nil {
+ betweenWords = false
+ buf.WriteString(header[:start+2])
+ header = header[start+2:]
+ continue
+ }
+
+ // Write characters before the encoded-word. White-space and newline
+ // characters separating two encoded-words must be deleted.
+ if start > 0 && (!betweenWords || hasNonWhitespace(header[:start])) {
+ buf.WriteString(header[:start])
+ }
+
+ if err := d.convert(buf, charset, content); err != nil {
+ return "", err
+ }
+
+ header = header[end:]
+ betweenWords = true
+ }
+
+ if len(header) > 0 {
+ buf.WriteString(header)
+ }
+
+ return buf.String(), nil
+}
+
+func decode(encoding byte, text string) ([]byte, error) {
+ switch encoding {
+ case 'B', 'b':
+ return base64.StdEncoding.DecodeString(text)
+ case 'Q', 'q':
+ return qDecode(text)
+ default:
+ return nil, errInvalidWord
+ }
+}
+
+func (d *WordDecoder) convert(buf *bytes.Buffer, charset string, content []byte) error {
+ switch {
+ case strings.EqualFold("utf-8", charset):
+ buf.Write(content)
+ case strings.EqualFold("iso-8859-1", charset):
+ for _, c := range content {
+ buf.WriteRune(rune(c))
+ }
+ case strings.EqualFold("us-ascii", charset):
+ for _, c := range content {
+ if c >= utf8.RuneSelf {
+ buf.WriteRune(unicode.ReplacementChar)
+ } else {
+ buf.WriteByte(c)
+ }
+ }
+ default:
+ if d.CharsetReader == nil {
+ return fmt.Errorf("mime: unhandled charset %q", charset)
+ }
+ r, err := d.CharsetReader(strings.ToLower(charset), bytes.NewReader(content))
+ if err != nil {
+ return err
+ }
+ if _, err = buf.ReadFrom(r); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// hasNonWhitespace reports whether s (assumed to be ASCII) contains at least
+// one byte of non-whitespace.
+func hasNonWhitespace(s string) bool {
+ for _, b := range s {
+ switch b {
+ // Encoded-words can only be separated by linear white spaces which does
+ // not include vertical tabs (\v).
+ case ' ', '\t', '\n', '\r':
+ default:
+ return true
+ }
+ }
+ return false
+}
+
+// qDecode decodes a Q encoded string.
+func qDecode(s string) ([]byte, error) {
+ dec := make([]byte, len(s))
+ n := 0
+ for i := 0; i < len(s); i++ {
+ switch c := s[i]; {
+ case c == '_':
+ dec[n] = ' '
+ case c == '=':
+ if i+2 >= len(s) {
+ return nil, errInvalidWord
+ }
+ b, err := readHexByte(s[i+1], s[i+2])
+ if err != nil {
+ return nil, err
+ }
+ dec[n] = b
+ i += 2
+ case (c <= '~' && c >= ' ') || c == '\n' || c == '\r' || c == '\t':
+ dec[n] = c
+ default:
+ return nil, errInvalidWord
+ }
+ n++
+ }
+
+ return dec[:n], nil
+}
+
+// readHexByte returns the byte from its quoted-printable representation.
+func readHexByte(a, b byte) (byte, error) {
+ var hb, lb byte
+ var err error
+ if hb, err = fromHex(a); err != nil {
+ return 0, err
+ }
+ if lb, err = fromHex(b); err != nil {
+ return 0, err
+ }
+ return hb<<4 | lb, nil
+}
+
+func fromHex(b byte) (byte, error) {
+ switch {
+ case b >= '0' && b <= '9':
+ return b - '0', nil
+ case b >= 'A' && b <= 'F':
+ return b - 'A' + 10, nil
+ // Accept badly encoded bytes.
+ case b >= 'a' && b <= 'f':
+ return b - 'a' + 10, nil
+ }
+ return 0, fmt.Errorf("mime: invalid hex byte %#02x", b)
+}
+
+var bufPool = sync.Pool{
+ New: func() interface{} {
+ return new(bytes.Buffer)
+ },
+}
+
+func getBuffer() *bytes.Buffer {
+ return bufPool.Get().(*bytes.Buffer)
+}
+
+func putBuffer(buf *bytes.Buffer) {
+ if buf.Len() > 1024 {
+ return
+ }
+ buf.Reset()
+ bufPool.Put(buf)
+}
diff --git a/src/mime/encodedword_test.go b/src/mime/encodedword_test.go
new file mode 100644
index 0000000000..02236ea521
--- /dev/null
+++ b/src/mime/encodedword_test.go
@@ -0,0 +1,241 @@
+// 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 mime
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "strings"
+ "testing"
+)
+
+func ExampleEncodeWord() {
+ fmt.Println(QEncoding.Encode("utf-8", "¡Hola, señor!"))
+ fmt.Println(QEncoding.Encode("utf-8", "Hello!"))
+ fmt.Println(BEncoding.Encode("UTF-8", "¡Hola, señor!"))
+ fmt.Println(QEncoding.Encode("ISO-8859-1", "Caf\xE9"))
+ // Output:
+ // =?utf-8?q?=C2=A1Hola,_se=C3=B1or!?=
+ // Hello!
+ // =?UTF-8?b?wqFIb2xhLCBzZcOxb3Ih?=
+ // =?ISO-8859-1?q?Caf=E9?=
+}
+
+func ExampleDecodeWord() {
+ dec := new(WordDecoder)
+ header, err := dec.DecodeHeader("=?utf-8?q?=C2=A1Hola,_se=C3=B1or!?=")
+ if err != nil {
+ panic(err)
+ }
+ fmt.Println(header)
+ // Output: ¡Hola, señor!
+}
+
+func ExampleDecodeHeader() {
+ dec := new(WordDecoder)
+ header, err := dec.DecodeHeader("=?utf-8?q?=C3=89ric?= , =?utf-8?q?Ana=C3=AFs?= ")
+ if err != nil {
+ panic(err)
+ }
+ fmt.Println(header)
+
+ header, err = dec.DecodeHeader("=?utf-8?q?=C2=A1Hola,?= =?utf-8?q?_se=C3=B1or!?=")
+ if err != nil {
+ panic(err)
+ }
+ fmt.Println(header)
+ // Output:
+ // Éric , Anaïs
+ // ¡Hola, señor!
+}
+
+func TestEncodeWord(t *testing.T) {
+ utf8, iso88591 := "utf-8", "iso-8859-1"
+ tests := []struct {
+ enc WordEncoder
+ charset string
+ src, exp string
+ }{
+ {QEncoding, utf8, "François-Jérôme", "=?utf-8?q?Fran=C3=A7ois-J=C3=A9r=C3=B4me?="},
+ {BEncoding, utf8, "Café", "=?utf-8?b?Q2Fmw6k=?="},
+ {QEncoding, iso88591, "La Seleção", "=?iso-8859-1?q?La_Sele=C3=A7=C3=A3o?="},
+ {QEncoding, utf8, "", ""},
+ {QEncoding, utf8, "A", "A"},
+ {QEncoding, iso88591, "a", "a"},
+ {QEncoding, utf8, "123 456", "123 456"},
+ {QEncoding, utf8, "\t !\"#$%&'()*+,-./ :;<>?@[\\]^_`{|}~", "\t !\"#$%&'()*+,-./ :;<>?@[\\]^_`{|}~"},
+ }
+
+ for _, test := range tests {
+ if s := test.enc.Encode(test.charset, test.src); s != test.exp {
+ t.Errorf("Encode(%q) = %q, want %q", test.src, s, test.exp)
+ }
+ }
+}
+
+func TestDecodeWord(t *testing.T) {
+ tests := []struct {
+ src, exp string
+ hasErr bool
+ }{
+ {"=?UTF-8?Q?=C2=A1Hola,_se=C3=B1or!?=", "¡Hola, señor!", false},
+ {"=?UTF-8?Q?Fran=C3=A7ois-J=C3=A9r=C3=B4me?=", "François-Jérôme", false},
+ {"=?UTF-8?q?ascii?=", "ascii", false},
+ {"=?utf-8?B?QW5kcsOp?=", "André", false},
+ {"=?ISO-8859-1?Q?Rapha=EBl_Dupont?=", "Raphaël Dupont", false},
+ {"=?utf-8?b?IkFudG9uaW8gSm9zw6kiIDxqb3NlQGV4YW1wbGUub3JnPg==?=", `"Antonio José" `, false},
+ {"=?UTF-8?A?Test?=", "", true},
+ {"=?UTF-8?Q?A=B?=", "", true},
+ {"=?UTF-8?Q?=A?=", "", true},
+ {"=?UTF-8?A?A?=", "", true},
+ }
+
+ for _, test := range tests {
+ dec := new(WordDecoder)
+ s, err := dec.Decode(test.src)
+ if test.hasErr && err == nil {
+ t.Errorf("Decode(%q) should return an error", test.src)
+ continue
+ }
+ if !test.hasErr && err != nil {
+ t.Errorf("Decode(%q): %v", test.src, err)
+ continue
+ }
+ if s != test.exp {
+ t.Errorf("Decode(%q) = %q, want %q", test.src, s, test.exp)
+ }
+ }
+}
+
+func TestDecodeHeader(t *testing.T) {
+ tests := []struct {
+ src, exp string
+ }{
+ {"=?UTF-8?Q?=C2=A1Hola,_se=C3=B1or!?=", "¡Hola, señor!"},
+ {"=?UTF-8?Q?Fran=C3=A7ois-J=C3=A9r=C3=B4me?=", "François-Jérôme"},
+ {"=?UTF-8?q?ascii?=", "ascii"},
+ {"=?utf-8?B?QW5kcsOp?=", "André"},
+ {"=?ISO-8859-1?Q?Rapha=EBl_Dupont?=", "Raphaël Dupont"},
+ {"Jean", "Jean"},
+ {"=?utf-8?b?IkFudG9uaW8gSm9zw6kiIDxqb3NlQGV4YW1wbGUub3JnPg==?=", `"Antonio José" `},
+ {"=?UTF-8?A?Test?=", "=?UTF-8?A?Test?="},
+ {"=?UTF-8?Q?A=B?=", "=?UTF-8?Q?A=B?="},
+ {"=?UTF-8?Q?=A?=", "=?UTF-8?Q?=A?="},
+ {"=?UTF-8?A?A?=", "=?UTF-8?A?A?="},
+ // Incomplete words
+ {"=?", "=?"},
+ {"=?UTF-8?", "=?UTF-8?"},
+ {"=?UTF-8?=", "=?UTF-8?="},
+ {"=?UTF-8?Q", "=?UTF-8?Q"},
+ {"=?UTF-8?Q?", "=?UTF-8?Q?"},
+ {"=?UTF-8?Q?=", "=?UTF-8?Q?="},
+ {"=?UTF-8?Q?A", "=?UTF-8?Q?A"},
+ {"=?UTF-8?Q?A?", "=?UTF-8?Q?A?"},
+ // Tests from RFC 2047
+ {"=?ISO-8859-1?Q?a?=", "a"},
+ {"=?ISO-8859-1?Q?a?= b", "a b"},
+ {"=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=", "ab"},
+ {"=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=", "ab"},
+ {"=?ISO-8859-1?Q?a?= \r\n\t =?ISO-8859-1?Q?b?=", "ab"},
+ {"=?ISO-8859-1?Q?a_b?=", "a b"},
+ }
+
+ for _, test := range tests {
+ dec := new(WordDecoder)
+ s, err := dec.DecodeHeader(test.src)
+ if err != nil {
+ t.Errorf("DecodeHeader(%q): %v", test.src, err)
+ }
+ if s != test.exp {
+ t.Errorf("DecodeHeader(%q) = %q, want %q", test.src, s, test.exp)
+ }
+ }
+}
+
+func TestCharsetDecoder(t *testing.T) {
+ tests := []struct {
+ src string
+ want string
+ charsets []string
+ content []string
+ }{
+ {"=?utf-8?b?Q2Fmw6k=?=", "Café", nil, nil},
+ {"=?ISO-8859-1?Q?caf=E9?=", "café", nil, nil},
+ {"=?US-ASCII?Q?foo_bar?=", "foo bar", nil, nil},
+ {"=?utf-8?Q?=?=", "=?utf-8?Q?=?=", nil, nil},
+ {"=?utf-8?Q?=A?=", "=?utf-8?Q?=A?=", nil, nil},
+ {
+ "=?ISO-8859-15?Q?f=F5=F6?= =?windows-1252?Q?b=E0r?=",
+ "f\xf5\xf6b\xe0r",
+ []string{"iso-8859-15", "windows-1252"},
+ []string{"f\xf5\xf6", "b\xe0r"},
+ },
+ }
+
+ for _, test := range tests {
+ i := 0
+ dec := &WordDecoder{
+ CharsetReader: func(charset string, input io.Reader) (io.Reader, error) {
+ if charset != test.charsets[i] {
+ t.Errorf("DecodeHeader(%q), got charset %q, want %q", test.src, charset, test.charsets[i])
+ }
+ content, err := ioutil.ReadAll(input)
+ if err != nil {
+ t.Errorf("DecodeHeader(%q), error in reader: %v", test.src, err)
+ }
+ got := string(content)
+ if got != test.content[i] {
+ t.Errorf("DecodeHeader(%q), got content %q, want %q", test.src, got, test.content[i])
+ }
+ i++
+
+ return strings.NewReader(got), nil
+ },
+ }
+ got, err := dec.DecodeHeader(test.src)
+ if err != nil {
+ t.Errorf("DecodeHeader(%q): %v", test.src, err)
+ }
+ if got != test.want {
+ t.Errorf("DecodeHeader(%q) = %q, want %q", test.src, got, test.want)
+ }
+ }
+}
+
+func TestCharsetDecoderError(t *testing.T) {
+ dec := &WordDecoder{
+ CharsetReader: func(charset string, input io.Reader) (io.Reader, error) {
+ return nil, errors.New("Test error")
+ },
+ }
+
+ if _, err := dec.DecodeHeader("=?charset?Q?foo?="); err == nil {
+ t.Error("DecodeHeader should return an error")
+ }
+}
+
+func BenchmarkQEncodeWord(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ QEncoding.Encode("UTF-8", "¡Hola, señor!")
+ }
+}
+
+func BenchmarkQDecodeWord(b *testing.B) {
+ dec := new(WordDecoder)
+
+ for i := 0; i < b.N; i++ {
+ dec.Decode("=?utf-8?q?=C2=A1Hola,_se=C3=B1or!?=")
+ }
+}
+
+func BenchmarkQDecodeHeader(b *testing.B) {
+ dec := new(WordDecoder)
+
+ for i := 0; i < b.N; i++ {
+ dec.Decode("=?utf-8?q?=C2=A1Hola,_se=C3=B1or!?=")
+ }
+}
diff --git a/src/net/cgo_unix_test.go b/src/net/cgo_unix_test.go
index 55ea86a458..4d5ab23fd3 100644
--- a/src/net/cgo_unix_test.go
+++ b/src/net/cgo_unix_test.go
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// +build cgo,!netgo
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package net
diff --git a/src/net/dnsclient_unix.go b/src/net/dnsclient_unix.go
index 5a4411f5c7..fab515f5c2 100644
--- a/src/net/dnsclient_unix.go
+++ b/src/net/dnsclient_unix.go
@@ -214,95 +214,106 @@ func convertRR_AAAA(records []dnsRR) []IP {
return addrs
}
+// cfg is used for the storage and reparsing of /etc/resolv.conf
var cfg struct {
- ch chan struct{}
+ // ch is used as a semaphore that only allows one lookup at a time to
+ // recheck resolv.conf. It acts as guard for lastChecked and modTime.
+ ch chan struct{}
+ lastChecked time.Time // last time resolv.conf was checked
+ modTime time.Time // time of resolv.conf modification
+
mu sync.RWMutex // protects dnsConfig
- dnsConfig *dnsConfig
+ dnsConfig *dnsConfig // parsed resolv.conf structure used in lookups
}
var onceLoadConfig sync.Once
-// Assume dns config file is /etc/resolv.conf here
-func loadDefaultConfig() {
- loadConfig("/etc/resolv.conf", 5*time.Second, nil)
-}
-
-func loadConfig(resolvConfPath string, reloadTime time.Duration, quit <-chan chan struct{}) {
- var mtime time.Time
- cfg.ch = make(chan struct{}, 1)
- if fi, err := os.Stat(resolvConfPath); err == nil {
- mtime = fi.ModTime()
+func initCfg() {
+ // Set dnsConfig, modTime, and lastChecked so we don't parse
+ // resolv.conf twice the first time.
+ cfg.dnsConfig = systemConf().resolv
+ if cfg.dnsConfig == nil {
+ cfg.dnsConfig = dnsReadConfig("/etc/resolv.conf")
}
- cfg.dnsConfig = dnsReadConfig(resolvConfPath)
+ if fi, err := os.Stat("/etc/resolv.conf"); err == nil {
+ cfg.modTime = fi.ModTime()
+ }
+ cfg.lastChecked = time.Now()
- go func() {
- for {
- time.Sleep(reloadTime)
- select {
- case qresp := <-quit:
- qresp <- struct{}{}
- return
- case <-cfg.ch:
- }
+ // Prepare ch so that only one loadConfig may run at once
+ cfg.ch = make(chan struct{}, 1)
+ cfg.ch <- struct{}{}
+}
- // In case of error, we keep the previous config
- fi, err := os.Stat(resolvConfPath)
- if err != nil {
- continue
- }
- // If the resolv.conf mtime didn't change, do not reload
- m := fi.ModTime()
- if m.Equal(mtime) {
- continue
- }
- mtime = m
- // In case of error, we keep the previous config
- if ncfg := dnsReadConfig(resolvConfPath); ncfg.err == nil {
- cfg.mu.Lock()
- cfg.dnsConfig = ncfg
- cfg.mu.Unlock()
- }
+func loadConfig(resolvConfPath string) {
+ onceLoadConfig.Do(initCfg)
+
+ // ensure only one loadConfig at a time checks /etc/resolv.conf
+ select {
+ case <-cfg.ch:
+ defer func() { cfg.ch <- struct{}{} }()
+ default:
+ return
+ }
+
+ now := time.Now()
+ if cfg.lastChecked.After(now.Add(-5 * time.Second)) {
+ return
+ }
+ cfg.lastChecked = now
+
+ if fi, err := os.Stat(resolvConfPath); err == nil {
+ if fi.ModTime().Equal(cfg.modTime) {
+ return
}
- }()
+ cfg.modTime = fi.ModTime()
+ } else {
+ // If modTime wasn't set prior, assume nothing has changed.
+ if cfg.modTime.IsZero() {
+ return
+ }
+ cfg.modTime = time.Time{}
+ }
+
+ ncfg := dnsReadConfig(resolvConfPath)
+ cfg.mu.Lock()
+ cfg.dnsConfig = ncfg
+ cfg.mu.Unlock()
}
func lookup(name string, qtype uint16) (cname string, rrs []dnsRR, err error) {
if !isDomainName(name) {
return name, nil, &DNSError{Err: "invalid domain name", Name: name}
}
- onceLoadConfig.Do(loadDefaultConfig)
-
- select {
- case cfg.ch <- struct{}{}:
- default:
- }
+ loadConfig("/etc/resolv.conf")
cfg.mu.RLock()
- defer cfg.mu.RUnlock()
+ resolv := cfg.dnsConfig
+ cfg.mu.RUnlock()
// If name is rooted (trailing dot) or has enough dots,
// try it by itself first.
rooted := len(name) > 0 && name[len(name)-1] == '.'
- if rooted || count(name, '.') >= cfg.dnsConfig.ndots {
+ if rooted || count(name, '.') >= resolv.ndots {
rname := name
if !rooted {
rname += "."
}
// Can try as ordinary name.
- cname, rrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
+ cname, rrs, err = tryOneName(resolv, rname, qtype)
if rooted || err == nil {
return
}
}
// Otherwise, try suffixes.
- for i := 0; i < len(cfg.dnsConfig.search); i++ {
- rname := name + "." + cfg.dnsConfig.search[i]
+ for _, suffix := range resolv.search {
+ rname := name + "." + suffix
if rname[len(rname)-1] != '.' {
rname += "."
}
- cname, rrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
+ cname, rrs, err = tryOneName(resolv, rname, qtype)
if err == nil {
return
}
@@ -310,8 +321,8 @@ func lookup(name string, qtype uint16) (cname string, rrs []dnsRR, err error) {
// Last ditch effort: try unsuffixed only if we haven't already,
// that is, name is not rooted and has less than ndots dots.
- if count(name, '.') < cfg.dnsConfig.ndots {
- cname, rrs, err = tryOneName(cfg.dnsConfig, name+".", qtype)
+ if count(name, '.') < resolv.ndots {
+ cname, rrs, err = tryOneName(resolv, name+".", qtype)
if err == nil {
return
}
diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go
index 1b88e7762b..06c9ad3134 100644
--- a/src/net/dnsclient_unix_test.go
+++ b/src/net/dnsclient_unix_test.go
@@ -94,10 +94,8 @@ func TestSpecialDomainName(t *testing.T) {
type resolvConfTest struct {
*testing.T
- dir string
- path string
- started bool
- quitc chan chan struct{}
+ dir string
+ path string
}
func newResolvConfTest(t *testing.T) *resolvConfTest {
@@ -106,24 +104,15 @@ func newResolvConfTest(t *testing.T) *resolvConfTest {
t.Fatal(err)
}
- // Disable the default loadConfig
- onceLoadConfig.Do(func() {})
-
r := &resolvConfTest{
- T: t,
- dir: dir,
- path: path.Join(dir, "resolv.conf"),
- quitc: make(chan chan struct{}),
+ T: t,
+ dir: dir,
+ path: path.Join(dir, "resolv.conf"),
}
return r
}
-func (r *resolvConfTest) Start() {
- loadConfig(r.path, 100*time.Millisecond, r.quitc)
- r.started = true
-}
-
func (r *resolvConfTest) SetConf(s string) {
// Make sure the file mtime will be different once we're done here,
// even on systems with coarse (1s) mtime resolution.
@@ -138,12 +127,8 @@ func (r *resolvConfTest) SetConf(s string) {
r.Fatalf("failed to write temp file: %v", err)
}
f.Close()
-
- if r.started {
- cfg.ch <- struct{}{} // fill buffer
- cfg.ch <- struct{}{} // wait for reload to begin
- cfg.ch <- struct{}{} // wait for reload to complete
- }
+ cfg.lastChecked = time.Time{}
+ loadConfig(r.path)
}
func (r *resolvConfTest) WantServers(want []string) {
@@ -155,9 +140,6 @@ func (r *resolvConfTest) WantServers(want []string) {
}
func (r *resolvConfTest) Close() {
- resp := make(chan struct{})
- r.quitc <- resp
- <-resp
if err := os.RemoveAll(r.dir); err != nil {
r.Logf("failed to remove temp dir %s: %v", r.dir, err)
}
@@ -171,7 +153,6 @@ func TestReloadResolvConfFail(t *testing.T) {
r := newResolvConfTest(t)
defer r.Close()
- r.Start()
r.SetConf("nameserver 8.8.8.8")
if _, err := goLookupIP("golang.org"); err != nil {
@@ -200,7 +181,6 @@ func TestReloadResolvConfChange(t *testing.T) {
r := newResolvConfTest(t)
defer r.Close()
- r.Start()
r.SetConf("nameserver 8.8.8.8")
if _, err := goLookupIP("golang.org"); err != nil {
@@ -227,7 +207,7 @@ func TestReloadResolvConfChange(t *testing.T) {
}
func BenchmarkGoLookupIP(b *testing.B) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
+ testHookUninstaller.Do(uninstallTestHooks)
for i := 0; i < b.N; i++ {
goLookupIP("www.example.com")
@@ -235,7 +215,7 @@ func BenchmarkGoLookupIP(b *testing.B) {
}
func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
+ testHookUninstaller.Do(uninstallTestHooks)
for i := 0; i < b.N; i++ {
goLookupIP("some.nonexistent")
@@ -243,16 +223,16 @@ func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
}
func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
-
- onceLoadConfig.Do(loadDefaultConfig)
+ testHookUninstaller.Do(uninstallTestHooks)
// This looks ugly but it's safe as long as benchmarks are run
// sequentially in package testing.
+ <-cfg.ch // keep config from being reloaded upon lookup
orig := cfg.dnsConfig
cfg.dnsConfig.servers = append([]string{"203.0.113.254"}, cfg.dnsConfig.servers...) // use TEST-NET-3 block, see RFC 5737
for i := 0; i < b.N; i++ {
goLookupIP("www.example.com")
}
cfg.dnsConfig = orig
+ cfg.ch <- struct{}{}
}
diff --git a/src/net/dnsname_test.go b/src/net/dnsname_test.go
index cc660c9d42..be07dc6a16 100644
--- a/src/net/dnsname_test.go
+++ b/src/net/dnsname_test.go
@@ -68,7 +68,7 @@ func TestDNSName(t *testing.T) {
}
func BenchmarkDNSName(b *testing.B) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
+ testHookUninstaller.Do(uninstallTestHooks)
benchmarks := append(dnsNameTests, []dnsNameTest{
{strings.Repeat("a", 63), true},
diff --git a/src/net/error_test.go b/src/net/error_test.go
index c65d3f9d8a..772e0c7f5f 100644
--- a/src/net/error_test.go
+++ b/src/net/error_test.go
@@ -521,7 +521,7 @@ third:
func TestFileError(t *testing.T) {
switch runtime.GOOS {
case "windows":
- t.Skip("not supported on %s", runtime.GOOS)
+ t.Skipf("not supported on %s", runtime.GOOS)
}
f, err := ioutil.TempFile("", "go-nettest")
diff --git a/src/net/external_test.go b/src/net/external_test.go
index 20611ff420..d5ff2be20a 100644
--- a/src/net/external_test.go
+++ b/src/net/external_test.go
@@ -15,33 +15,23 @@ func TestResolveGoogle(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("avoid external network")
}
- if !supportsIPv4 && !supportsIPv6 {
- t.Skip("ipv4 and ipv6 are not supported")
+ if !supportsIPv4 || !supportsIPv6 || !*testIPv4 || !*testIPv6 {
+ t.Skip("both IPv4 and IPv6 are required")
}
for _, network := range []string{"tcp", "tcp4", "tcp6"} {
addr, err := ResolveTCPAddr(network, "www.google.com:http")
if err != nil {
- switch {
- case network == "tcp" && !supportsIPv4:
- fallthrough
- case network == "tcp4" && !supportsIPv4:
- t.Logf("skipping test; ipv4 is not supported: %v", err)
- case network == "tcp6" && !supportsIPv6:
- t.Logf("skipping test; ipv6 is not supported: %v", err)
- default:
- t.Error(err)
- }
+ t.Error(err)
continue
}
-
switch {
case network == "tcp" && addr.IP.To4() == nil:
fallthrough
case network == "tcp4" && addr.IP.To4() == nil:
- t.Errorf("got %v; want an ipv4 address on %s", addr, network)
+ t.Errorf("got %v; want an IPv4 address on %s", addr, network)
case network == "tcp6" && (addr.IP.To16() == nil || addr.IP.To4() != nil):
- t.Errorf("got %v; want an ipv6 address on %s", addr, network)
+ t.Errorf("got %v; want an IPv6 address on %s", addr, network)
}
}
}
@@ -73,8 +63,8 @@ func TestDialGoogle(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("avoid external network")
}
- if !supportsIPv4 && !supportsIPv6 {
- t.Skip("ipv4 and ipv6 are not supported")
+ if !supportsIPv4 || !supportsIPv6 || !*testIPv4 || !*testIPv6 {
+ t.Skip("both IPv4 and IPv6 are required")
}
var err error
@@ -84,25 +74,6 @@ func TestDialGoogle(t *testing.T) {
}
for _, tt := range dialGoogleTests {
for _, network := range tt.networks {
- switch {
- case network == "tcp4" && !supportsIPv4:
- t.Log("skipping test; ipv4 is not supported")
- continue
- case network == "tcp4" && !*testIPv4:
- fallthrough
- case tt.unreachableNetwork == "tcp6" && !*testIPv4:
- t.Log("disabled; use -ipv4 to enable")
- continue
- case network == "tcp6" && !supportsIPv6:
- t.Log("skipping test; ipv6 is not supported")
- continue
- case network == "tcp6" && !*testIPv6:
- fallthrough
- case tt.unreachableNetwork == "tcp4" && !*testIPv6:
- t.Log("disabled; use -ipv6 to enable")
- continue
- }
-
disableSocketConnect(tt.unreachableNetwork)
for _, addr := range tt.addrs {
if err := fetchGoogle(tt.dial, network, addr); err != nil {
diff --git a/src/net/fd_unix.go b/src/net/fd_unix.go
index 64e94fecf8..827045b13d 100644
--- a/src/net/fd_unix.go
+++ b/src/net/fd_unix.go
@@ -7,6 +7,7 @@
package net
import (
+ "internal/syscall/unix"
"io"
"os"
"runtime"
@@ -225,7 +226,7 @@ func (fd *netFD) Read(p []byte) (n int, err error) {
return 0, err
}
for {
- n, err = syscall.Read(int(fd.sysfd), p)
+ n, err = syscall.Read(fd.sysfd, p)
if err != nil {
n = 0
if err == syscall.EAGAIN {
@@ -270,6 +271,33 @@ func (fd *netFD) readFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
return
}
+func (fd *netFD) recvFrom(b []byte, flags int, from []byte) (n int, err error) {
+ if err := fd.readLock(); err != nil {
+ return 0, err
+ }
+ defer fd.readUnlock()
+ if err := fd.pd.PrepareRead(); err != nil {
+ return 0, err
+ }
+ for {
+ n, err = unix.Recvfrom(fd.sysfd, b, flags, from)
+ if err != nil {
+ n = 0
+ if err == syscall.EAGAIN {
+ if err = fd.pd.WaitRead(); err == nil {
+ continue
+ }
+ }
+ }
+ err = fd.eofError(n, err)
+ break
+ }
+ if _, ok := err.(syscall.Errno); ok {
+ err = os.NewSyscallError("recvfrom", err)
+ }
+ return
+}
+
func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
if err := fd.readLock(); err != nil {
return 0, 0, 0, nil, err
@@ -307,7 +335,7 @@ func (fd *netFD) Write(p []byte) (nn int, err error) {
}
for {
var n int
- n, err = syscall.Write(int(fd.sysfd), p[nn:])
+ n, err = syscall.Write(fd.sysfd, p[nn:])
if n > 0 {
nn += n
}
@@ -359,6 +387,29 @@ func (fd *netFD) writeTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
return
}
+func (fd *netFD) sendTo(b []byte, flags int, to []byte) (n int, err error) {
+ if err := fd.writeLock(); err != nil {
+ return 0, err
+ }
+ defer fd.writeUnlock()
+ if err := fd.pd.PrepareWrite(); err != nil {
+ return 0, err
+ }
+ for {
+ n, err = unix.Sendto(fd.sysfd, b, flags, to)
+ if err == syscall.EAGAIN {
+ if err = fd.pd.WaitWrite(); err == nil {
+ continue
+ }
+ }
+ break
+ }
+ if _, ok := err.(syscall.Errno); ok {
+ err = os.NewSyscallError("sendto", err)
+ }
+ return
+}
+
func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
if err := fd.writeLock(); err != nil {
return 0, 0, err
diff --git a/src/net/file.go b/src/net/file.go
index 1aad477400..1d0686c9fc 100644
--- a/src/net/file.go
+++ b/src/net/file.go
@@ -46,3 +46,47 @@ func FilePacketConn(f *os.File) (c PacketConn, err error) {
}
return
}
+
+// A SocketAddr is used with SocketConn or SocketPacketConn to
+// implement a user-configured socket address.
+// The net package does not provide any implementations of SocketAddr;
+// the caller of SocketConn or SocketPacketConn is expected to provide
+// one.
+type SocketAddr interface {
+ // Addr takes a platform-specific socket address and returns
+ // a net.Addr. The result may be nil when the syscall package,
+ // system call or underlying protocol does not support the
+ // socket address.
+ Addr([]byte) Addr
+
+ // Raw takes a net.Addr and returns a platform-specific socket
+ // address. The result may be nil when the syscall package,
+ // system call or underlying protocol does not support the
+ // socket address.
+ Raw(Addr) []byte
+}
+
+// SocketConn returns a copy of the network connection corresponding
+// to the open file f and user-defined socket address sa.
+// It is the caller's responsibility to close f when finished.
+// Closing c does not affect f, and closing f does not affect c.
+func SocketConn(f *os.File, sa SocketAddr) (c Conn, err error) {
+ c, err = socketConn(f, sa)
+ if err != nil {
+ err = &OpError{Op: "file", Net: "file+net", Source: nil, Addr: fileAddr(f.Name()), Err: err}
+ }
+ return
+}
+
+// SocketPacketConn returns a copy of the packet network connection
+// corresponding to the open file f and user-defined socket address
+// sa.
+// It is the caller's responsibility to close f when finished.
+// Closing c does not affect f, and closing f does not affect c.
+func SocketPacketConn(f *os.File, sa SocketAddr) (c PacketConn, err error) {
+ c, err = socketPacketConn(f, sa)
+ if err != nil {
+ err = &OpError{Op: "file", Net: "file+net", Source: nil, Addr: fileAddr(f.Name()), Err: err}
+ }
+ return
+}
diff --git a/src/net/file_bsd_test.go b/src/net/file_bsd_test.go
new file mode 100644
index 0000000000..ffe3c612b4
--- /dev/null
+++ b/src/net/file_bsd_test.go
@@ -0,0 +1,95 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package net
+
+import (
+ "os"
+ "runtime"
+ "strings"
+ "sync"
+ "syscall"
+ "testing"
+ "unsafe"
+)
+
+type routeAddr struct{}
+
+func (a *routeAddr) Network() string { return "route" }
+func (a *routeAddr) String() string { return "" }
+
+func (a *routeAddr) Addr(rsa []byte) Addr { return &routeAddr{} }
+func (a *routeAddr) Raw(addr Addr) []byte { return nil }
+
+func TestSocketConn(t *testing.T) {
+ var freebsd32o64 bool
+ if runtime.GOOS == "freebsd" && runtime.GOARCH == "386" {
+ archs, _ := syscall.Sysctl("kern.supported_archs")
+ for _, s := range strings.Split(archs, " ") {
+ if strings.TrimSpace(s) == "amd64" {
+ freebsd32o64 = true
+ break
+ }
+ }
+ }
+
+ s, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC)
+ if err != nil {
+ t.Fatal(err)
+ }
+ f := os.NewFile(uintptr(s), "route")
+ c, err := SocketConn(f, &routeAddr{})
+ f.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ const N = 3
+ var wg sync.WaitGroup
+ wg.Add(2 * N)
+ for i := 0; i < N; i++ {
+ go func(i int) {
+ defer wg.Done()
+ l := syscall.SizeofRtMsghdr + syscall.SizeofSockaddrInet4
+ if freebsd32o64 {
+ l += syscall.SizeofRtMetrics // see syscall/route_freebsd_32bit.go
+ }
+ b := make([]byte, l)
+ h := (*syscall.RtMsghdr)(unsafe.Pointer(&b[0]))
+ h.Msglen = uint16(len(b))
+ h.Version = syscall.RTM_VERSION
+ h.Type = syscall.RTM_GET
+ h.Addrs = syscall.RTA_DST
+ h.Pid = int32(os.Getpid())
+ h.Seq = int32(i)
+ p := (*syscall.RawSockaddrInet4)(unsafe.Pointer(&b[syscall.SizeofRtMsghdr]))
+ p.Len = syscall.SizeofSockaddrInet4
+ p.Family = syscall.AF_INET
+ p.Addr = [4]byte{127, 0, 0, 1}
+ if _, err := c.Write(b); err != nil {
+ t.Error(err)
+ return
+ }
+ }(i + 1)
+ }
+ for i := 0; i < N; i++ {
+ go func() {
+ defer wg.Done()
+ b := make([]byte, os.Getpagesize())
+ n, err := c.Read(b)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if _, err := syscall.ParseRoutingMessage(b[:n]); err != nil {
+ t.Error(err)
+ return
+ }
+ }()
+ }
+ wg.Wait()
+}
diff --git a/src/net/file_linux_test.go b/src/net/file_linux_test.go
new file mode 100644
index 0000000000..e04fea38f6
--- /dev/null
+++ b/src/net/file_linux_test.go
@@ -0,0 +1,98 @@
+// 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 net
+
+import (
+ "fmt"
+ "os"
+ "sync"
+ "syscall"
+ "testing"
+ "unsafe"
+)
+
+type netlinkAddr struct {
+ PID uint32
+ Groups uint32
+}
+
+func (a *netlinkAddr) Network() string { return "netlink" }
+func (a *netlinkAddr) String() string { return fmt.Sprintf("%x:%x", a.PID, a.Groups) }
+
+func (a *netlinkAddr) Addr(rsa []byte) Addr {
+ if len(rsa) < syscall.SizeofSockaddrNetlink {
+ return nil
+ }
+ var addr netlinkAddr
+ b := (*[unsafe.Sizeof(addr)]byte)(unsafe.Pointer(&addr))
+ copy(b[0:4], rsa[4:8])
+ copy(b[4:8], rsa[8:12])
+ return &addr
+}
+
+func (a *netlinkAddr) Raw(addr Addr) []byte {
+ if addr, ok := addr.(*netlinkAddr); ok {
+ rsa := &syscall.RawSockaddrNetlink{Family: syscall.AF_NETLINK, Pid: addr.PID, Groups: addr.Groups}
+ return (*[unsafe.Sizeof(*rsa)]byte)(unsafe.Pointer(rsa))[:]
+ }
+ return nil
+}
+
+func TestSocketPacketConn(t *testing.T) {
+ s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_ROUTE)
+ if err != nil {
+ t.Fatal(err)
+ }
+ lsa := syscall.SockaddrNetlink{Family: syscall.AF_NETLINK}
+ if err := syscall.Bind(s, &lsa); err != nil {
+ syscall.Close(s)
+ t.Fatal(err)
+ }
+ f := os.NewFile(uintptr(s), "netlink")
+ c, err := SocketPacketConn(f, &netlinkAddr{})
+ f.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ const N = 3
+ var wg sync.WaitGroup
+ wg.Add(2 * N)
+ dst := &netlinkAddr{PID: 0}
+ for i := 0; i < N; i++ {
+ go func() {
+ defer wg.Done()
+ l := syscall.NLMSG_HDRLEN + syscall.SizeofRtGenmsg
+ b := make([]byte, l)
+ *(*uint32)(unsafe.Pointer(&b[0:4][0])) = uint32(l)
+ *(*uint16)(unsafe.Pointer(&b[4:6][0])) = uint16(syscall.RTM_GETLINK)
+ *(*uint16)(unsafe.Pointer(&b[6:8][0])) = uint16(syscall.NLM_F_DUMP | syscall.NLM_F_REQUEST)
+ *(*uint32)(unsafe.Pointer(&b[8:12][0])) = uint32(1)
+ *(*uint32)(unsafe.Pointer(&b[12:16][0])) = uint32(0)
+ b[16] = byte(syscall.AF_UNSPEC)
+ if _, err := c.WriteTo(b, dst); err != nil {
+ t.Error(err)
+ return
+ }
+ }()
+ }
+ for i := 0; i < N; i++ {
+ go func() {
+ defer wg.Done()
+ b := make([]byte, os.Getpagesize())
+ n, _, err := c.ReadFrom(b)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if _, err := syscall.ParseNetlinkMessage(b[:n]); err != nil {
+ t.Error(err)
+ return
+ }
+ }()
+ }
+ wg.Wait()
+}
diff --git a/src/net/file_plan9.go b/src/net/file_plan9.go
index 892775a024..efe416f690 100644
--- a/src/net/file_plan9.go
+++ b/src/net/file_plan9.go
@@ -135,3 +135,11 @@ func fileListener(f *os.File) (Listener, error) {
func filePacketConn(f *os.File) (PacketConn, error) {
return nil, syscall.EPLAN9
}
+
+func socketConn(f *os.File, sa SocketAddr) (Conn, error) {
+ return nil, syscall.EPLAN9
+}
+
+func socketPacketConn(f *os.File, sa SocketAddr) (PacketConn, error) {
+ return nil, syscall.EPLAN9
+}
diff --git a/src/net/file_stub.go b/src/net/file_stub.go
index 0f7460c757..41ca78b437 100644
--- a/src/net/file_stub.go
+++ b/src/net/file_stub.go
@@ -11,6 +11,8 @@ import (
"syscall"
)
-func fileConn(f *os.File) (Conn, error) { return nil, syscall.ENOPROTOOPT }
-func fileListener(f *os.File) (Listener, error) { return nil, syscall.ENOPROTOOPT }
-func filePacketConn(f *os.File) (PacketConn, error) { return nil, syscall.ENOPROTOOPT }
+func fileConn(f *os.File) (Conn, error) { return nil, syscall.ENOPROTOOPT }
+func fileListener(f *os.File) (Listener, error) { return nil, syscall.ENOPROTOOPT }
+func filePacketConn(f *os.File) (PacketConn, error) { return nil, syscall.ENOPROTOOPT }
+func socketConn(f *os.File, sa SocketAddr) (Conn, error) { return nil, syscall.ENOPROTOOPT }
+func socketPacketConn(f *os.File, sa SocketAddr) (PacketConn, error) { return nil, syscall.ENOPROTOOPT }
diff --git a/src/net/file_unix.go b/src/net/file_unix.go
index 147ca1ed95..df884d1603 100644
--- a/src/net/file_unix.go
+++ b/src/net/file_unix.go
@@ -7,76 +7,81 @@
package net
import (
+ "internal/syscall/unix"
"os"
"syscall"
)
-func newFileFD(f *os.File) (*netFD, error) {
- fd, err := dupCloseOnExec(int(f.Fd()))
+func dupSocket(f *os.File) (int, error) {
+ s, err := dupCloseOnExec(int(f.Fd()))
+ if err != nil {
+ return -1, err
+ }
+ if err := syscall.SetNonblock(s, true); err != nil {
+ closeFunc(s)
+ return -1, os.NewSyscallError("setnonblock", err)
+ }
+ return s, nil
+}
+
+func newFileFD(f *os.File, sa SocketAddr) (*netFD, error) {
+ s, err := dupSocket(f)
if err != nil {
return nil, err
}
-
- if err = syscall.SetNonblock(fd, true); err != nil {
- closeFunc(fd)
- return nil, os.NewSyscallError("setnonblock", err)
+ var laddr, raddr Addr
+ var fd *netFD
+ if sa != nil {
+ lsa := make([]byte, syscall.SizeofSockaddrAny)
+ if err := unix.Getsockname(s, lsa); err != nil {
+ lsa = nil
+ }
+ rsa := make([]byte, syscall.SizeofSockaddrAny)
+ if err := unix.Getpeername(s, rsa); err != nil {
+ rsa = nil
+ }
+ laddr = sa.Addr(lsa)
+ raddr = sa.Addr(rsa)
+ fd, err = newFD(s, -1, -1, laddr.Network())
+ } else {
+ family := syscall.AF_UNSPEC
+ sotype, err := syscall.GetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_TYPE)
+ if err != nil {
+ closeFunc(s)
+ return nil, os.NewSyscallError("getsockopt", err)
+ }
+ lsa, _ := syscall.Getsockname(s)
+ rsa, _ := syscall.Getpeername(s)
+ switch lsa.(type) {
+ case *syscall.SockaddrInet4:
+ family = syscall.AF_INET
+ case *syscall.SockaddrInet6:
+ family = syscall.AF_INET6
+ case *syscall.SockaddrUnix:
+ family = syscall.AF_UNIX
+ default:
+ closeFunc(s)
+ return nil, syscall.EPROTONOSUPPORT
+ }
+ fd, err = newFD(s, family, sotype, "")
+ laddr = fd.addrFunc()(lsa)
+ raddr = fd.addrFunc()(rsa)
+ fd.net = laddr.Network()
}
-
- sotype, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TYPE)
if err != nil {
- closeFunc(fd)
- return nil, os.NewSyscallError("getsockopt", err)
- }
-
- family := syscall.AF_UNSPEC
- toAddr := sockaddrToTCP
- lsa, _ := syscall.Getsockname(fd)
- switch lsa.(type) {
- case *syscall.SockaddrInet4:
- family = syscall.AF_INET
- if sotype == syscall.SOCK_DGRAM {
- toAddr = sockaddrToUDP
- } else if sotype == syscall.SOCK_RAW {
- toAddr = sockaddrToIP
- }
- case *syscall.SockaddrInet6:
- family = syscall.AF_INET6
- if sotype == syscall.SOCK_DGRAM {
- toAddr = sockaddrToUDP
- } else if sotype == syscall.SOCK_RAW {
- toAddr = sockaddrToIP
- }
- case *syscall.SockaddrUnix:
- family = syscall.AF_UNIX
- toAddr = sockaddrToUnix
- if sotype == syscall.SOCK_DGRAM {
- toAddr = sockaddrToUnixgram
- } else if sotype == syscall.SOCK_SEQPACKET {
- toAddr = sockaddrToUnixpacket
- }
- default:
- closeFunc(fd)
- return nil, syscall.EPROTONOSUPPORT
- }
- laddr := toAddr(lsa)
- rsa, _ := syscall.Getpeername(fd)
- raddr := toAddr(rsa)
-
- netfd, err := newFD(fd, family, sotype, laddr.Network())
- if err != nil {
- closeFunc(fd)
+ closeFunc(s)
return nil, err
}
- if err := netfd.init(); err != nil {
- netfd.Close()
+ if err := fd.init(); err != nil {
+ fd.Close()
return nil, err
}
- netfd.setAddr(laddr, raddr)
- return netfd, nil
+ fd.setAddr(laddr, raddr)
+ return fd, nil
}
func fileConn(f *os.File) (Conn, error) {
- fd, err := newFileFD(f)
+ fd, err := newFileFD(f, nil)
if err != nil {
return nil, err
}
@@ -95,7 +100,7 @@ func fileConn(f *os.File) (Conn, error) {
}
func fileListener(f *os.File) (Listener, error) {
- fd, err := newFileFD(f)
+ fd, err := newFileFD(f, nil)
if err != nil {
return nil, err
}
@@ -110,7 +115,7 @@ func fileListener(f *os.File) (Listener, error) {
}
func filePacketConn(f *os.File) (PacketConn, error) {
- fd, err := newFileFD(f)
+ fd, err := newFileFD(f, nil)
if err != nil {
return nil, err
}
@@ -125,3 +130,55 @@ func filePacketConn(f *os.File) (PacketConn, error) {
fd.Close()
return nil, syscall.EINVAL
}
+
+func socketConn(f *os.File, sa SocketAddr) (Conn, error) {
+ fd, err := newFileFD(f, sa)
+ if err != nil {
+ return nil, err
+ }
+ return &socketFile{conn: conn{fd}, SocketAddr: sa}, nil
+}
+
+func socketPacketConn(f *os.File, sa SocketAddr) (PacketConn, error) {
+ fd, err := newFileFD(f, sa)
+ if err != nil {
+ return nil, err
+ }
+ return &socketFile{conn: conn{fd}, SocketAddr: sa}, nil
+}
+
+var (
+ _ Conn = &socketFile{}
+ _ PacketConn = &socketFile{}
+)
+
+// A socketFile is a placeholder that holds a user-specified socket
+// descriptor and a profile of socket address encoding.
+// It implements both Conn and PacketConn interfaces.
+type socketFile struct {
+ conn
+ SocketAddr
+}
+
+func (c *socketFile) ReadFrom(b []byte) (int, Addr, error) {
+ if !c.ok() {
+ return 0, nil, syscall.EINVAL
+ }
+ from := make([]byte, syscall.SizeofSockaddrAny)
+ n, err := c.fd.recvFrom(b, 0, from)
+ if err != nil {
+ return n, nil, &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return n, c.SocketAddr.Addr(from), nil
+}
+
+func (c *socketFile) WriteTo(b []byte, addr Addr) (int, error) {
+ if !c.ok() {
+ return 0, syscall.EINVAL
+ }
+ n, err := c.fd.sendTo(b, 0, c.SocketAddr.Raw(addr))
+ if err != nil {
+ return n, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return n, nil
+}
diff --git a/src/net/file_windows.go b/src/net/file_windows.go
index 241fa17617..1ed72d5bd4 100644
--- a/src/net/file_windows.go
+++ b/src/net/file_windows.go
@@ -23,3 +23,13 @@ func filePacketConn(f *os.File) (PacketConn, error) {
// TODO: Implement this
return nil, syscall.EWINDOWS
}
+
+func socketConn(f *os.File, sa SocketAddr) (Conn, error) {
+ // TODO: Implement this
+ return nil, syscall.EWINDOWS
+}
+
+func socketPacketConn(f *os.File, sa SocketAddr) (PacketConn, error) {
+ // TODO: Implement this
+ return nil, syscall.EWINDOWS
+}
diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go
index dc499a90b6..b1d8799fa5 100644
--- a/src/net/http/client_test.go
+++ b/src/net/http/client_test.go
@@ -927,7 +927,14 @@ func TestClientTimeout_Headers(t *testing.T) {
<-donec
}))
defer ts.Close()
- defer close(donec)
+ // Note that we use a channel send here and not a close.
+ // The race detector doesn't know that we're waiting for a timeout
+ // and thinks that the waitgroup inside httptest.Server is added to concurrently
+ // with us closing it. If we timed out immediately, we could close the testserver
+ // before we entered the handler. We're not timing out immediately and there's
+ // no way we would be done before we entered the handler, but the race detector
+ // doesn't know this, so synchronize explicitly.
+ defer func() { donec <- true }()
c := &Client{Timeout: 500 * time.Millisecond}
diff --git a/src/net/http/internal/chunked.go b/src/net/http/internal/chunked.go
index 9294deb3e5..6d7c69874d 100644
--- a/src/net/http/internal/chunked.go
+++ b/src/net/http/internal/chunked.go
@@ -173,8 +173,12 @@ func (cw *chunkedWriter) Write(data []byte) (n int, err error) {
err = io.ErrShortWrite
return
}
- _, err = io.WriteString(cw.Wire, "\r\n")
-
+ if _, err = io.WriteString(cw.Wire, "\r\n"); err != nil {
+ return
+ }
+ if bw, ok := cw.Wire.(*FlushAfterChunkWriter); ok {
+ err = bw.Flush()
+ }
return
}
@@ -183,6 +187,15 @@ func (cw *chunkedWriter) Close() error {
return err
}
+// FlushAfterChunkWriter signals from the caller of NewChunkedWriter
+// that each chunk should be followed by a flush. It is used by the
+// http.Transport code to keep the buffering behavior for headers and
+// trailers, but flush out chunks aggressively in the middle for
+// request bodies which may be generated slowly. See Issue 6574.
+type FlushAfterChunkWriter struct {
+ *bufio.Writer
+}
+
func parseHexUint(v []byte) (n uint64, err error) {
for _, b := range v {
n <<= 4
diff --git a/src/net/http/transfer.go b/src/net/http/transfer.go
index 5640344345..289d53dec0 100644
--- a/src/net/http/transfer.go
+++ b/src/net/http/transfer.go
@@ -43,6 +43,7 @@ type transferWriter struct {
Close bool
TransferEncoding []string
Trailer Header
+ IsResponse bool
}
func newTransferWriter(r interface{}) (t *transferWriter, err error) {
@@ -89,6 +90,7 @@ func newTransferWriter(r interface{}) (t *transferWriter, err error) {
}
}
case *Response:
+ t.IsResponse = true
if rr.Request != nil {
t.Method = rr.Request.Method
}
@@ -206,6 +208,9 @@ func (t *transferWriter) WriteBody(w io.Writer) error {
// Write body
if t.Body != nil {
if chunked(t.TransferEncoding) {
+ if bw, ok := w.(*bufio.Writer); ok && !t.IsResponse {
+ w = &internal.FlushAfterChunkWriter{bw}
+ }
cw := internal.NewChunkedWriter(w)
_, err = io.Copy(cw, t.Body)
if err == nil {
diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go
index ace58896b8..ca1a3ab407 100644
--- a/src/net/http/transport_test.go
+++ b/src/net/http/transport_test.go
@@ -23,6 +23,7 @@ import (
"net/http/httptest"
"net/url"
"os"
+ "reflect"
"runtime"
"strconv"
"strings"
@@ -2447,6 +2448,104 @@ func TestTransportDialCancelRace(t *testing.T) {
}
}
+// logWritesConn is a net.Conn that logs each Write call to writes
+// and then proxies to w.
+// It proxies Read calls to a reader it receives from rch.
+type logWritesConn struct {
+ net.Conn // nil. crash on use.
+
+ w io.Writer
+
+ rch <-chan io.Reader
+ r io.Reader // nil until received by rch
+
+ mu sync.Mutex
+ writes []string
+}
+
+func (c *logWritesConn) Write(p []byte) (n int, err error) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ c.writes = append(c.writes, string(p))
+ return c.w.Write(p)
+}
+
+func (c *logWritesConn) Read(p []byte) (n int, err error) {
+ if c.r == nil {
+ c.r = <-c.rch
+ }
+ return c.r.Read(p)
+}
+
+func (c *logWritesConn) Close() error { return nil }
+
+// Issue 6574
+func TestTransportFlushesBodyChunks(t *testing.T) {
+ defer afterTest(t)
+ resBody := make(chan io.Reader, 1)
+ connr, connw := io.Pipe() // connection pipe pair
+ lw := &logWritesConn{
+ rch: resBody,
+ w: connw,
+ }
+ tr := &Transport{
+ Dial: func(network, addr string) (net.Conn, error) {
+ return lw, nil
+ },
+ }
+ bodyr, bodyw := io.Pipe() // body pipe pair
+ go func() {
+ defer bodyw.Close()
+ for i := 0; i < 3; i++ {
+ fmt.Fprintf(bodyw, "num%d\n", i)
+ }
+ }()
+ resc := make(chan *Response)
+ go func() {
+ req, _ := NewRequest("POST", "http://localhost:8080", bodyr)
+ req.Header.Set("User-Agent", "x") // known value for test
+ res, err := tr.RoundTrip(req)
+ if err != nil {
+ t.Error("RoundTrip: %v", err)
+ close(resc)
+ return
+ }
+ resc <- res
+
+ }()
+ // Fully consume the request before checking the Write log vs. want.
+ req, err := ReadRequest(bufio.NewReader(connr))
+ if err != nil {
+ t.Fatal(err)
+ }
+ io.Copy(ioutil.Discard, req.Body)
+
+ // Unblock the transport's roundTrip goroutine.
+ resBody <- strings.NewReader("HTTP/1.1 204 No Content\r\nConnection: close\r\n\r\n")
+ res, ok := <-resc
+ if !ok {
+ return
+ }
+ defer res.Body.Close()
+
+ want := []string{
+ // Because Request.ContentLength = 0, the body is sniffed for 1 byte to determine whether there's content.
+ // That explains the initial "num0" being split into "n" and "um0".
+ // The first byte is included with the request headers Write. Perhaps in the future
+ // we will want to flush the headers out early if the first byte of the request body is
+ // taking a long time to arrive. But not yet.
+ "POST / HTTP/1.1\r\nHost: localhost:8080\r\nUser-Agent: x\r\nTransfer-Encoding: chunked\r\nAccept-Encoding: gzip\r\n\r\n" +
+ "1\r\nn\r\n",
+ "4\r\num0\n\r\n",
+ "5\r\nnum1\n\r\n",
+ "5\r\nnum2\n\r\n",
+ "0\r\n\r\n",
+ }
+ if !reflect.DeepEqual(lw.writes, want) {
+ t.Errorf("Writes differed.\n Got: %q\nWant: %q\n", lw.writes, want)
+ }
+}
+
func wantBody(res *http.Response, err error, want string) error {
if err != nil {
return err
diff --git a/src/net/interface_test.go b/src/net/interface_test.go
index 0e5c2e3ddf..567d18de44 100644
--- a/src/net/interface_test.go
+++ b/src/net/interface_test.go
@@ -229,7 +229,7 @@ func testMulticastAddrs(t *testing.T, ifmat []Addr) (nmaf4, nmaf6 int) {
}
func BenchmarkInterfaces(b *testing.B) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
+ testHookUninstaller.Do(uninstallTestHooks)
for i := 0; i < b.N; i++ {
if _, err := Interfaces(); err != nil {
@@ -239,7 +239,7 @@ func BenchmarkInterfaces(b *testing.B) {
}
func BenchmarkInterfaceByIndex(b *testing.B) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
+ testHookUninstaller.Do(uninstallTestHooks)
ifi := loopbackInterface()
if ifi == nil {
@@ -253,7 +253,7 @@ func BenchmarkInterfaceByIndex(b *testing.B) {
}
func BenchmarkInterfaceByName(b *testing.B) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
+ testHookUninstaller.Do(uninstallTestHooks)
ifi := loopbackInterface()
if ifi == nil {
@@ -267,7 +267,7 @@ func BenchmarkInterfaceByName(b *testing.B) {
}
func BenchmarkInterfaceAddrs(b *testing.B) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
+ testHookUninstaller.Do(uninstallTestHooks)
for i := 0; i < b.N; i++ {
if _, err := InterfaceAddrs(); err != nil {
@@ -277,7 +277,7 @@ func BenchmarkInterfaceAddrs(b *testing.B) {
}
func BenchmarkInterfacesAndAddrs(b *testing.B) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
+ testHookUninstaller.Do(uninstallTestHooks)
ifi := loopbackInterface()
if ifi == nil {
@@ -291,7 +291,7 @@ func BenchmarkInterfacesAndAddrs(b *testing.B) {
}
func BenchmarkInterfacesAndMulticastAddrs(b *testing.B) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
+ testHookUninstaller.Do(uninstallTestHooks)
ifi := loopbackInterface()
if ifi == nil {
diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go
index 3ae1c6be3c..60e581f463 100644
--- a/src/net/internal/socktest/main_test.go
+++ b/src/net/internal/socktest/main_test.go
@@ -9,6 +9,7 @@ package socktest_test
import (
"net/internal/socktest"
"os"
+ "sync"
"syscall"
"testing"
)
@@ -27,6 +28,21 @@ func TestMain(m *testing.M) {
os.Exit(st)
}
+func TestSwitch(t *testing.T) {
+ const N = 10
+ var wg sync.WaitGroup
+ wg.Add(N)
+ for i := 0; i < N; i++ {
+ go func() {
+ defer wg.Done()
+ for _, family := range []int{syscall.AF_INET, syscall.AF_INET6} {
+ socketFunc(family, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
+ }
+ }()
+ }
+ wg.Wait()
+}
+
func TestSocket(t *testing.T) {
for _, f := range []socktest.Filter{
func(st *socktest.Status) (socktest.AfterFilter, error) { return nil, nil },
diff --git a/src/net/internal/socktest/switch.go b/src/net/internal/socktest/switch.go
index 5e558a2de3..4e38c7a85f 100644
--- a/src/net/internal/socktest/switch.go
+++ b/src/net/internal/socktest/switch.go
@@ -10,12 +10,6 @@ import (
"sync"
)
-func switchInit(sw *Switch) {
- sw.fltab = make(map[FilterType]Filter)
- sw.sotab = make(Sockets)
- sw.stats = make(stats)
-}
-
// A Switch represents a callpath point switch for socket system
// calls.
type Switch struct {
@@ -29,6 +23,12 @@ type Switch struct {
stats stats
}
+func (sw *Switch) init() {
+ sw.fltab = make(map[FilterType]Filter)
+ sw.sotab = make(Sockets)
+ sw.stats = make(stats)
+}
+
// Stats returns a list of per-cookie socket statistics.
func (sw *Switch) Stats() []Stat {
var st []Stat
@@ -162,7 +162,7 @@ func (f AfterFilter) apply(st *Status) error {
// Set deploys the socket system call filter f for the filter type t.
func (sw *Switch) Set(t FilterType, f Filter) {
- sw.once.Do(func() { switchInit(sw) })
+ sw.once.Do(sw.init)
sw.fmu.Lock()
sw.fltab[t] = f
sw.fmu.Unlock()
diff --git a/src/net/internal/socktest/switch_unix.go b/src/net/internal/socktest/switch_unix.go
index 2b89276fa1..14c0c228a2 100644
--- a/src/net/internal/socktest/switch_unix.go
+++ b/src/net/internal/socktest/switch_unix.go
@@ -22,7 +22,7 @@ func (sw *Switch) sockso(s int) *Status {
// addLocked returns a new Status without locking.
// sw.smu must be held before call.
func (sw *Switch) addLocked(s, family, sotype, proto int) *Status {
- sw.once.Do(func() { switchInit(sw) })
+ sw.once.Do(sw.init)
so := Status{Cookie: cookie(family, sotype, proto)}
sw.sotab[s] = so
return &so
diff --git a/src/net/internal/socktest/switch_windows.go b/src/net/internal/socktest/switch_windows.go
index 3cee49ba0b..4f1d597a27 100644
--- a/src/net/internal/socktest/switch_windows.go
+++ b/src/net/internal/socktest/switch_windows.go
@@ -22,7 +22,7 @@ func (sw *Switch) sockso(s syscall.Handle) *Status {
// addLocked returns a new Status without locking.
// sw.smu must be held before call.
func (sw *Switch) addLocked(s syscall.Handle, family, sotype, proto int) *Status {
- sw.once.Do(func() { switchInit(sw) })
+ sw.once.Do(sw.init)
so := Status{Cookie: cookie(family, sotype, proto)}
sw.sotab[s] = so
return &so
diff --git a/src/net/internal/socktest/sys_unix.go b/src/net/internal/socktest/sys_unix.go
index 4089f8cea2..f983e266f1 100644
--- a/src/net/internal/socktest/sys_unix.go
+++ b/src/net/internal/socktest/sys_unix.go
@@ -10,6 +10,8 @@ import "syscall"
// Socket wraps syscall.Socket.
func (sw *Switch) Socket(family, sotype, proto int) (s int, err error) {
+ sw.once.Do(sw.init)
+
so := &Status{Cookie: cookie(family, sotype, proto)}
sw.fmu.RLock()
f, _ := sw.fltab[FilterSocket]
diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go
index 907e01b5a2..07af0e7046 100644
--- a/src/net/internal/socktest/sys_windows.go
+++ b/src/net/internal/socktest/sys_windows.go
@@ -8,6 +8,8 @@ import "syscall"
// Socket wraps syscall.Socket.
func (sw *Switch) Socket(family, sotype, proto int) (s syscall.Handle, err error) {
+ sw.once.Do(sw.init)
+
so := &Status{Cookie: cookie(family, sotype, proto)}
sw.fmu.RLock()
f, _ := sw.fltab[FilterSocket]
diff --git a/src/net/ip_test.go b/src/net/ip_test.go
index 24f67cac97..b1939cd08f 100644
--- a/src/net/ip_test.go
+++ b/src/net/ip_test.go
@@ -53,7 +53,7 @@ func TestParseIP(t *testing.T) {
}
func BenchmarkParseIP(b *testing.B) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
+ testHookUninstaller.Do(uninstallTestHooks)
for i := 0; i < b.N; i++ {
for _, tt := range parseIPTests {
@@ -110,7 +110,7 @@ func TestIPString(t *testing.T) {
}
func BenchmarkIPString(b *testing.B) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
+ testHookUninstaller.Do(uninstallTestHooks)
for i := 0; i < b.N; i++ {
for _, tt := range ipStringTests {
@@ -162,7 +162,7 @@ func TestIPMaskString(t *testing.T) {
}
func BenchmarkIPMaskString(b *testing.B) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
+ testHookUninstaller.Do(uninstallTestHooks)
for i := 0; i < b.N; i++ {
for _, tt := range ipMaskStringTests {
diff --git a/src/net/ipsock_posix.go b/src/net/ipsock_posix.go
index 56b9872fd1..83eaf855b4 100644
--- a/src/net/ipsock_posix.go
+++ b/src/net/ipsock_posix.go
@@ -9,10 +9,18 @@
package net
import (
+ "runtime"
"syscall"
"time"
)
+// BUG(rsc,mikio): On DragonFly BSD and OpenBSD, listening on the
+// "tcp" and "udp" networks does not listen for both IPv4 and IPv6
+// connections. This is due to the fact that IPv4 traffic will not be
+// routed to an IPv6 socket - two separate sockets are required if
+// both address families are to be supported.
+// See inet6(4) for details.
+
func probeIPv4Stack() bool {
s, err := socketFunc(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
switch err {
@@ -41,13 +49,28 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
var probes = []struct {
laddr TCPAddr
value int
- ok bool
}{
// IPv6 communication capability
{laddr: TCPAddr{IP: ParseIP("::1")}, value: 1},
// IPv6 IPv4-mapped address communication capability
{laddr: TCPAddr{IP: IPv4(127, 0, 0, 1)}, value: 0},
}
+ var supps [2]bool
+ switch runtime.GOOS {
+ case "dragonfly", "openbsd":
+ // Some released versions of DragonFly BSD pretend to
+ // accept IPV6_V6ONLY=0 successfully, but the state
+ // still stays IPV6_V6ONLY=1. Eventually DragonFly BSD
+ // stops preteding, but the transition period would
+ // cause unpredictable behavior and we need to avoid
+ // it.
+ //
+ // OpenBSD also doesn't support IPV6_V6ONLY=0 but it
+ // never pretends to accept IPV6_V6OLY=0. It always
+ // returns an error and we don't need to probe the
+ // capability.
+ probes = probes[:1]
+ }
for i := range probes {
s, err := socketFunc(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
@@ -63,10 +86,10 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
if err := syscall.Bind(s, sa); err != nil {
continue
}
- probes[i].ok = true
+ supps[i] = true
}
- return probes[0].ok, probes[1].ok
+ return supps[0], supps[1]
}
// favoriteAddrFamily returns the appropriate address family to
diff --git a/src/net/listen_test.go b/src/net/listen_test.go
index 8f43c846d9..89d4d7e0de 100644
--- a/src/net/listen_test.go
+++ b/src/net/listen_test.go
@@ -218,8 +218,6 @@ var dualStackTCPListenerTests = []struct {
// listening address and same port.
func TestDualStackTCPListener(t *testing.T) {
switch runtime.GOOS {
- case "dragonfly":
- t.Skip("not supported on DragonFly, see golang.org/issue/10729")
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
@@ -233,7 +231,7 @@ func TestDualStackTCPListener(t *testing.T) {
continue
}
- if runtime.GOOS == "openbsd" && differentWildcardAddr(tt.address1, tt.address2) {
+ if !supportsIPv4map && differentWildcardAddr(tt.address1, tt.address2) {
tt.xerr = nil
}
var firstErr, secondErr error
@@ -320,7 +318,7 @@ func TestDualStackUDPListener(t *testing.T) {
continue
}
- if runtime.GOOS == "openbsd" && differentWildcardAddr(tt.address1, tt.address2) {
+ if !supportsIPv4map && differentWildcardAddr(tt.address1, tt.address2) {
tt.xerr = nil
}
var firstErr, secondErr error
diff --git a/src/net/lookup_test.go b/src/net/lookup_test.go
index 1f36184d55..064bc0b9f1 100644
--- a/src/net/lookup_test.go
+++ b/src/net/lookup_test.go
@@ -23,17 +23,34 @@ func lookupLocalhost(fn func(string) ([]IPAddr, error), host string) ([]IPAddr,
}
}
+// The Lookup APIs use various sources such as local database, DNS or
+// mDNS, and may use platform-dependent DNS stub resolver if possible.
+// The APIs accept any of forms for a query; host name in various
+// encodings, UTF-8 encoded net name, domain name, FQDN or absolute
+// FQDN, but the result would be one of the forms and it depends on
+// the circumstances.
+
var lookupGoogleSRVTests = []struct {
service, proto, name string
cname, target string
}{
{
"xmpp-server", "tcp", "google.com",
- ".google.com", ".google.com",
+ "google.com", "google.com",
},
{
- "", "", "_xmpp-server._tcp.google.com", // non-standard back door
- ".google.com", ".google.com",
+ "xmpp-server", "tcp", "google.com.",
+ "google.com", "google.com",
+ },
+
+ // non-standard back door
+ {
+ "", "", "_xmpp-server._tcp.google.com",
+ "google.com", "google.com",
+ },
+ {
+ "", "", "_xmpp-server._tcp.google.com.",
+ "google.com", "google.com",
},
}
@@ -41,6 +58,9 @@ func TestLookupGoogleSRV(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("avoid external network")
}
+ if !supportsIPv4 || !*testIPv4 {
+ t.Skip("IPv4 is required")
+ }
for _, tt := range lookupGoogleSRVTests {
cname, srvs, err := LookupSRV(tt.service, tt.proto, tt.name)
@@ -50,88 +70,126 @@ func TestLookupGoogleSRV(t *testing.T) {
if len(srvs) == 0 {
t.Error("got no record")
}
- if !strings.Contains(cname, tt.cname) {
- t.Errorf("got %q; want %q", cname, tt.cname)
+ if !strings.HasSuffix(cname, tt.cname) && !strings.HasSuffix(cname, tt.cname+".") {
+ t.Errorf("got %s; want %s", cname, tt.cname)
}
for _, srv := range srvs {
- if !strings.Contains(srv.Target, tt.target) {
- t.Errorf("got %v; want a record containing %q", srv, tt.target)
+ if !strings.HasSuffix(srv.Target, tt.target) && !strings.HasSuffix(srv.Target, tt.target+".") {
+ t.Errorf("got %v; want a record containing %s", srv, tt.target)
}
}
}
}
+var lookupGmailMXTests = []struct {
+ name, host string
+}{
+ {"gmail.com", "google.com"},
+ {"gmail.com.", "google.com"},
+}
+
func TestLookupGmailMX(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("avoid external network")
}
+ if !supportsIPv4 || !*testIPv4 {
+ t.Skip("IPv4 is required")
+ }
- mxs, err := LookupMX("gmail.com")
- if err != nil {
- t.Fatal(err)
- }
- if len(mxs) == 0 {
- t.Error("got no record")
- }
- for _, mx := range mxs {
- if !strings.Contains(mx.Host, ".google.com") {
- t.Errorf("got %v; want a record containing .google.com.", mx)
+ for _, tt := range lookupGmailMXTests {
+ mxs, err := LookupMX(tt.name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(mxs) == 0 {
+ t.Error("got no record")
+ }
+ for _, mx := range mxs {
+ if !strings.HasSuffix(mx.Host, tt.host) && !strings.HasSuffix(mx.Host, tt.host+".") {
+ t.Errorf("got %v; want a record containing %s", mx, tt.host)
+ }
}
}
}
+var lookupGmailNSTests = []struct {
+ name, host string
+}{
+ {"gmail.com", "google.com"},
+ {"gmail.com.", "google.com"},
+}
+
func TestLookupGmailNS(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("avoid external network")
}
+ if !supportsIPv4 || !*testIPv4 {
+ t.Skip("IPv4 is required")
+ }
- nss, err := LookupNS("gmail.com")
- if err != nil {
- t.Fatal(err)
- }
- if len(nss) == 0 {
- t.Error("got no record")
- }
- for _, ns := range nss {
- if !strings.Contains(ns.Host, ".google.com") {
- t.Errorf("got %v; want a record containing .google.com.", ns)
+ for _, tt := range lookupGmailNSTests {
+ nss, err := LookupNS(tt.name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(nss) == 0 {
+ t.Error("got no record")
+ }
+ for _, ns := range nss {
+ if !strings.HasSuffix(ns.Host, tt.host) && !strings.HasSuffix(ns.Host, tt.host+".") {
+ t.Errorf("got %v; want a record containing %s", ns, tt.host)
+ }
}
}
}
+var lookupGmailTXTTests = []struct {
+ name, txt, host string
+}{
+ {"gmail.com", "spf", "google.com"},
+ {"gmail.com.", "spf", "google.com"},
+}
+
func TestLookupGmailTXT(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("avoid external network")
}
+ if !supportsIPv4 || !*testIPv4 {
+ t.Skip("IPv4 is required")
+ }
- txts, err := LookupTXT("gmail.com")
- if err != nil {
- t.Fatal(err)
- }
- if len(txts) == 0 {
- t.Error("got no record")
- }
- for _, txt := range txts {
- if !strings.Contains(txt, "spf") {
- t.Errorf("got %q; want a spf record", txt)
+ for _, tt := range lookupGmailTXTTests {
+ txts, err := LookupTXT(tt.name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(txts) == 0 {
+ t.Error("got no record")
+ }
+ for _, txt := range txts {
+ if !strings.Contains(txt, tt.txt) || (!strings.HasSuffix(txt, tt.host) && !strings.HasSuffix(txt, tt.host+".")) {
+ t.Errorf("got %s; want a record containing %s, %s", txt, tt.txt, tt.host)
+ }
}
}
}
var lookupGooglePublicDNSAddrs = []struct {
- addr string
- name string
+ addr, name string
}{
- {"8.8.8.8", ".google.com."},
- {"8.8.4.4", ".google.com."},
- {"2001:4860:4860::8888", ".google.com."},
- {"2001:4860:4860::8844", ".google.com."},
+ {"8.8.8.8", ".google.com"},
+ {"8.8.4.4", ".google.com"},
+ {"2001:4860:4860::8888", ".google.com"},
+ {"2001:4860:4860::8844", ".google.com"},
}
func TestLookupGooglePublicDNSAddr(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("avoid external network")
}
+ if !supportsIPv4 || !supportsIPv6 || !*testIPv4 || !*testIPv6 {
+ t.Skip("both IPv4 and IPv6 are required")
+ }
for _, tt := range lookupGooglePublicDNSAddrs {
names, err := LookupAddr(tt.addr)
@@ -142,61 +200,97 @@ func TestLookupGooglePublicDNSAddr(t *testing.T) {
t.Error("got no record")
}
for _, name := range names {
- if !strings.HasSuffix(name, tt.name) {
- t.Errorf("got %q; want a record containing %q", name, tt.name)
+ if !strings.HasSuffix(name, tt.name) && !strings.HasSuffix(name, tt.name+".") {
+ t.Errorf("got %s; want a record containing %s", name, tt.name)
}
}
}
}
+var lookupIANACNAMETests = []struct {
+ name, cname string
+}{
+ {"www.iana.org", "icann.org"},
+ {"www.iana.org.", "icann.org"},
+}
+
func TestLookupIANACNAME(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("avoid external network")
}
+ if !supportsIPv4 || !*testIPv4 {
+ t.Skip("IPv4 is required")
+ }
- cname, err := LookupCNAME("www.iana.org")
- if err != nil {
- t.Fatal(err)
- }
- if !strings.HasSuffix(cname, ".icann.org.") {
- t.Errorf("got %q; want a record containing .icann.org.", cname)
+ for _, tt := range lookupIANACNAMETests {
+ cname, err := LookupCNAME(tt.name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !strings.HasSuffix(cname, tt.cname) && !strings.HasSuffix(cname, tt.cname+".") {
+ t.Errorf("got %s; want a record containing %s", cname, tt.cname)
+ }
}
}
+var lookupGoogleHostTests = []struct {
+ name string
+}{
+ {"google.com"},
+ {"google.com."},
+}
+
func TestLookupGoogleHost(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("avoid external network")
}
+ if !supportsIPv4 || !*testIPv4 {
+ t.Skip("IPv4 is required")
+ }
- addrs, err := LookupHost("google.com")
- if err != nil {
- t.Fatal(err)
- }
- if len(addrs) == 0 {
- t.Error("got no record")
- }
- for _, addr := range addrs {
- if ParseIP(addr) == nil {
- t.Errorf("got %q; want a literal ip address", addr)
+ for _, tt := range lookupGoogleHostTests {
+ addrs, err := LookupHost(tt.name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(addrs) == 0 {
+ t.Error("got no record")
+ }
+ for _, addr := range addrs {
+ if ParseIP(addr) == nil {
+ t.Errorf("got %q; want a literal IP address", addr)
+ }
}
}
}
+var lookupGoogleIPTests = []struct {
+ name string
+}{
+ {"google.com"},
+ {"google.com."},
+}
+
func TestLookupGoogleIP(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("avoid external network")
}
+ if !supportsIPv4 || !*testIPv4 {
+ t.Skip("IPv4 is required")
+ }
- ips, err := LookupIP("google.com")
- if err != nil {
- t.Fatal(err)
- }
- if len(ips) == 0 {
- t.Error("got no record")
- }
- for _, ip := range ips {
- if ip.To4() == nil && ip.To16() == nil {
- t.Errorf("got %v; want an ip address", ip)
+ for _, tt := range lookupGoogleIPTests {
+ ips, err := LookupIP(tt.name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(ips) == 0 {
+ t.Error("got no record")
+ }
+ for _, ip := range ips {
+ if ip.To4() == nil && ip.To16() == nil {
+ t.Errorf("got %v; want an IP address", ip)
+ }
}
}
}
diff --git a/src/net/mail/message.go b/src/net/mail/message.go
index f3f698cf23..04cbfd3e8b 100644
--- a/src/net/mail/message.go
+++ b/src/net/mail/message.go
@@ -20,9 +20,9 @@ import (
"bytes"
"errors"
"fmt"
- "internal/mime"
"io"
"log"
+ "mime"
"net/textproto"
"strings"
"time"
@@ -138,12 +138,30 @@ type Address struct {
// Parses a single RFC 5322 address, e.g. "Barry Gibbs "
func ParseAddress(address string) (*Address, error) {
- return newAddrParser(address).parseAddress()
+ return (&addrParser{s: address}).parseAddress()
}
// ParseAddressList parses the given string as a list of addresses.
func ParseAddressList(list string) ([]*Address, error) {
- return newAddrParser(list).parseAddressList()
+ return (&addrParser{s: list}).parseAddressList()
+}
+
+// An AddressParser is an RFC 5322 address parser.
+type AddressParser struct {
+ // WordDecoder optionally specifies a decoder for RFC 2047 encoded-words.
+ WordDecoder *mime.WordDecoder
+}
+
+// Parse parses a single RFC 5322 address of the
+// form "Gogh Fir " or "foo@example.com".
+func (p *AddressParser) Parse(address string) (*Address, error) {
+ return (&addrParser{s: address, dec: p.WordDecoder}).parseAddress()
+}
+
+// ParseList parses the given string as a list of comma-separated addresses
+// of the form "Gogh Fir " or "foo@example.com".
+func (p *AddressParser) ParseList(list string) ([]*Address, error) {
+ return (&addrParser{s: list, dec: p.WordDecoder}).parseAddressList()
}
// String formats the address as a valid RFC 5322 address.
@@ -177,14 +195,12 @@ func (a *Address) String() string {
return b.String()
}
- return mime.EncodeWord(a.Name) + " " + s
+ return mime.QEncoding.Encode("utf-8", a.Name) + " " + s
}
-type addrParser []byte
-
-func newAddrParser(s string) *addrParser {
- p := addrParser(s)
- return &p
+type addrParser struct {
+ s string
+ dec *mime.WordDecoder // may be nil
}
func (p *addrParser) parseAddressList() ([]*Address, error) {
@@ -210,7 +226,7 @@ func (p *addrParser) parseAddressList() ([]*Address, error) {
// parseAddress parses a single RFC 5322 address at the start of p.
func (p *addrParser) parseAddress() (addr *Address, err error) {
- debug.Printf("parseAddress: %q", *p)
+ debug.Printf("parseAddress: %q", p.s)
p.skipSpace()
if p.empty() {
return nil, errors.New("mail: no address")
@@ -229,7 +245,7 @@ func (p *addrParser) parseAddress() (addr *Address, err error) {
}, err
}
debug.Printf("parseAddress: not an addr-spec: %v", err)
- debug.Printf("parseAddress: state is now %q", *p)
+ debug.Printf("parseAddress: state is now %q", p.s)
// display-name
var displayName string
@@ -263,7 +279,7 @@ func (p *addrParser) parseAddress() (addr *Address, err error) {
// consumeAddrSpec parses a single RFC 5322 addr-spec at the start of p.
func (p *addrParser) consumeAddrSpec() (spec string, err error) {
- debug.Printf("consumeAddrSpec: %q", *p)
+ debug.Printf("consumeAddrSpec: %q", p.s)
orig := *p
defer func() {
@@ -313,7 +329,7 @@ func (p *addrParser) consumeAddrSpec() (spec string, err error) {
// consumePhrase parses the RFC 5322 phrase at the start of p.
func (p *addrParser) consumePhrase() (phrase string, err error) {
- debug.Printf("consumePhrase: [%s]", *p)
+ debug.Printf("consumePhrase: [%s]", p.s)
// phrase = 1*word
var words []string
for {
@@ -333,9 +349,8 @@ func (p *addrParser) consumePhrase() (phrase string, err error) {
word, err = p.consumeAtom(true)
}
- // RFC 2047 encoded-word starts with =?, ends with ?=, and has two other ?s.
- if err == nil && strings.HasPrefix(word, "=?") && strings.HasSuffix(word, "?=") && strings.Count(word, "?") == 4 {
- word, err = mime.DecodeWord(word)
+ if err == nil {
+ word, err = p.decodeRFC2047Word(word)
}
if err != nil {
@@ -363,14 +378,14 @@ Loop:
if i >= p.len() {
return "", errors.New("mail: unclosed quoted-string")
}
- switch c := (*p)[i]; {
+ switch c := p.s[i]; {
case c == '"':
break Loop
case c == '\\':
if i+1 == p.len() {
return "", errors.New("mail: unclosed quoted-string")
}
- qsb = append(qsb, (*p)[i+1])
+ qsb = append(qsb, p.s[i+1])
i += 2
case isQtext(c), c == ' ' || c == '\t':
// qtext (printable US-ASCII excluding " and \), or
@@ -381,7 +396,7 @@ Loop:
return "", fmt.Errorf("mail: bad character in quoted-string: %q", c)
}
}
- *p = (*p)[i+1:]
+ p.s = p.s[i+1:]
return string(qsb), nil
}
@@ -392,9 +407,9 @@ func (p *addrParser) consumeAtom(dot bool) (atom string, err error) {
return "", errors.New("mail: invalid string")
}
i := 1
- for ; i < p.len() && isAtext((*p)[i], dot); i++ {
+ for ; i < p.len() && isAtext(p.s[i], dot); i++ {
}
- atom, *p = string((*p)[:i]), (*p)[i:]
+ atom, p.s = string(p.s[:i]), p.s[i:]
return atom, nil
}
@@ -402,17 +417,17 @@ func (p *addrParser) consume(c byte) bool {
if p.empty() || p.peek() != c {
return false
}
- *p = (*p)[1:]
+ p.s = p.s[1:]
return true
}
// skipSpace skips the leading space and tab characters.
func (p *addrParser) skipSpace() {
- *p = bytes.TrimLeft(*p, " \t")
+ p.s = strings.TrimLeft(p.s, " \t")
}
func (p *addrParser) peek() byte {
- return (*p)[0]
+ return p.s[0]
}
func (p *addrParser) empty() bool {
@@ -420,7 +435,37 @@ func (p *addrParser) empty() bool {
}
func (p *addrParser) len() int {
- return len(*p)
+ return len(p.s)
+}
+
+func (p *addrParser) decodeRFC2047Word(s string) (string, error) {
+ if p.dec != nil {
+ return p.dec.DecodeHeader(s)
+ }
+
+ dec, err := rfc2047Decoder.Decode(s)
+ if err == nil {
+ return dec, nil
+ }
+
+ if _, ok := err.(charsetError); ok {
+ return s, err
+ }
+
+ // Ignore invalid RFC 2047 encoded-word errors.
+ return s, nil
+}
+
+var rfc2047Decoder = mime.WordDecoder{
+ CharsetReader: func(charset string, input io.Reader) (io.Reader, error) {
+ return nil, charsetError(charset)
+ },
+}
+
+type charsetError string
+
+func (e charsetError) Error() string {
+ return fmt.Sprintf("charset not supported: %q", string(e))
}
var atextChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
diff --git a/src/net/mail/message_test.go b/src/net/mail/message_test.go
index 6ba48be04f..43574c6188 100644
--- a/src/net/mail/message_test.go
+++ b/src/net/mail/message_test.go
@@ -6,7 +6,9 @@ package mail
import (
"bytes"
+ "io"
"io/ioutil"
+ "mime"
"reflect"
"strings"
"testing"
@@ -278,6 +280,175 @@ func TestAddressParsing(t *testing.T) {
}
}
+func TestAddressParser(t *testing.T) {
+ tests := []struct {
+ addrsStr string
+ exp []*Address
+ }{
+ // Bare address
+ {
+ `jdoe@machine.example`,
+ []*Address{{
+ Address: "jdoe@machine.example",
+ }},
+ },
+ // RFC 5322, Appendix A.1.1
+ {
+ `John Doe `,
+ []*Address{{
+ Name: "John Doe",
+ Address: "jdoe@machine.example",
+ }},
+ },
+ // RFC 5322, Appendix A.1.2
+ {
+ `"Joe Q. Public" `,
+ []*Address{{
+ Name: "Joe Q. Public",
+ Address: "john.q.public@example.com",
+ }},
+ },
+ {
+ `Mary Smith , jdoe@example.org, Who? `,
+ []*Address{
+ {
+ Name: "Mary Smith",
+ Address: "mary@x.test",
+ },
+ {
+ Address: "jdoe@example.org",
+ },
+ {
+ Name: "Who?",
+ Address: "one@y.test",
+ },
+ },
+ },
+ {
+ `, "Giant; \"Big\" Box" `,
+ []*Address{
+ {
+ Address: "boss@nil.test",
+ },
+ {
+ Name: `Giant; "Big" Box`,
+ Address: "sysservices@example.net",
+ },
+ },
+ },
+ // RFC 2047 "Q"-encoded ISO-8859-1 address.
+ {
+ `=?iso-8859-1?q?J=F6rg_Doe?= `,
+ []*Address{
+ {
+ Name: `Jörg Doe`,
+ Address: "joerg@example.com",
+ },
+ },
+ },
+ // RFC 2047 "Q"-encoded US-ASCII address. Dumb but legal.
+ {
+ `=?us-ascii?q?J=6Frg_Doe?= `,
+ []*Address{
+ {
+ Name: `Jorg Doe`,
+ Address: "joerg@example.com",
+ },
+ },
+ },
+ // RFC 2047 "Q"-encoded ISO-8859-15 address.
+ {
+ `=?ISO-8859-15?Q?J=F6rg_Doe?= `,
+ []*Address{
+ {
+ Name: `Jörg Doe`,
+ Address: "joerg@example.com",
+ },
+ },
+ },
+ // RFC 2047 "B"-encoded windows-1252 address.
+ {
+ `=?windows-1252?q?Andr=E9?= Pirard `,
+ []*Address{
+ {
+ Name: `André Pirard`,
+ Address: "PIRARD@vm1.ulg.ac.be",
+ },
+ },
+ },
+ // Custom example of RFC 2047 "B"-encoded ISO-8859-15 address.
+ {
+ `=?ISO-8859-15?B?SvZyZw==?= `,
+ []*Address{
+ {
+ Name: `Jörg`,
+ Address: "joerg@example.com",
+ },
+ },
+ },
+ // Custom example of RFC 2047 "B"-encoded UTF-8 address.
+ {
+ `=?UTF-8?B?SsO2cmc=?= `,
+ []*Address{
+ {
+ Name: `Jörg`,
+ Address: "joerg@example.com",
+ },
+ },
+ },
+ // Custom example with "." in name. For issue 4938
+ {
+ `Asem H. `,
+ []*Address{
+ {
+ Name: `Asem H.`,
+ Address: "noreply@example.com",
+ },
+ },
+ },
+ }
+
+ ap := AddressParser{WordDecoder: &mime.WordDecoder{
+ CharsetReader: func(charset string, input io.Reader) (io.Reader, error) {
+ in, err := ioutil.ReadAll(input)
+ if err != nil {
+ return nil, err
+ }
+
+ switch charset {
+ case "iso-8859-15":
+ in = bytes.Replace(in, []byte("\xf6"), []byte("ö"), -1)
+ case "windows-1252":
+ in = bytes.Replace(in, []byte("\xe9"), []byte("é"), -1)
+ }
+
+ return bytes.NewReader(in), nil
+ },
+ }}
+
+ for _, test := range tests {
+ if len(test.exp) == 1 {
+ addr, err := ap.Parse(test.addrsStr)
+ if err != nil {
+ t.Errorf("Failed parsing (single) %q: %v", test.addrsStr, err)
+ continue
+ }
+ if !reflect.DeepEqual([]*Address{addr}, test.exp) {
+ t.Errorf("Parse (single) of %q: got %+v, want %+v", test.addrsStr, addr, test.exp)
+ }
+ }
+
+ addrs, err := ap.ParseList(test.addrsStr)
+ if err != nil {
+ t.Errorf("Failed parsing (list) %q: %v", test.addrsStr, err)
+ continue
+ }
+ if !reflect.DeepEqual(addrs, test.exp) {
+ t.Errorf("Parse (list) of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp)
+ }
+ }
+}
+
func TestAddressFormatting(t *testing.T) {
tests := []struct {
addr *Address
diff --git a/src/net/main_test.go b/src/net/main_test.go
index a56b9cd5f9..f3f8b1a900 100644
--- a/src/net/main_test.go
+++ b/src/net/main_test.go
@@ -30,10 +30,16 @@ var (
// If external IPv4 connectivity exists, we can try dialing
// non-node/interface local scope IPv4 addresses.
+ // On Windows, Lookup APIs may not return IPv4-related
+ // resource records when a node has no external IPv4
+ // connectivity.
testIPv4 = flag.Bool("ipv4", true, "assume external IPv4 connectivity exists")
// If external IPv6 connectivity exists, we can try dialing
// non-node/interface local scope IPv6 addresses.
+ // On Windows, Lookup APIs may not return IPv6-related
+ // resource records when a node has no external IPv6
+ // connectivity.
testIPv6 = flag.Bool("ipv6", false, "assume external IPv6 connectivity exists")
)
@@ -43,16 +49,26 @@ func TestMain(m *testing.M) {
st := m.Run()
- testHookUninstaller.Do(func() { uninstallTestHooks() })
- if !testing.Short() {
- printLeakedGoroutines()
- printLeakedSockets()
+ testHookUninstaller.Do(uninstallTestHooks)
+ if testing.Verbose() {
+ printRunningGoroutines()
+ printInflightSockets()
printSocketStats()
}
forceCloseSockets()
os.Exit(st)
}
+type ipv6LinkLocalUnicastTest struct {
+ network, address string
+ nameLookup bool
+}
+
+var (
+ ipv6LinkLocalUnicastTCPTests []ipv6LinkLocalUnicastTest
+ ipv6LinkLocalUnicastUDPTests []ipv6LinkLocalUnicastTest
+)
+
func setupTestData() {
if supportsIPv4 {
resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
@@ -75,7 +91,8 @@ func setupTestData() {
resolveIPAddrTests = append(resolveIPAddrTests, resolveIPAddrTest{"ip6", "localhost", &IPAddr{IP: IPv6loopback}, nil})
}
- if ifi := loopbackInterface(); ifi != nil {
+ ifi := loopbackInterface()
+ if ifi != nil {
index := fmt.Sprintf("%v", ifi.Index)
resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
{"tcp6", "[fe80::1%" + ifi.Name + "]:1", &TCPAddr{IP: ParseIP("fe80::1"), Port: 1, Zone: zoneToString(ifi.Index)}, nil},
@@ -90,23 +107,60 @@ func setupTestData() {
{"ip6", "fe80::1%" + index, &IPAddr{IP: ParseIP("fe80::1"), Zone: index}, nil},
}...)
}
+
+ addr := ipv6LinkLocalUnicastAddr(ifi)
+ if addr != "" {
+ if runtime.GOOS != "dragonfly" {
+ ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
+ {"tcp", "[" + addr + "%" + ifi.Name + "]:0", false},
+ }...)
+ ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
+ {"udp", "[" + addr + "%" + ifi.Name + "]:0", false},
+ }...)
+ }
+ ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
+ {"tcp6", "[" + addr + "%" + ifi.Name + "]:0", false},
+ }...)
+ ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
+ {"udp6", "[" + addr + "%" + ifi.Name + "]:0", false},
+ }...)
+ switch runtime.GOOS {
+ case "darwin", "dragonfly", "freebsd", "openbsd", "netbsd":
+ ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
+ {"tcp", "[localhost%" + ifi.Name + "]:0", true},
+ {"tcp6", "[localhost%" + ifi.Name + "]:0", true},
+ }...)
+ ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
+ {"udp", "[localhost%" + ifi.Name + "]:0", true},
+ {"udp6", "[localhost%" + ifi.Name + "]:0", true},
+ }...)
+ case "linux":
+ ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
+ {"tcp", "[ip6-localhost%" + ifi.Name + "]:0", true},
+ {"tcp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
+ }...)
+ ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
+ {"udp", "[ip6-localhost%" + ifi.Name + "]:0", true},
+ {"udp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
+ }...)
+ }
+ }
}
-func printLeakedGoroutines() {
- gss := leakedGoroutines()
+func printRunningGoroutines() {
+ gss := runningGoroutines()
if len(gss) == 0 {
return
}
- fmt.Fprintf(os.Stderr, "Leaked goroutines:\n")
+ fmt.Fprintf(os.Stderr, "Running goroutines:\n")
for _, gs := range gss {
fmt.Fprintf(os.Stderr, "%v\n", gs)
}
fmt.Fprintf(os.Stderr, "\n")
}
-// leakedGoroutines returns a list of remaining goroutines used in
-// test cases.
-func leakedGoroutines() []string {
+// runningGoroutines returns a list of remaining goroutines.
+func runningGoroutines() []string {
var gss []string
b := make([]byte, 2<<20)
b = b[:runtime.Stack(b, true)]
@@ -125,12 +179,12 @@ func leakedGoroutines() []string {
return gss
}
-func printLeakedSockets() {
+func printInflightSockets() {
sos := sw.Sockets()
if len(sos) == 0 {
return
}
- fmt.Fprintf(os.Stderr, "Leaked sockets:\n")
+ fmt.Fprintf(os.Stderr, "Inflight sockets:\n")
for s, so := range sos {
fmt.Fprintf(os.Stderr, "%v: %v\n", s, so)
}
diff --git a/src/net/mockserver_test.go b/src/net/mockserver_test.go
index 62bcfa4022..dd6f4df3b9 100644
--- a/src/net/mockserver_test.go
+++ b/src/net/mockserver_test.go
@@ -186,7 +186,7 @@ func newDualStackServer(lns []streamListener) (*dualStackServer, error) {
for i := range dss.lns {
ln, err := Listen(dss.lns[i].network, JoinHostPort(dss.lns[i].address, dss.port))
if err != nil {
- for _, ln := range dss.lns {
+ for _, ln := range dss.lns[:i] {
ln.Listener.Close()
}
return nil, err
diff --git a/src/net/net_windows_test.go b/src/net/net_windows_test.go
index 21b47964a4..da03e10b36 100644
--- a/src/net/net_windows_test.go
+++ b/src/net/net_windows_test.go
@@ -15,16 +15,31 @@ import (
"time"
)
+func toErrno(err error) (syscall.Errno, bool) {
+ operr, ok := err.(*OpError)
+ if !ok {
+ return 0, false
+ }
+ syserr, ok := operr.Err.(*os.SyscallError)
+ if !ok {
+ return 0, false
+ }
+ errno, ok := syserr.Err.(syscall.Errno)
+ if !ok {
+ return 0, false
+ }
+ return errno, true
+}
+
+// TestAcceptIgnoreSomeErrors tests that windows TCPListener.AcceptTCP
+// handles broken connections. It verifies that broken connections do
+// not affect future connections.
func TestAcceptIgnoreSomeErrors(t *testing.T) {
- recv := func(ln Listener) (string, error) {
+ recv := func(ln Listener, ignoreSomeReadErrors bool) (string, error) {
c, err := ln.Accept()
if err != nil {
// Display windows errno in error message.
- operr, ok := err.(*OpError)
- if !ok {
- return "", err
- }
- errno, ok := operr.Err.(syscall.Errno)
+ errno, ok := toErrno(err)
if !ok {
return "", err
}
@@ -34,10 +49,14 @@ func TestAcceptIgnoreSomeErrors(t *testing.T) {
b := make([]byte, 100)
n, err := c.Read(b)
- if err != nil && err != io.EOF {
- return "", err
+ if err == nil || err == io.EOF {
+ return string(b[:n]), nil
}
- return string(b[:n]), nil
+ errno, ok := toErrno(err)
+ if ok && ignoreSomeReadErrors && (errno == syscall.ERROR_NETNAME_DELETED || errno == syscall.WSAECONNRESET) {
+ return "", nil
+ }
+ return "", err
}
send := func(addr string, data string) error {
@@ -121,13 +140,13 @@ func TestAcceptIgnoreSomeErrors(t *testing.T) {
}()
// Receive first or second connection.
- s, err := recv(ln)
+ s, err := recv(ln, true)
if err != nil {
t.Fatalf("recv failed: %v", err)
}
switch s {
case "":
- // First connection data is received, lets get second connection data.
+ // First connection data is received, let's get second connection data.
case "abc":
// First connection is lost forever, but that is ok.
return
@@ -136,7 +155,7 @@ func TestAcceptIgnoreSomeErrors(t *testing.T) {
}
// Get second connection data.
- s, err = recv(ln)
+ s, err = recv(ln, false)
if err != nil {
t.Fatalf("recv failed: %v", err)
}
diff --git a/src/net/platform_test.go b/src/net/platform_test.go
index b700091dc5..d6248520f3 100644
--- a/src/net/platform_test.go
+++ b/src/net/platform_test.go
@@ -14,7 +14,8 @@ import (
// testableNetwork reports whether network is testable on the current
// platform configuration.
func testableNetwork(network string) bool {
- switch ss := strings.Split(network, ":"); ss[0] {
+ ss := strings.Split(network, ":")
+ switch ss[0] {
case "ip+nopriv":
switch runtime.GOOS {
case "nacl":
@@ -46,6 +47,16 @@ func testableNetwork(network string) bool {
return false
}
}
+ switch ss[0] {
+ case "tcp4", "udp4", "ip4":
+ if !supportsIPv4 {
+ return false
+ }
+ case "tcp6", "udp6", "ip6":
+ if !supportsIPv6 {
+ return false
+ }
+ }
return true
}
diff --git a/src/net/sockopt_bsd.go b/src/net/sockopt_bsd.go
index 00e4dbf376..1b4a586a7e 100644
--- a/src/net/sockopt_bsd.go
+++ b/src/net/sockopt_bsd.go
@@ -25,7 +25,7 @@ func setDefaultSockopts(s, family, sotype int, ipv6only bool) error {
syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_PORTRANGE, syscall.IPV6_PORTRANGE_HIGH)
}
}
- if family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
+ if supportsIPv4map && family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
// Allow both IP versions even if the OS default
// is otherwise. Note that some operating systems
// never admit this option.
diff --git a/src/net/tcp_test.go b/src/net/tcp_test.go
index 6229df2869..2191c91fa3 100644
--- a/src/net/tcp_test.go
+++ b/src/net/tcp_test.go
@@ -58,7 +58,7 @@ func BenchmarkTCP6PersistentTimeout(b *testing.B) {
}
func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
+ testHookUninstaller.Do(uninstallTestHooks)
const msgLen = 512
conns := b.N
@@ -168,7 +168,7 @@ func BenchmarkTCP6ConcurrentReadWrite(b *testing.B) {
}
func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
+ testHookUninstaller.Do(uninstallTestHooks)
// The benchmark creates GOMAXPROCS client/server pairs.
// Each pair creates 4 goroutines: client reader/writer and server reader/writer.
@@ -367,39 +367,11 @@ func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
t.Skip("avoid external network")
}
if !supportsIPv6 {
- t.Skip("ipv6 is not supported")
- }
- ifi := loopbackInterface()
- if ifi == nil {
- t.Skip("loopback interface not found")
- }
- laddr := ipv6LinkLocalUnicastAddr(ifi)
- if laddr == "" {
- t.Skip("ipv6 unicast address on loopback not found")
+ t.Skip("IPv6 is not supported")
}
- type test struct {
- net, addr string
- nameLookup bool
- }
- var tests = []test{
- {"tcp", "[" + laddr + "%" + ifi.Name + "]:0", false},
- {"tcp6", "[" + laddr + "%" + ifi.Name + "]:0", false},
- }
- switch runtime.GOOS {
- case "darwin", "freebsd", "openbsd", "netbsd":
- tests = append(tests, []test{
- {"tcp", "[localhost%" + ifi.Name + "]:0", true},
- {"tcp6", "[localhost%" + ifi.Name + "]:0", true},
- }...)
- case "linux":
- tests = append(tests, []test{
- {"tcp", "[ip6-localhost%" + ifi.Name + "]:0", true},
- {"tcp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
- }...)
- }
- for i, tt := range tests {
- ln, err := Listen(tt.net, tt.addr)
+ for i, tt := range ipv6LinkLocalUnicastTCPTests {
+ ln, err := Listen(tt.network, tt.address)
if err != nil {
// It might return "LookupHost returned no
// suitable address" error on some platforms.
@@ -420,7 +392,7 @@ func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
t.Fatalf("got %v; expected a proper address with zone identifier", la)
}
- c, err := Dial(tt.net, ls.Listener.Addr().String())
+ c, err := Dial(tt.network, ls.Listener.Addr().String())
if err != nil {
t.Fatal(err)
}
diff --git a/src/net/tcpsock_posix.go b/src/net/tcpsock_posix.go
index 1f43521a9e..51a8e97915 100644
--- a/src/net/tcpsock_posix.go
+++ b/src/net/tcpsock_posix.go
@@ -13,11 +13,6 @@ import (
"time"
)
-// BUG(rsc): On OpenBSD, listening on the "tcp" network does not listen for
-// both IPv4 and IPv6 connections. This is due to the fact that IPv4 traffic
-// will not be routed to an IPv6 socket - two separate sockets are required
-// if both AFs are to be supported. See inet6(4) on OpenBSD for details.
-
func sockaddrToTCP(sa syscall.Sockaddr) Addr {
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
diff --git a/src/net/timeout_test.go b/src/net/timeout_test.go
index cafa3755f6..9688c21699 100644
--- a/src/net/timeout_test.go
+++ b/src/net/timeout_test.go
@@ -696,14 +696,18 @@ func TestWriteTimeoutFluctuation(t *testing.T) {
}
defer c.Close()
- max := time.NewTimer(time.Second)
+ d := time.Second
+ if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
+ d = 3 * time.Second // see golang.org/issue/10775
+ }
+ max := time.NewTimer(d)
defer max.Stop()
ch := make(chan error)
go timeoutTransmitter(c, 100*time.Millisecond, 50*time.Millisecond, 250*time.Millisecond, ch)
select {
case <-max.C:
- t.Fatal("Write took over 1s; expected 0.1s")
+ t.Fatalf("Write took over %v; expected 0.1s", d)
case err := <-ch:
if perr := parseWriteError(err); perr != nil {
t.Error(perr)
diff --git a/src/net/udp_test.go b/src/net/udp_test.go
index 2213468e79..b25f96a3fd 100644
--- a/src/net/udp_test.go
+++ b/src/net/udp_test.go
@@ -238,55 +238,32 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
t.Skip("avoid external network")
}
if !supportsIPv6 {
- t.Skip("ipv6 is not supported")
- }
- ifi := loopbackInterface()
- if ifi == nil {
- t.Skip("loopback interface not found")
- }
- laddr := ipv6LinkLocalUnicastAddr(ifi)
- if laddr == "" {
- t.Skip("ipv6 unicast address on loopback not found")
+ t.Skip("IPv6 is not supported")
}
- type test struct {
- net, addr string
- nameLookup bool
- }
- var tests = []test{
- {"udp", "[" + laddr + "%" + ifi.Name + "]:0", false},
- {"udp6", "[" + laddr + "%" + ifi.Name + "]:0", false},
- }
- // The first udp test fails on DragonFly - see issue 7473.
- if runtime.GOOS == "dragonfly" {
- tests = tests[1:]
- }
- switch runtime.GOOS {
- case "darwin", "dragonfly", "freebsd", "openbsd", "netbsd":
- tests = append(tests, []test{
- {"udp", "[localhost%" + ifi.Name + "]:0", true},
- {"udp6", "[localhost%" + ifi.Name + "]:0", true},
- }...)
- case "linux":
- tests = append(tests, []test{
- {"udp", "[ip6-localhost%" + ifi.Name + "]:0", true},
- {"udp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
- }...)
- }
- for _, tt := range tests {
- c1, err := ListenPacket(tt.net, tt.addr)
+ for i, tt := range ipv6LinkLocalUnicastUDPTests {
+ c1, err := ListenPacket(tt.network, tt.address)
if err != nil {
// It might return "LookupHost returned no
// suitable address" error on some platforms.
t.Log(err)
continue
}
- defer c1.Close()
+ ls, err := (&packetListener{PacketConn: c1}).newLocalServer()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ls.teardown()
+ ch := make(chan error, 1)
+ handler := func(ls *localPacketServer, c PacketConn) { packetTransponder(c, ch) }
+ if err := ls.buildup(handler); err != nil {
+ t.Fatal(err)
+ }
if la, ok := c1.LocalAddr().(*UDPAddr); !ok || !tt.nameLookup && la.Zone == "" {
t.Fatalf("got %v; expected a proper address with zone identifier", la)
}
- c2, err := Dial(tt.net, c1.LocalAddr().String())
+ c2, err := Dial(tt.network, ls.PacketConn.LocalAddr().String())
if err != nil {
t.Fatal(err)
}
@@ -302,12 +279,12 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
t.Fatal(err)
}
b := make([]byte, 32)
- if _, from, err := c1.ReadFrom(b); err != nil {
+ if _, err := c2.Read(b); err != nil {
t.Fatal(err)
- } else {
- if ra, ok := from.(*UDPAddr); !ok || !tt.nameLookup && ra.Zone == "" {
- t.Fatalf("got %v; expected a proper address with zone identifier", ra)
- }
+ }
+
+ for err := range ch {
+ t.Errorf("#%d: %v", i, err)
}
}
}
diff --git a/src/net/udpsock_plan9.go b/src/net/udpsock_plan9.go
index 949f666d74..7bbcf6e4cb 100644
--- a/src/net/udpsock_plan9.go
+++ b/src/net/udpsock_plan9.go
@@ -200,9 +200,16 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
}
// ListenMulticastUDP listens for incoming multicast UDP packets
-// addressed to the group address gaddr on ifi, which specifies the
-// interface to join. ListenMulticastUDP uses default multicast
-// interface if ifi is nil.
-func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
- return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: gaddr, Err: syscall.EPLAN9}
+// addressed to the group address gaddr on the interface ifi.
+// Network must be "udp", "udp4" or "udp6".
+// ListenMulticastUDP uses the system-assigned multicast interface
+// when ifi is nil, although this is not recommended because the
+// assignment depends on platforms and sometimes it might require
+// routing configuration.
+//
+// ListenMulticastUDP is just for convenience of simple, small
+// applications. There are golang.org/x/net/ipv4 and
+// golang.org/x/net/ipv6 packages for general purpose uses.
+func ListenMulticastUDP(network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
+ return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr, Err: syscall.EPLAN9}
}
diff --git a/src/net/udpsock_posix.go b/src/net/udpsock_posix.go
index 2e43068be3..36ada176a1 100644
--- a/src/net/udpsock_posix.go
+++ b/src/net/udpsock_posix.go
@@ -220,32 +220,39 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
}
// ListenMulticastUDP listens for incoming multicast UDP packets
-// addressed to the group address gaddr on ifi, which specifies the
-// interface to join. ListenMulticastUDP uses default multicast
-// interface if ifi is nil.
-func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
- switch net {
+// addressed to the group address gaddr on the interface ifi.
+// Network must be "udp", "udp4" or "udp6".
+// ListenMulticastUDP uses the system-assigned multicast interface
+// when ifi is nil, although this is not recommended because the
+// assignment depends on platforms and sometimes it might require
+// routing configuration.
+//
+// ListenMulticastUDP is just for convenience of simple, small
+// applications. There are golang.org/x/net/ipv4 and
+// golang.org/x/net/ipv6 packages for general purpose uses.
+func ListenMulticastUDP(network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
+ switch network {
case "udp", "udp4", "udp6":
default:
- return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: gaddr, Err: UnknownNetworkError(net)}
+ return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr, Err: UnknownNetworkError(network)}
}
if gaddr == nil || gaddr.IP == nil {
- return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: gaddr, Err: errMissingAddress}
+ return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr, Err: errMissingAddress}
}
- fd, err := internetSocket(net, gaddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen")
+ fd, err := internetSocket(network, gaddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen")
if err != nil {
- return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: gaddr, Err: err}
+ return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr, Err: err}
}
c := newUDPConn(fd)
if ip4 := gaddr.IP.To4(); ip4 != nil {
if err := listenIPv4MulticastUDP(c, ifi, ip4); err != nil {
c.Close()
- return nil, &OpError{Op: "listen", Net: net, Source: c.fd.laddr, Addr: &IPAddr{IP: ip4}, Err: err}
+ return nil, &OpError{Op: "listen", Net: network, Source: c.fd.laddr, Addr: &IPAddr{IP: ip4}, Err: err}
}
} else {
if err := listenIPv6MulticastUDP(c, ifi, gaddr.IP); err != nil {
c.Close()
- return nil, &OpError{Op: "listen", Net: net, Source: c.fd.laddr, Addr: &IPAddr{IP: gaddr.IP}, Err: err}
+ return nil, &OpError{Op: "listen", Net: network, Source: c.fd.laddr, Addr: &IPAddr{IP: gaddr.IP}, Err: err}
}
}
return c, nil
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index 877b2efd84..9a99f742d6 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -4388,18 +4388,16 @@ func TestCallGC(t *testing.T) {
type funcLayoutTest struct {
rcvr, t Type
size, argsize, retOffset uintptr
- stack []byte
+ stack []byte // pointer bitmap: 1 is pointer, 0 is scalar (or uninitialized)
gc []byte
}
var funcLayoutTests []funcLayoutTest
func init() {
- var argAlign = PtrSize
- var naclExtra []byte
+ var argAlign uintptr = PtrSize
if runtime.GOARCH == "amd64p32" {
argAlign = 2 * PtrSize
- naclExtra = append(naclExtra, BitsScalar)
}
roundup := func(x uintptr, a uintptr) uintptr {
return (x + a - 1) / a * a
@@ -4412,17 +4410,15 @@ func init() {
6 * PtrSize,
4 * PtrSize,
4 * PtrSize,
- []byte{BitsPointer, BitsScalar, BitsPointer},
- []byte{BitsPointer, BitsScalar, BitsPointer, BitsScalar, BitsPointer, BitsScalar},
+ []byte{1, 0, 1},
+ []byte{1, 0, 1, 0, 1},
})
- var r, s []byte
+ var r []byte
if PtrSize == 4 {
- r = []byte{BitsScalar, BitsScalar, BitsScalar, BitsPointer}
- s = append([]byte{BitsScalar, BitsScalar, BitsScalar, BitsPointer, BitsScalar}, naclExtra...)
+ r = []byte{0, 0, 0, 1}
} else {
- r = []byte{BitsScalar, BitsScalar, BitsPointer}
- s = []byte{BitsScalar, BitsScalar, BitsPointer, BitsScalar}
+ r = []byte{0, 0, 1}
}
funcLayoutTests = append(funcLayoutTests,
funcLayoutTest{
@@ -4432,7 +4428,7 @@ func init() {
roundup(3*4, PtrSize) + PtrSize + 2,
roundup(roundup(3*4, PtrSize)+PtrSize+2, argAlign),
r,
- s,
+ r,
})
funcLayoutTests = append(funcLayoutTests,
@@ -4442,8 +4438,8 @@ func init() {
4 * PtrSize,
4 * PtrSize,
4 * PtrSize,
- []byte{BitsPointer, BitsScalar, BitsPointer, BitsPointer},
- []byte{BitsPointer, BitsScalar, BitsPointer, BitsPointer},
+ []byte{1, 0, 1, 1},
+ []byte{1, 0, 1, 1},
})
type S struct {
@@ -4457,8 +4453,8 @@ func init() {
4 * PtrSize,
4 * PtrSize,
4 * PtrSize,
- []byte{BitsScalar, BitsScalar, BitsPointer, BitsPointer},
- []byte{BitsScalar, BitsScalar, BitsPointer, BitsPointer},
+ []byte{0, 0, 1, 1},
+ []byte{0, 0, 1, 1},
})
funcLayoutTests = append(funcLayoutTests,
@@ -4468,8 +4464,8 @@ func init() {
roundup(3*PtrSize, argAlign),
3 * PtrSize,
roundup(3*PtrSize, argAlign),
- []byte{BitsPointer, BitsScalar, BitsPointer},
- append([]byte{BitsPointer, BitsScalar, BitsPointer}, naclExtra...),
+ []byte{1, 0, 1},
+ []byte{1, 0, 1},
})
funcLayoutTests = append(funcLayoutTests,
@@ -4480,7 +4476,7 @@ func init() {
PtrSize,
roundup(PtrSize, argAlign),
[]byte{},
- append([]byte{BitsScalar}, naclExtra...),
+ []byte{},
})
funcLayoutTests = append(funcLayoutTests,
@@ -4491,7 +4487,7 @@ func init() {
0,
0,
[]byte{},
- []byte{BitsScalar},
+ []byte{},
})
funcLayoutTests = append(funcLayoutTests,
@@ -4501,8 +4497,8 @@ func init() {
2 * PtrSize,
2 * PtrSize,
2 * PtrSize,
- []byte{BitsPointer},
- []byte{BitsPointer, BitsScalar},
+ []byte{1},
+ []byte{1},
// Note: this one is tricky, as the receiver is not a pointer. But we
// pass the receiver by reference to the autogenerated pointer-receiver
// version of the function.
@@ -4532,3 +4528,139 @@ func TestFuncLayout(t *testing.T) {
}
}
}
+
+func verifyGCBits(t *testing.T, typ Type, bits []byte) {
+ heapBits := GCBits(New(typ).Interface())
+ if !bytes.Equal(heapBits, bits) {
+ t.Errorf("heapBits incorrect for %v\nhave %v\nwant %v", typ, heapBits, bits)
+ }
+}
+
+func TestGCBits(t *testing.T) {
+ verifyGCBits(t, TypeOf((*byte)(nil)), []byte{1})
+
+ // Building blocks for types seen by the compiler (like [2]Xscalar).
+ // The compiler will create the type structures for the derived types,
+ // including their GC metadata.
+ type Xscalar struct{ x uintptr }
+ type Xptr struct{ x *byte }
+ type Xptrscalar struct {
+ *byte
+ uintptr
+ }
+ type Xscalarptr struct {
+ uintptr
+ *byte
+ }
+
+ var Tscalar, Tptr, Tscalarptr, Tptrscalar Type
+ {
+ // Building blocks for types constructed by reflect.
+ // This code is in a separate block so that code below
+ // cannot accidentally refer to these.
+ // The compiler must NOT see types derived from these
+ // (for example, [2]Scalar must NOT appear in the program),
+ // or else reflect will use it instead of having to construct one.
+ // The goal is to test the construction.
+ type Scalar struct{ x uintptr }
+ type Ptr struct{ x *byte }
+ type Ptrscalar struct {
+ *byte
+ uintptr
+ }
+ type Scalarptr struct {
+ uintptr
+ *byte
+ }
+ Tscalar = TypeOf(Scalar{})
+ Tptr = TypeOf(Ptr{})
+ Tscalarptr = TypeOf(Scalarptr{})
+ Tptrscalar = TypeOf(Ptrscalar{})
+ }
+
+ empty := []byte{}
+
+ verifyGCBits(t, TypeOf(Xscalar{}), empty)
+ verifyGCBits(t, Tscalar, empty)
+ verifyGCBits(t, TypeOf(Xptr{}), lit(1))
+ verifyGCBits(t, Tptr, lit(1))
+ verifyGCBits(t, TypeOf(Xscalarptr{}), lit(0, 1))
+ verifyGCBits(t, Tscalarptr, lit(0, 1))
+ verifyGCBits(t, TypeOf(Xptrscalar{}), lit(1))
+ verifyGCBits(t, Tptrscalar, lit(1))
+
+ verifyGCBits(t, TypeOf([0]Xptr{}), empty)
+ verifyGCBits(t, ArrayOf(0, Tptr), empty)
+ verifyGCBits(t, TypeOf([1]Xptrscalar{}), lit(1))
+ verifyGCBits(t, ArrayOf(1, Tptrscalar), lit(1))
+ verifyGCBits(t, TypeOf([2]Xscalar{}), empty)
+ verifyGCBits(t, ArrayOf(2, Tscalar), empty)
+ verifyGCBits(t, TypeOf([100]Xscalar{}), empty)
+ verifyGCBits(t, ArrayOf(100, Tscalar), empty)
+ verifyGCBits(t, TypeOf([2]Xptr{}), lit(1, 1))
+ verifyGCBits(t, ArrayOf(2, Tptr), lit(1, 1))
+ verifyGCBits(t, TypeOf([100]Xptr{}), rep(100, lit(1)))
+ verifyGCBits(t, ArrayOf(100, Tptr), rep(100, lit(1)))
+ verifyGCBits(t, TypeOf([2]Xscalarptr{}), lit(0, 1, 0, 1))
+ verifyGCBits(t, ArrayOf(2, Tscalarptr), lit(0, 1, 0, 1))
+ verifyGCBits(t, TypeOf([100]Xscalarptr{}), rep(100, lit(0, 1)))
+ verifyGCBits(t, ArrayOf(100, Tscalarptr), rep(100, lit(0, 1)))
+ verifyGCBits(t, TypeOf([2]Xptrscalar{}), lit(1, 0, 1))
+ verifyGCBits(t, ArrayOf(2, Tptrscalar), lit(1, 0, 1))
+ verifyGCBits(t, TypeOf([100]Xptrscalar{}), rep(100, lit(1, 0)))
+ verifyGCBits(t, ArrayOf(100, Tptrscalar), rep(100, lit(1, 0)))
+ verifyGCBits(t, TypeOf([1][100]Xptrscalar{}), rep(100, lit(1, 0)))
+ verifyGCBits(t, ArrayOf(1, ArrayOf(100, Tptrscalar)), rep(100, lit(1, 0)))
+ verifyGCBits(t, TypeOf([2][100]Xptrscalar{}), rep(200, lit(1, 0)))
+ verifyGCBits(t, ArrayOf(2, ArrayOf(100, Tptrscalar)), rep(200, lit(1, 0)))
+
+ verifyGCBits(t, TypeOf((chan [100]Xscalar)(nil)), lit(1))
+ verifyGCBits(t, ChanOf(BothDir, ArrayOf(100, Tscalar)), lit(1))
+
+ verifyGCBits(t, TypeOf((func([100]Xscalarptr))(nil)), lit(1))
+ verifyGCBits(t, FuncOf([]Type{ArrayOf(100, Tscalarptr)}, nil, false), lit(1))
+
+ verifyGCBits(t, TypeOf((map[[100]Xscalarptr]Xscalar)(nil)), lit(1))
+ verifyGCBits(t, MapOf(ArrayOf(100, Tscalarptr), Tscalar), lit(1))
+
+ verifyGCBits(t, TypeOf((*[100]Xscalar)(nil)), lit(1))
+ verifyGCBits(t, PtrTo(ArrayOf(100, Tscalar)), lit(1))
+
+ verifyGCBits(t, TypeOf(([][100]Xscalar)(nil)), lit(1))
+ verifyGCBits(t, SliceOf(ArrayOf(100, Tscalar)), lit(1))
+
+ hdr := make([]byte, 8/PtrSize)
+ verifyGCBits(t, MapBucketOf(Tscalar, Tptr), join(hdr, rep(8, lit(0)), rep(8, lit(1)), lit(1)))
+ verifyGCBits(t, MapBucketOf(Tscalarptr, Tptr), join(hdr, rep(8, lit(0, 1)), rep(8, lit(1)), lit(1)))
+ verifyGCBits(t, MapBucketOf(Tscalar, Tscalar), empty)
+ verifyGCBits(t, MapBucketOf(ArrayOf(2, Tscalarptr), ArrayOf(3, Tptrscalar)), join(hdr, rep(8*2, lit(0, 1)), rep(8*3, lit(1, 0)), lit(1)))
+ verifyGCBits(t, MapBucketOf(ArrayOf(64/PtrSize, Tscalarptr), ArrayOf(64/PtrSize, Tptrscalar)), join(hdr, rep(8*64/PtrSize, lit(0, 1)), rep(8*64/PtrSize, lit(1, 0)), lit(1)))
+ verifyGCBits(t, MapBucketOf(ArrayOf(64/PtrSize+1, Tscalarptr), ArrayOf(64/PtrSize, Tptrscalar)), join(hdr, rep(8, lit(1)), rep(8*64/PtrSize, lit(1, 0)), lit(1)))
+ verifyGCBits(t, MapBucketOf(ArrayOf(64/PtrSize, Tscalarptr), ArrayOf(64/PtrSize+1, Tptrscalar)), join(hdr, rep(8*64/PtrSize, lit(0, 1)), rep(8, lit(1)), lit(1)))
+ verifyGCBits(t, MapBucketOf(ArrayOf(64/PtrSize+1, Tscalarptr), ArrayOf(64/PtrSize+1, Tptrscalar)), join(hdr, rep(8, lit(1)), rep(8, lit(1)), lit(1)))
+}
+
+func rep(n int, b []byte) []byte { return bytes.Repeat(b, n) }
+func join(b ...[]byte) []byte { return bytes.Join(b, nil) }
+func lit(x ...byte) []byte { return x }
+
+func TestTypeOfTypeOf(t *testing.T) {
+ // Check that all the type constructors return concrete *rtype implementations.
+ // It's difficult to test directly because the reflect package is only at arm's length.
+ // The easiest thing to do is just call a function that crashes if it doesn't get an *rtype.
+ check := func(name string, typ Type) {
+ if underlying := TypeOf(typ).String(); underlying != "*reflect.rtype" {
+ t.Errorf("%v returned %v, not *reflect.rtype", name, underlying)
+ }
+ }
+
+ type T struct{ int }
+ check("TypeOf", TypeOf(T{}))
+
+ check("ArrayOf", ArrayOf(10, TypeOf(T{})))
+ check("ChanOf", ChanOf(BothDir, TypeOf(T{})))
+ check("FuncOf", FuncOf([]Type{TypeOf(T{})}, nil, false))
+ check("MapOf", MapOf(TypeOf(T{}), TypeOf(T{})))
+ check("PtrTo", PtrTo(TypeOf(T{})))
+ check("SliceOf", SliceOf(TypeOf(T{})))
+}
diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go
index c89e9c1298..a4e2e7e28c 100644
--- a/src/reflect/export_test.go
+++ b/src/reflect/export_test.go
@@ -4,6 +4,8 @@
package reflect
+import "unsafe"
+
// MakeRO returns a copy of v with the read-only flag set.
func MakeRO(v Value) Value {
v.flag |= flagRO
@@ -18,8 +20,6 @@ func IsRO(v Value) bool {
var CallGC = &callGC
const PtrSize = ptrSize
-const BitsPointer = bitsPointer
-const BitsScalar = bitsScalar
func FuncLayout(t Type, rcvr Type) (frametype Type, argSize, retOffset uintptr, stack []byte, gc []byte, ptrs bool) {
var ft *rtype
@@ -30,15 +30,15 @@ func FuncLayout(t Type, rcvr Type) (frametype Type, argSize, retOffset uintptr,
ft, argSize, retOffset, s, _ = funcLayout(t.(*rtype), nil)
}
frametype = ft
- for i := uint32(0); i < s.n; i += 2 {
- stack = append(stack, s.data[i/8]>>(i%8)&3)
+ for i := uint32(0); i < s.n; i++ {
+ stack = append(stack, s.data[i/8]>>(i%8)&1)
}
if ft.kind&kindGCProg != 0 {
panic("can't handle gc programs")
}
- gcdata := (*[1000]byte)(ft.gc[0])
- for i := uintptr(0); i < ft.size/ptrSize; i++ {
- gc = append(gc, gcdata[i/2]>>(i%2*4+2)&3)
+ gcdata := (*[1000]byte)(unsafe.Pointer(ft.gcdata))
+ for i := uintptr(0); i < ft.ptrdata/ptrSize; i++ {
+ gc = append(gc, gcdata[i/8]>>(i%8)&1)
}
ptrs = ft.kind&kindNoPointers == 0
return
@@ -53,3 +53,11 @@ func TypeLinks() []string {
}
return r
}
+
+var GCBits = gcbits
+
+func gcbits(interface{}) []byte // provided by runtime
+
+func MapBucketOf(x, y Type) Type {
+ return bucketOf(x.(*rtype), y.(*rtype))
+}
diff --git a/src/reflect/type.go b/src/reflect/type.go
index 5315bd3971..e55a0d146c 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -247,17 +247,17 @@ const (
type rtype struct {
size uintptr
ptrdata uintptr
- hash uint32 // hash of type; avoids computation in hash tables
- _ uint8 // unused/padding
- align uint8 // alignment of variable with this type
- fieldAlign uint8 // alignment of struct field with this type
- kind uint8 // enumeration for C
- alg *typeAlg // algorithm table
- gc [2]unsafe.Pointer // garbage collection data
- string *string // string form; unnecessary but undeniably useful
- *uncommonType // (relatively) uncommon fields
- ptrToThis *rtype // type for pointer to this type, if used in binary or has methods
- zero unsafe.Pointer // pointer to zero value
+ hash uint32 // hash of type; avoids computation in hash tables
+ _ uint8 // unused/padding
+ align uint8 // alignment of variable with this type
+ fieldAlign uint8 // alignment of struct field with this type
+ kind uint8 // enumeration for C
+ alg *typeAlg // algorithm table
+ gcdata *byte // garbage collection data
+ string *string // string form; unnecessary but undeniably useful
+ *uncommonType // (relatively) uncommon fields
+ ptrToThis *rtype // type for pointer to this type, if used in binary or has methods
+ zero unsafe.Pointer // pointer to zero value
}
// a copy of runtime.typeAlg
@@ -1087,7 +1087,6 @@ func (t *rtype) ptrTo() *rtype {
p.uncommonType = nil
p.ptrToThis = nil
- p.zero = unsafe.Pointer(&make([]byte, p.size)[0])
p.elem = t
ptrMap.m[t] = p
@@ -1467,7 +1466,6 @@ func ChanOf(dir ChanDir, t Type) Type {
ch.elem = typ
ch.uncommonType = nil
ch.ptrToThis = nil
- ch.zero = unsafe.Pointer(&make([]byte, ch.size)[0])
return cachePut(ckey, &ch.rtype)
}
@@ -1530,7 +1528,6 @@ func MapOf(key, elem Type) Type {
mt.reflexivekey = isReflexive(ktyp)
mt.uncommonType = nil
mt.ptrToThis = nil
- mt.zero = unsafe.Pointer(&make([]byte, mt.size)[0])
return cachePut(ckey, &mt.rtype)
}
@@ -1610,10 +1607,9 @@ func FuncOf(in, out []Type, variadic bool) Type {
ft.string = &str
ft.uncommonType = nil
ft.ptrToThis = nil
- ft.zero = unsafe.Pointer(&make([]byte, ft.size)[0])
funcLookupCache.m[hash] = append(funcLookupCache.m[hash], &ft.rtype)
- return ft
+ return &ft.rtype
}
// funcStr builds a string representation of a funcType.
@@ -1674,125 +1670,14 @@ func isReflexive(t *rtype) bool {
}
}
-// gcProg is a helper type for generatation of GC pointer info.
-type gcProg struct {
- gc []byte
- size uintptr // size of type in bytes
- hasPtr bool
- lastZero uintptr // largest offset of a zero-byte field
-}
-
-func (gc *gcProg) append(v byte) {
- gc.align(unsafe.Sizeof(uintptr(0)))
- gc.appendWord(v)
-}
-
-// Appends t's type info to the current program.
-func (gc *gcProg) appendProg(t *rtype) {
- gc.align(uintptr(t.align))
- if !t.pointers() {
- gc.size += t.size
- if t.size == 0 {
- gc.lastZero = gc.size
- }
- return
- }
- switch t.Kind() {
- default:
- panic("reflect: non-pointer type marked as having pointers")
- case Ptr, UnsafePointer, Chan, Func, Map:
- gc.appendWord(bitsPointer)
- case Slice:
- gc.appendWord(bitsPointer)
- gc.appendWord(bitsScalar)
- gc.appendWord(bitsScalar)
- case String:
- gc.appendWord(bitsPointer)
- gc.appendWord(bitsScalar)
- case Array:
- c := t.Len()
- e := t.Elem().common()
- for i := 0; i < c; i++ {
- gc.appendProg(e)
- }
- case Interface:
- gc.appendWord(bitsPointer)
- gc.appendWord(bitsPointer)
- case Struct:
- oldsize := gc.size
- c := t.NumField()
- for i := 0; i < c; i++ {
- gc.appendProg(t.Field(i).Type.common())
- }
- if gc.size > oldsize+t.size {
- panic("reflect: struct components are larger than the struct itself")
- }
- gc.size = oldsize + t.size
- }
-}
-
-func (gc *gcProg) appendWord(v byte) {
- ptrsize := unsafe.Sizeof(uintptr(0))
- if gc.size%ptrsize != 0 {
- panic("reflect: unaligned GC program")
- }
- nptr := gc.size / ptrsize
- for uintptr(len(gc.gc)) < nptr/2+1 {
- gc.gc = append(gc.gc, 0x44) // BitsScalar
- }
- gc.gc[nptr/2] &= ^(3 << ((nptr%2)*4 + 2))
- gc.gc[nptr/2] |= v << ((nptr%2)*4 + 2)
- gc.size += ptrsize
- if v == bitsPointer {
- gc.hasPtr = true
- }
-}
-
-func (gc *gcProg) finalize() (unsafe.Pointer, bool) {
- if gc.size == 0 {
- return nil, false
- }
- if gc.lastZero == gc.size {
- gc.size++
- }
- ptrsize := unsafe.Sizeof(uintptr(0))
- gc.align(ptrsize)
- nptr := gc.size / ptrsize
- for uintptr(len(gc.gc)) < nptr/2+1 {
- gc.gc = append(gc.gc, 0x44) // BitsScalar
- }
- // If number of words is odd, repeat the mask twice.
- // Compiler does the same.
- if nptr%2 != 0 {
- for i := uintptr(0); i < nptr; i++ {
- gc.appendWord(extractGCWord(gc.gc, i))
- }
- }
- return unsafe.Pointer(&gc.gc[0]), gc.hasPtr
-}
-
-func extractGCWord(gc []byte, i uintptr) byte {
- return (gc[i/2] >> ((i%2)*4 + 2)) & 3
-}
-
-func (gc *gcProg) align(a uintptr) {
- gc.size = align(gc.size, a)
-}
-
-// These constants must stay in sync with ../runtime/mbitmap.go.
-const (
- bitsScalar = 1
- bitsPointer = 2
-)
-
// Make sure these routines stay in sync with ../../runtime/hashmap.go!
// These types exist only for GC, so we only fill out GC relevant info.
// Currently, that's just size and the GC program. We also fill in string
// for possible debugging use.
const (
- bucketSize = 8
- maxKeySize = 128
- maxValSize = 128
+ bucketSize uintptr = 8
+ maxKeySize uintptr = 128
+ maxValSize uintptr = 128
)
func bucketOf(ktyp, etyp *rtype) *rtype {
@@ -1809,33 +1694,70 @@ func bucketOf(ktyp, etyp *rtype) *rtype {
if etyp.size > maxValSize {
etyp = PtrTo(etyp).(*rtype)
}
- ptrsize := unsafe.Sizeof(uintptr(0))
- var gc gcProg
- // topbits
- for i := 0; i < int(bucketSize*unsafe.Sizeof(uint8(0))/ptrsize); i++ {
- gc.append(bitsScalar)
+ // Prepare GC data if any.
+ // A bucket is at most bucketSize*(1+maxKeySize+maxValSize)+2*ptrSize bytes,
+ // or 2072 bytes, or 259 pointer-size words, or 33 bytes of pointer bitmap.
+ // Normally the enforced limit on pointer maps is 16 bytes,
+ // but larger ones are acceptable, 33 bytes isn't too too big,
+ // and it's easier to generate a pointer bitmap than a GC program.
+ // Note that since the key and value are known to be <= 128 bytes,
+ // they're guaranteed to have bitmaps instead of GC programs.
+ var gcdata *byte
+ var ptrdata uintptr
+ if kind != kindNoPointers {
+ nptr := (bucketSize*(1+ktyp.size+etyp.size) + ptrSize) / ptrSize
+ mask := make([]byte, (nptr+7)/8)
+ base := bucketSize / ptrSize
+
+ if ktyp.kind&kindNoPointers == 0 {
+ if ktyp.kind&kindGCProg != 0 {
+ panic("reflect: unexpected GC program in MapOf")
+ }
+ kmask := (*[16]byte)(unsafe.Pointer(ktyp.gcdata))
+ for i := uintptr(0); i < ktyp.size/ptrSize; i++ {
+ if (kmask[i/8]>>(i%8))&1 != 0 {
+ for j := uintptr(0); j < bucketSize; j++ {
+ word := base + j*ktyp.size/ptrSize + i
+ mask[word/8] |= 1 << (word % 8)
+ }
+ }
+ }
+ }
+ base += bucketSize * ktyp.size / ptrSize
+
+ if etyp.kind&kindNoPointers == 0 {
+ if etyp.kind&kindGCProg != 0 {
+ panic("reflect: unexpected GC program in MapOf")
+ }
+ emask := (*[16]byte)(unsafe.Pointer(etyp.gcdata))
+ for i := uintptr(0); i < etyp.size/ptrSize; i++ {
+ if (emask[i/8]>>(i%8))&1 != 0 {
+ for j := uintptr(0); j < bucketSize; j++ {
+ word := base + j*etyp.size/ptrSize + i
+ mask[word/8] |= 1 << (word % 8)
+ }
+ }
+ }
+ }
+ base += bucketSize * etyp.size / ptrSize
+
+ word := base
+ mask[word/8] |= 1 << (word % 8)
+ gcdata = &mask[0]
+ ptrdata = (word + 1) * ptrSize
}
- // keys
- for i := 0; i < bucketSize; i++ {
- gc.appendProg(ktyp)
- }
- // values
- for i := 0; i < bucketSize; i++ {
- gc.appendProg(etyp)
- }
- // overflow
- gc.append(bitsPointer)
- ptrdata := gc.size
+
+ size := bucketSize*(1+ktyp.size+etyp.size) + ptrSize
if runtime.GOARCH == "amd64p32" {
- gc.append(bitsScalar)
+ size += ptrSize
}
b := new(rtype)
- b.size = gc.size
+ b.size = size
b.ptrdata = ptrdata
b.kind = kind
- b.gc[0], _ = gc.finalize()
+ b.gcdata = gcdata
s := "bucket(" + *ktyp.string + "," + *etyp.string + ")"
b.string = &s
return b
@@ -1871,7 +1793,6 @@ func SliceOf(t Type) Type {
slice.elem = typ
slice.uncommonType = nil
slice.ptrToThis = nil
- slice.zero = unsafe.Pointer(&make([]byte, slice.size)[0])
return cachePut(ckey, &slice.rtype)
}
@@ -1927,26 +1848,86 @@ func ArrayOf(count int, elem Type) Type {
array.fieldAlign = typ.fieldAlign
array.uncommonType = nil
array.ptrToThis = nil
- if array.size > 0 {
- zero := make([]byte, array.size)
- array.zero = unsafe.Pointer(&zero[0])
- }
array.len = uintptr(count)
array.slice = slice.(*rtype)
- var gc gcProg
- // TODO(sbinet): count could be possibly very large.
- // use insArray directives from ../runtime/mbitmap.go.
- for i := 0; i < count; i++ {
- gc.appendProg(typ)
- }
-
- var hasPtr bool
- array.gc[0], hasPtr = gc.finalize()
- if !hasPtr {
+ array.kind &^= kindNoPointers
+ switch {
+ case typ.kind&kindNoPointers != 0 || array.size == 0:
+ // No pointers.
array.kind |= kindNoPointers
- } else {
- array.kind &^= kindNoPointers
+ array.gcdata = nil
+ array.ptrdata = 0
+
+ case count == 1:
+ // In memory, 1-element array looks just like the element.
+ array.kind |= typ.kind & kindGCProg
+ array.gcdata = typ.gcdata
+ array.ptrdata = typ.ptrdata
+
+ case typ.kind&kindGCProg == 0 && array.size <= 16*8*ptrSize:
+ // Element is small with pointer mask; array is still small.
+ // Create direct pointer mask by turning each 1 bit in elem
+ // into count 1 bits in larger mask.
+ mask := make([]byte, (array.ptrdata/ptrSize+7)/8)
+ elemMask := (*[1 << 30]byte)(unsafe.Pointer(typ.gcdata))[:]
+ elemWords := typ.size / ptrSize
+ for j := uintptr(0); j < typ.ptrdata/ptrSize; j++ {
+ if (elemMask[j/8]>>(j%8))&1 != 0 {
+ for i := uintptr(0); i < array.len; i++ {
+ k := i*elemWords + j
+ mask[k/8] |= 1 << (k % 8)
+ }
+ }
+ }
+ array.gcdata = &mask[0]
+
+ default:
+ // Create program that emits one element
+ // and then repeats to make the array.
+ prog := []byte{0, 0, 0, 0} // will be length of prog
+ elemGC := (*[1 << 30]byte)(unsafe.Pointer(typ.gcdata))[:]
+ elemPtrs := typ.ptrdata / ptrSize
+ if typ.kind&kindGCProg == 0 {
+ // Element is small with pointer mask; use as literal bits.
+ mask := elemGC
+ // Emit 120-bit chunks of full bytes (max is 127 but we avoid using partial bytes).
+ var n uintptr
+ for n = elemPtrs; n > 120; n -= 120 {
+ prog = append(prog, 120)
+ prog = append(prog, mask[:15]...)
+ mask = mask[15:]
+ }
+ prog = append(prog, byte(n))
+ prog = append(prog, mask[:(n+7)/8]...)
+ } else {
+ // Element has GC program; emit one element.
+ elemProg := elemGC[4 : 4+*(*uint32)(unsafe.Pointer(&elemGC[0]))-1]
+ prog = append(prog, elemProg...)
+ }
+ // Pad from ptrdata to size.
+ elemWords := typ.size / ptrSize
+ if elemPtrs < elemWords {
+ // Emit literal 0 bit, then repeat as needed.
+ prog = append(prog, 0x01, 0x00)
+ if elemPtrs+1 < elemWords {
+ prog = append(prog, 0x81)
+ prog = appendVarint(prog, elemWords-elemPtrs-1)
+ }
+ }
+ // Repeat count-1 times.
+ if elemWords < 0x80 {
+ prog = append(prog, byte(elemWords|0x80))
+ } else {
+ prog = append(prog, 0x80)
+ prog = appendVarint(prog, elemWords)
+ }
+ prog = appendVarint(prog, uintptr(count)-1)
+ prog = append(prog, 0)
+ *(*uint32)(unsafe.Pointer(&prog[0])) = uint32(len(prog) - 4)
+ array.kind |= kindGCProg
+ array.gcdata = &prog[0]
+ array.ptrdata = array.size // overestimate but ok; must match program
}
etyp := typ.common()
@@ -1990,6 +1971,14 @@ func ArrayOf(count int, elem Type) Type {
return cachePut(ckey, &array.rtype)
}
+func appendVarint(x []byte, v uintptr) []byte {
+ for ; v >= 0x80; v >>= 7 {
+ x = append(x, byte(v|0x80))
+ }
+ x = append(x, byte(v))
+ return x
+}
+
// toType converts from a *rtype to a Type that can be returned
// to the client of package reflect. In gc, the only concern is that
// a nil *rtype must be replaced by a nil Type, but in gccgo this
@@ -2026,7 +2015,7 @@ var layoutCache struct {
// The returned type exists only for GC, so we only fill out GC relevant info.
// Currently, that's just size and the GC program. We also fill in
// the name for possible debugging use.
-func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uintptr, stack *bitVector, framePool *sync.Pool) {
+func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uintptr, stk *bitVector, framePool *sync.Pool) {
if t.Kind() != Func {
panic("reflect: funcLayout of non-func type")
}
@@ -2049,53 +2038,47 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin
tt := (*funcType)(unsafe.Pointer(t))
// compute gc program & stack bitmap for arguments
- stack = new(bitVector)
- var gc gcProg
+ ptrmap := new(bitVector)
var offset uintptr
if rcvr != nil {
// Reflect uses the "interface" calling convention for
// methods, where receivers take one word of argument
// space no matter how big they actually are.
- if ifaceIndir(rcvr) {
- // we pass a pointer to the receiver.
- gc.append(bitsPointer)
- stack.append2(bitsPointer)
- } else if rcvr.pointers() {
- // rcvr is a one-word pointer object. Its gc program
- // is just what we need here.
- gc.append(bitsPointer)
- stack.append2(bitsPointer)
- } else {
- gc.append(bitsScalar)
- stack.append2(bitsScalar)
+ if ifaceIndir(rcvr) || rcvr.pointers() {
+ ptrmap.append(1)
}
offset += ptrSize
}
for _, arg := range tt.in {
- gc.appendProg(arg)
- addTypeBits(stack, &offset, arg)
+ offset += -offset & uintptr(arg.align-1)
+ addTypeBits(ptrmap, offset, arg)
+ offset += arg.size
}
- argSize = gc.size
+ argN := ptrmap.n
+ argSize = offset
if runtime.GOARCH == "amd64p32" {
- gc.align(8)
+ offset += -offset & (8 - 1)
}
- gc.align(ptrSize)
- retOffset = gc.size
+ offset += -offset & (ptrSize - 1)
+ retOffset = offset
for _, res := range tt.out {
- gc.appendProg(res)
- // stack map does not need result bits
+ offset += -offset & uintptr(res.align-1)
+ addTypeBits(ptrmap, offset, res)
+ offset += res.size
}
- gc.align(ptrSize)
+ offset += -offset & (ptrSize - 1)
// build dummy rtype holding gc program
x := new(rtype)
- x.size = gc.size
- x.ptrdata = gc.size // over-approximation
- var hasPtr bool
- x.gc[0], hasPtr = gc.finalize()
- if !hasPtr {
+ x.size = offset
+ x.ptrdata = uintptr(ptrmap.n) * ptrSize
+ if ptrmap.n > 0 {
+ x.gcdata = &ptrmap.data[0]
+ } else {
x.kind |= kindNoPointers
}
+ ptrmap.n = argN
+
var s string
if rcvr != nil {
s = "methodargs(" + *rcvr.string + ")(" + *t.string + ")"
@@ -2115,11 +2098,11 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin
t: x,
argSize: argSize,
retOffset: retOffset,
- stack: stack,
+ stack: ptrmap,
framePool: framePool,
}
layoutCache.Unlock()
- return x, argSize, retOffset, stack, framePool
+ return x, argSize, retOffset, ptrmap, framePool
}
// ifaceIndir reports whether t is stored indirectly in an interface value.
@@ -2133,56 +2116,49 @@ type bitVector struct {
data []byte
}
-// append a bit pair to the bitmap.
-func (bv *bitVector) append2(bits uint8) {
- // assume bv.n is a multiple of 2, since append2 is the only operation.
+// append a bit to the bitmap.
+func (bv *bitVector) append(bit uint8) {
if bv.n%8 == 0 {
bv.data = append(bv.data, 0)
}
- bv.data[bv.n/8] |= bits << (bv.n % 8)
- bv.n += 2
+ bv.data[bv.n/8] |= bit << (bv.n % 8)
+ bv.n++
}
-func addTypeBits(bv *bitVector, offset *uintptr, t *rtype) {
- *offset = align(*offset, uintptr(t.align))
- if !t.pointers() {
- *offset += t.size
+func addTypeBits(bv *bitVector, offset uintptr, t *rtype) {
+ if t.kind&kindNoPointers != 0 {
return
}
switch Kind(t.kind & kindMask) {
case Chan, Func, Map, Ptr, Slice, String, UnsafePointer:
// 1 pointer at start of representation
- for bv.n < 2*uint32(*offset/uintptr(ptrSize)) {
- bv.append2(bitsScalar)
+ for bv.n < uint32(offset/uintptr(ptrSize)) {
+ bv.append(0)
}
- bv.append2(bitsPointer)
+ bv.append(1)
case Interface:
// 2 pointers
- for bv.n < 2*uint32(*offset/uintptr(ptrSize)) {
- bv.append2(bitsScalar)
+ for bv.n < uint32(offset/uintptr(ptrSize)) {
+ bv.append(0)
}
- bv.append2(bitsPointer)
- bv.append2(bitsPointer)
+ bv.append(1)
+ bv.append(1)
case Array:
// repeat inner type
tt := (*arrayType)(unsafe.Pointer(t))
for i := 0; i < int(tt.len); i++ {
- addTypeBits(bv, offset, tt.elem)
+ addTypeBits(bv, offset+uintptr(i)*tt.elem.size, tt.elem)
}
case Struct:
// apply fields
tt := (*structType)(unsafe.Pointer(t))
- start := *offset
for i := range tt.fields {
f := &tt.fields[i]
- off := start + f.offset
- addTypeBits(bv, &off, f.typ)
+ addTypeBits(bv, offset+f.offset, f.typ)
}
}
-
- *offset += t.size
}
diff --git a/src/reflect/value.go b/src/reflect/value.go
index fb9e85a8cf..91c38c9ffc 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -10,7 +10,7 @@ import (
"unsafe"
)
-const ptrSize = unsafe.Sizeof((*byte)(nil))
+const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const
const cannotSet = "cannot set value obtained from unexported struct field"
// Value is the reflection interface to a Go value.
diff --git a/src/run.bash b/src/run.bash
index 6fc864dc0e..f35ec78982 100755
--- a/src/run.bash
+++ b/src/run.bash
@@ -35,4 +35,4 @@ if ulimit -T &> /dev/null; then
[ "$(ulimit -H -T)" == "unlimited" ] || ulimit -S -T $(ulimit -H -T)
fi
-exec go tool dist test $@
+exec go tool dist test "$@"
diff --git a/src/runtime/arch1_386.go b/src/runtime/arch1_386.go
index b024d7a51f..d41696a6d6 100644
--- a/src/runtime/arch1_386.go
+++ b/src/runtime/arch1_386.go
@@ -5,12 +5,11 @@
package runtime
const (
- thechar = '8'
- _BigEndian = 0
- _CacheLineSize = 64
- _RuntimeGogoBytes = 64
- _PhysPageSize = goos_nacl*65536 + (1-goos_nacl)*4096 // 4k normally; 64k on NaCl
- _PCQuantum = 1
- _Int64Align = 4
- hugePageSize = 1 << 21
+ thechar = '8'
+ _BigEndian = 0
+ _CacheLineSize = 64
+ _PhysPageSize = goos_nacl*65536 + (1-goos_nacl)*4096 // 4k normally; 64k on NaCl
+ _PCQuantum = 1
+ _Int64Align = 4
+ hugePageSize = 1 << 21
)
diff --git a/src/runtime/arch1_amd64.go b/src/runtime/arch1_amd64.go
index 932b2b7c55..15f4cc65fe 100644
--- a/src/runtime/arch1_amd64.go
+++ b/src/runtime/arch1_amd64.go
@@ -5,12 +5,11 @@
package runtime
const (
- thechar = '6'
- _BigEndian = 0
- _CacheLineSize = 64
- _RuntimeGogoBytes = 80 + (goos_solaris)*16
- _PhysPageSize = 4096
- _PCQuantum = 1
- _Int64Align = 8
- hugePageSize = 1 << 21
+ thechar = '6'
+ _BigEndian = 0
+ _CacheLineSize = 64
+ _PhysPageSize = 4096
+ _PCQuantum = 1
+ _Int64Align = 8
+ hugePageSize = 1 << 21
)
diff --git a/src/runtime/arch1_amd64p32.go b/src/runtime/arch1_amd64p32.go
index 79421e848a..3c5456f933 100644
--- a/src/runtime/arch1_amd64p32.go
+++ b/src/runtime/arch1_amd64p32.go
@@ -5,12 +5,11 @@
package runtime
const (
- thechar = '6'
- _BigEndian = 0
- _CacheLineSize = 64
- _RuntimeGogoBytes = 64
- _PhysPageSize = 65536*goos_nacl + 4096*(1-goos_nacl)
- _PCQuantum = 1
- _Int64Align = 8
- hugePageSize = 1 << 21
+ thechar = '6'
+ _BigEndian = 0
+ _CacheLineSize = 64
+ _PhysPageSize = 65536*goos_nacl + 4096*(1-goos_nacl)
+ _PCQuantum = 1
+ _Int64Align = 8
+ hugePageSize = 1 << 21
)
diff --git a/src/runtime/arch1_arm.go b/src/runtime/arch1_arm.go
index c3fe4f0cb3..0ec2093881 100644
--- a/src/runtime/arch1_arm.go
+++ b/src/runtime/arch1_arm.go
@@ -5,12 +5,11 @@
package runtime
const (
- thechar = '5'
- _BigEndian = 0
- _CacheLineSize = 32
- _RuntimeGogoBytes = 60
- _PhysPageSize = 65536*goos_nacl + 4096*(1-goos_nacl)
- _PCQuantum = 4
- _Int64Align = 4
- hugePageSize = 0
+ thechar = '5'
+ _BigEndian = 0
+ _CacheLineSize = 32
+ _PhysPageSize = 65536*goos_nacl + 4096*(1-goos_nacl)
+ _PCQuantum = 4
+ _Int64Align = 4
+ hugePageSize = 0
)
diff --git a/src/runtime/arch1_arm64.go b/src/runtime/arch1_arm64.go
index 549a635ca4..1a3165c8b7 100644
--- a/src/runtime/arch1_arm64.go
+++ b/src/runtime/arch1_arm64.go
@@ -5,12 +5,11 @@
package runtime
const (
- thechar = '7'
- _BigEndian = 0
- _CacheLineSize = 32
- _RuntimeGogoBytes = 64
- _PhysPageSize = 4096*(1-goos_darwin) + 16384*goos_darwin
- _PCQuantum = 4
- _Int64Align = 8
- hugePageSize = 0
+ thechar = '7'
+ _BigEndian = 0
+ _CacheLineSize = 32
+ _PhysPageSize = 4096*(1-goos_darwin) + 16384*goos_darwin
+ _PCQuantum = 4
+ _Int64Align = 8
+ hugePageSize = 0
)
diff --git a/src/runtime/arch1_ppc64.go b/src/runtime/arch1_ppc64.go
index ee453c09f2..de6dd91401 100644
--- a/src/runtime/arch1_ppc64.go
+++ b/src/runtime/arch1_ppc64.go
@@ -5,12 +5,11 @@
package runtime
const (
- thechar = '9'
- _BigEndian = 1
- _CacheLineSize = 64
- _RuntimeGogoBytes = 72
- _PhysPageSize = 65536
- _PCQuantum = 4
- _Int64Align = 8
- hugePageSize = 0
+ thechar = '9'
+ _BigEndian = 1
+ _CacheLineSize = 64
+ _PhysPageSize = 65536
+ _PCQuantum = 4
+ _Int64Align = 8
+ hugePageSize = 0
)
diff --git a/src/runtime/arch1_ppc64le.go b/src/runtime/arch1_ppc64le.go
index aa028a10f3..9a55c71101 100644
--- a/src/runtime/arch1_ppc64le.go
+++ b/src/runtime/arch1_ppc64le.go
@@ -5,12 +5,11 @@
package runtime
const (
- thechar = '9'
- _BigEndian = 0
- _CacheLineSize = 64
- _RuntimeGogoBytes = 72
- _PhysPageSize = 65536
- _PCQuantum = 4
- _Int64Align = 8
- hugePageSize = 0
+ thechar = '9'
+ _BigEndian = 0
+ _CacheLineSize = 64
+ _PhysPageSize = 65536
+ _PCQuantum = 4
+ _Int64Align = 8
+ hugePageSize = 0
)
diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s
index 36353d108f..0f9aeb8f37 100644
--- a/src/runtime/asm_amd64.s
+++ b/src/runtime/asm_amd64.s
@@ -1693,8 +1693,10 @@ TEXT runtime·prefetchnta(SB),NOSPLIT,$0-8
RET
// This is called from .init_array and follows the platform, not Go, ABI.
-TEXT runtime·addmoduledata(SB),NOSPLIT,$0-8
+TEXT runtime·addmoduledata(SB),NOSPLIT,$0-0
+ PUSHQ R15 // The access to global variables below implicitly uses R15, which is callee-save
MOVQ runtime·lastmoduledatap(SB), AX
MOVQ DI, moduledata_next(AX)
MOVQ DI, runtime·lastmoduledatap(SB)
+ POPQ R15
RET
diff --git a/src/runtime/atomic_pointer.go b/src/runtime/atomic_pointer.go
index 50a30242d9..f84afe0362 100644
--- a/src/runtime/atomic_pointer.go
+++ b/src/runtime/atomic_pointer.go
@@ -20,18 +20,12 @@ import "unsafe"
func atomicstorep(ptr unsafe.Pointer, new unsafe.Pointer) {
atomicstorep1(noescape(ptr), new)
writebarrierptr_nostore((*uintptr)(ptr), uintptr(new))
- if mheap_.shadow_enabled {
- writebarrierptr_noshadow((*uintptr)(noescape(ptr)))
- }
}
//go:nosplit
func xchgp(ptr unsafe.Pointer, new unsafe.Pointer) unsafe.Pointer {
old := xchgp1(noescape(ptr), new)
writebarrierptr_nostore((*uintptr)(ptr), uintptr(new))
- if mheap_.shadow_enabled {
- writebarrierptr_noshadow((*uintptr)(noescape(ptr)))
- }
return old
}
@@ -41,9 +35,6 @@ func casp(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool {
return false
}
writebarrierptr_nostore((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
- if mheap_.shadow_enabled {
- writebarrierptr_noshadow((*uintptr)(noescape(unsafe.Pointer(ptr))))
- }
return true
}
@@ -60,9 +51,6 @@ func sync_atomic_StorePointer(ptr *unsafe.Pointer, new unsafe.Pointer) {
sync_atomic_StoreUintptr((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
atomicstorep1(noescape(unsafe.Pointer(ptr)), new)
writebarrierptr_nostore((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
- if mheap_.shadow_enabled {
- writebarrierptr_noshadow((*uintptr)(noescape(unsafe.Pointer(ptr))))
- }
}
//go:linkname sync_atomic_SwapUintptr sync/atomic.SwapUintptr
@@ -73,9 +61,6 @@ func sync_atomic_SwapUintptr(ptr *uintptr, new uintptr) uintptr
func sync_atomic_SwapPointer(ptr unsafe.Pointer, new unsafe.Pointer) unsafe.Pointer {
old := unsafe.Pointer(sync_atomic_SwapUintptr((*uintptr)(noescape(ptr)), uintptr(new)))
writebarrierptr_nostore((*uintptr)(ptr), uintptr(new))
- if mheap_.shadow_enabled {
- writebarrierptr_noshadow((*uintptr)(noescape(ptr)))
- }
return old
}
@@ -89,8 +74,5 @@ func sync_atomic_CompareAndSwapPointer(ptr *unsafe.Pointer, old, new unsafe.Poin
return false
}
writebarrierptr_nostore((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
- if mheap_.shadow_enabled {
- writebarrierptr_noshadow((*uintptr)(noescape(unsafe.Pointer(ptr))))
- }
return true
}
diff --git a/src/runtime/debug.go b/src/runtime/debug.go
index 3ecaac10bc..9aec3b03e0 100644
--- a/src/runtime/debug.go
+++ b/src/runtime/debug.go
@@ -22,17 +22,12 @@ func GOMAXPROCS(n int) int {
return ret
}
- semacquire(&worldsema, false)
- gp := getg()
- gp.m.preemptoff = "GOMAXPROCS"
- systemstack(stoptheworld)
+ stopTheWorld("GOMAXPROCS")
- // newprocs will be processed by starttheworld
+ // newprocs will be processed by startTheWorld
newprocs = int32(n)
- gp.m.preemptoff = ""
- semrelease(&worldsema)
- systemstack(starttheworld)
+ startTheWorld()
return ret
}
diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go
index e0c8b17bd3..3fddcc868f 100644
--- a/src/runtime/export_test.go
+++ b/src/runtime/export_test.go
@@ -76,24 +76,17 @@ func ParForIters(desc *ParFor, tid uint32) (uint32, uint32) {
}
func GCMask(x interface{}) (ret []byte) {
- e := (*eface)(unsafe.Pointer(&x))
- s := (*slice)(unsafe.Pointer(&ret))
systemstack(func() {
- var len uintptr
- var a *byte
- getgcmask(e.data, e._type, &a, &len)
- s.array = unsafe.Pointer(a)
- s.len = int(len)
- s.cap = s.len
+ ret = getgcmask(x)
})
return
}
func RunSchedLocalQueueTest() {
- systemstack(testSchedLocalQueue)
+ testSchedLocalQueue()
}
func RunSchedLocalQueueStealTest() {
- systemstack(testSchedLocalQueueSteal)
+ testSchedLocalQueueSteal()
}
var StringHash = stringHash
@@ -106,11 +99,6 @@ var MemclrBytes = memclrBytes
var HashLoad = &hashLoad
-// For testing.
-func GogoBytes() int32 {
- return _RuntimeGogoBytes
-}
-
// entry point for testing
func GostringW(w []uint16) (s string) {
systemstack(func() {
@@ -133,3 +121,34 @@ func Envs() []string { return envs }
func SetEnvs(e []string) { envs = e }
var BigEndian = _BigEndian
+
+// For benchmarking.
+
+func BenchSetType(n int, x interface{}) {
+ e := *(*eface)(unsafe.Pointer(&x))
+ t := e._type
+ var size uintptr
+ var p unsafe.Pointer
+ switch t.kind & kindMask {
+ case _KindPtr:
+ t = (*ptrtype)(unsafe.Pointer(t)).elem
+ size = t.size
+ p = e.data
+ case _KindSlice:
+ slice := *(*struct {
+ ptr unsafe.Pointer
+ len, cap uintptr
+ })(e.data)
+ t = (*slicetype)(unsafe.Pointer(t)).elem
+ size = t.size * slice.len
+ p = slice.ptr
+ }
+ allocSize := roundupsize(size)
+ systemstack(func() {
+ for i := 0; i < n; i++ {
+ heapBitsSetType(uintptr(p), allocSize, size, t)
+ }
+ })
+}
+
+const PtrSize = ptrSize
diff --git a/src/runtime/extern.go b/src/runtime/extern.go
index 540d7b5124..476c3c5ae3 100644
--- a/src/runtime/extern.go
+++ b/src/runtime/extern.go
@@ -58,18 +58,6 @@ a comma-separated list of name=val pairs. Supported names are:
scavenge: scavenge=1 enables debugging mode of heap scavenger.
- wbshadow: setting wbshadow=1 enables a shadow copy of the heap
- used to detect missing write barriers at the next write to a
- given location. If a bug can be detected in this mode it is
- typically easy to understand, since the crash says quite
- clearly what kind of word has missed a write barrier.
- Setting wbshadow=2 checks the shadow copy during garbage
- collection as well. Bugs detected at garbage collection can be
- difficult to understand, because there is no context for what
- the found word means. Typically you have to reproduce the
- problem with allocfreetrace=1 in order to understand the type
- of the badly updated word.
-
gccheckmark: setting gccheckmark=1 enables verification of the
garbage collector's concurrent mark phase by performing a
second mark pass while the world is stopped. If the second
diff --git a/src/runtime/gc_test.go b/src/runtime/gc_test.go
index 6abec4cca7..e3e0c3a583 100644
--- a/src/runtime/gc_test.go
+++ b/src/runtime/gc_test.go
@@ -6,6 +6,7 @@ package runtime_test
import (
"os"
+ "reflect"
"runtime"
"runtime/debug"
"testing"
@@ -197,45 +198,166 @@ func TestHugeGCInfo(t *testing.T) {
}
}
-func BenchmarkSetTypeNoPtr1(b *testing.B) {
- type NoPtr1 struct {
- p uintptr
- }
- var p *NoPtr1
- for i := 0; i < b.N; i++ {
- p = &NoPtr1{}
- }
- _ = p
+func BenchmarkSetTypePtr(b *testing.B) {
+ benchSetType(b, new(*byte))
}
-func BenchmarkSetTypeNoPtr2(b *testing.B) {
- type NoPtr2 struct {
- p, q uintptr
- }
- var p *NoPtr2
- for i := 0; i < b.N; i++ {
- p = &NoPtr2{}
- }
- _ = p
+
+func BenchmarkSetTypePtr8(b *testing.B) {
+ benchSetType(b, new([8]*byte))
}
-func BenchmarkSetTypePtr1(b *testing.B) {
- type Ptr1 struct {
- p *byte
- }
- var p *Ptr1
- for i := 0; i < b.N; i++ {
- p = &Ptr1{}
- }
- _ = p
+
+func BenchmarkSetTypePtr16(b *testing.B) {
+ benchSetType(b, new([16]*byte))
}
-func BenchmarkSetTypePtr2(b *testing.B) {
- type Ptr2 struct {
- p, q *byte
+
+func BenchmarkSetTypePtr32(b *testing.B) {
+ benchSetType(b, new([32]*byte))
+}
+
+func BenchmarkSetTypePtr64(b *testing.B) {
+ benchSetType(b, new([64]*byte))
+}
+
+func BenchmarkSetTypePtr126(b *testing.B) {
+ benchSetType(b, new([126]*byte))
+}
+
+func BenchmarkSetTypePtr128(b *testing.B) {
+ benchSetType(b, new([128]*byte))
+}
+
+func BenchmarkSetTypePtrSlice(b *testing.B) {
+ benchSetType(b, make([]*byte, 1<<10))
+}
+
+type Node1 struct {
+ Value [1]uintptr
+ Left, Right *byte
+}
+
+func BenchmarkSetTypeNode1(b *testing.B) {
+ benchSetType(b, new(Node1))
+}
+
+func BenchmarkSetTypeNode1Slice(b *testing.B) {
+ benchSetType(b, make([]Node1, 32))
+}
+
+type Node8 struct {
+ Value [8]uintptr
+ Left, Right *byte
+}
+
+func BenchmarkSetTypeNode8(b *testing.B) {
+ benchSetType(b, new(Node8))
+}
+
+func BenchmarkSetTypeNode8Slice(b *testing.B) {
+ benchSetType(b, make([]Node8, 32))
+}
+
+type Node64 struct {
+ Value [64]uintptr
+ Left, Right *byte
+}
+
+func BenchmarkSetTypeNode64(b *testing.B) {
+ benchSetType(b, new(Node64))
+}
+
+func BenchmarkSetTypeNode64Slice(b *testing.B) {
+ benchSetType(b, make([]Node64, 32))
+}
+
+type Node64Dead struct {
+ Left, Right *byte
+ Value [64]uintptr
+}
+
+func BenchmarkSetTypeNode64Dead(b *testing.B) {
+ benchSetType(b, new(Node64Dead))
+}
+
+func BenchmarkSetTypeNode64DeadSlice(b *testing.B) {
+ benchSetType(b, make([]Node64Dead, 32))
+}
+
+type Node124 struct {
+ Value [124]uintptr
+ Left, Right *byte
+}
+
+func BenchmarkSetTypeNode124(b *testing.B) {
+ benchSetType(b, new(Node124))
+}
+
+func BenchmarkSetTypeNode124Slice(b *testing.B) {
+ benchSetType(b, make([]Node124, 32))
+}
+
+type Node126 struct {
+ Value [126]uintptr
+ Left, Right *byte
+}
+
+func BenchmarkSetTypeNode126(b *testing.B) {
+ benchSetType(b, new(Node126))
+}
+
+func BenchmarkSetTypeNode126Slice(b *testing.B) {
+ benchSetType(b, make([]Node126, 32))
+}
+
+type Node128 struct {
+ Value [128]uintptr
+ Left, Right *byte
+}
+
+func BenchmarkSetTypeNode128(b *testing.B) {
+ benchSetType(b, new(Node128))
+}
+
+func BenchmarkSetTypeNode128Slice(b *testing.B) {
+ benchSetType(b, make([]Node128, 32))
+}
+
+type Node130 struct {
+ Value [130]uintptr
+ Left, Right *byte
+}
+
+func BenchmarkSetTypeNode130(b *testing.B) {
+ benchSetType(b, new(Node130))
+}
+
+func BenchmarkSetTypeNode130Slice(b *testing.B) {
+ benchSetType(b, make([]Node130, 32))
+}
+
+type Node1024 struct {
+ Value [1024]uintptr
+ Left, Right *byte
+}
+
+func BenchmarkSetTypeNode1024(b *testing.B) {
+ benchSetType(b, new(Node1024))
+}
+
+func BenchmarkSetTypeNode1024Slice(b *testing.B) {
+ benchSetType(b, make([]Node1024, 32))
+}
+
+func benchSetType(b *testing.B, x interface{}) {
+ v := reflect.ValueOf(x)
+ t := v.Type()
+ switch t.Kind() {
+ case reflect.Ptr:
+ b.SetBytes(int64(t.Elem().Size()))
+ case reflect.Slice:
+ b.SetBytes(int64(t.Elem().Size()) * int64(v.Len()))
}
- var p *Ptr2
- for i := 0; i < b.N; i++ {
- p = &Ptr2{}
- }
- _ = p
+ b.ResetTimer()
+ runtime.BenchSetType(b.N, x)
}
func BenchmarkAllocation(b *testing.B) {
diff --git a/src/runtime/gcinfo_test.go b/src/runtime/gcinfo_test.go
index 66b0353f08..f330bf2430 100644
--- a/src/runtime/gcinfo_test.go
+++ b/src/runtime/gcinfo_test.go
@@ -10,8 +10,14 @@ import (
"testing"
)
+const (
+ typeScalar = 0
+ typePointer = 1
+)
+
// TestGCInfo tests that various objects in heap, data and bss receive correct GC pointer type info.
func TestGCInfo(t *testing.T) {
+ verifyGCInfo(t, "bss Ptr", &bssPtr, infoPtr)
verifyGCInfo(t, "bss ScalarPtr", &bssScalarPtr, infoScalarPtr)
verifyGCInfo(t, "bss PtrScalar", &bssPtrScalar, infoPtrScalar)
verifyGCInfo(t, "bss BigStruct", &bssBigStruct, infoBigStruct())
@@ -20,6 +26,7 @@ func TestGCInfo(t *testing.T) {
verifyGCInfo(t, "bss eface", &bssEface, infoEface)
verifyGCInfo(t, "bss iface", &bssIface, infoIface)
+ verifyGCInfo(t, "data Ptr", &dataPtr, infoPtr)
verifyGCInfo(t, "data ScalarPtr", &dataScalarPtr, infoScalarPtr)
verifyGCInfo(t, "data PtrScalar", &dataPtrScalar, infoPtrScalar)
verifyGCInfo(t, "data BigStruct", &dataBigStruct, infoBigStruct())
@@ -28,6 +35,7 @@ func TestGCInfo(t *testing.T) {
verifyGCInfo(t, "data eface", &dataEface, infoEface)
verifyGCInfo(t, "data iface", &dataIface, infoIface)
+ verifyGCInfo(t, "stack Ptr", new(Ptr), infoPtr)
verifyGCInfo(t, "stack ScalarPtr", new(ScalarPtr), infoScalarPtr)
verifyGCInfo(t, "stack PtrScalar", new(PtrScalar), infoPtrScalar)
verifyGCInfo(t, "stack BigStruct", new(BigStruct), infoBigStruct())
@@ -37,38 +45,43 @@ func TestGCInfo(t *testing.T) {
verifyGCInfo(t, "stack iface", new(Iface), infoIface)
for i := 0; i < 10; i++ {
- verifyGCInfo(t, "heap ScalarPtr", escape(new(ScalarPtr)), infoScalarPtr)
- verifyGCInfo(t, "heap PtrScalar", escape(new(PtrScalar)), infoPtrScalar)
- verifyGCInfo(t, "heap BigStruct", escape(new(BigStruct)), infoBigStruct())
- verifyGCInfo(t, "heap string", escape(new(string)), infoString)
- verifyGCInfo(t, "heap eface", escape(new(interface{})), infoEface)
- verifyGCInfo(t, "heap iface", escape(new(Iface)), infoIface)
+ verifyGCInfo(t, "heap Ptr", escape(new(Ptr)), trimDead(padDead(infoPtr)))
+ verifyGCInfo(t, "heap PtrSlice", escape(&make([]*byte, 10)[0]), trimDead(infoPtr10))
+ verifyGCInfo(t, "heap ScalarPtr", escape(new(ScalarPtr)), trimDead(infoScalarPtr))
+ verifyGCInfo(t, "heap ScalarPtrSlice", escape(&make([]ScalarPtr, 4)[0]), trimDead(infoScalarPtr4))
+ verifyGCInfo(t, "heap PtrScalar", escape(new(PtrScalar)), trimDead(infoPtrScalar))
+ verifyGCInfo(t, "heap BigStruct", escape(new(BigStruct)), trimDead(infoBigStruct()))
+ verifyGCInfo(t, "heap string", escape(new(string)), trimDead(infoString))
+ verifyGCInfo(t, "heap eface", escape(new(interface{})), trimDead(infoEface))
+ verifyGCInfo(t, "heap iface", escape(new(Iface)), trimDead(infoIface))
}
-
}
func verifyGCInfo(t *testing.T, name string, p interface{}, mask0 []byte) {
mask := runtime.GCMask(p)
- if len(mask) > len(mask0) {
- mask0 = append(mask0, typeDead)
- mask = mask[:len(mask0)]
- }
if bytes.Compare(mask, mask0) != 0 {
t.Errorf("bad GC program for %v:\nwant %+v\ngot %+v", name, mask0, mask)
return
}
}
-func nonStackInfo(mask []byte) []byte {
- // typeDead is replaced with typeScalar everywhere except stacks.
- mask1 := make([]byte, len(mask))
- for i, v := range mask {
- if v == typeDead {
- v = typeScalar
- }
- mask1[i] = v
+func padDead(mask []byte) []byte {
+ // Because the dead bit isn't encoded until the third word,
+ // and because on 32-bit systems a one-word allocation
+ // uses a two-word block, the pointer info for a one-word
+ // object needs to be expanded to include an extra scalar
+ // on 32-bit systems to match the heap bitmap.
+ if runtime.PtrSize == 4 && len(mask) == 1 {
+ return []byte{mask[0], 0}
}
- return mask1
+ return mask
+}
+
+func trimDead(mask []byte) []byte {
+ for len(mask) > 2 && mask[len(mask)-1] == typeScalar {
+ mask = mask[:len(mask)-1]
+ }
+ return mask
}
var gcinfoSink interface{}
@@ -78,18 +91,13 @@ func escape(p interface{}) interface{} {
return p
}
-const (
- typeDead = iota
- typeScalar
- typePointer
-)
+var infoPtr = []byte{typePointer}
-const (
- BitsString = iota // unused
- BitsSlice // unused
- BitsIface
- BitsEface
-)
+type Ptr struct {
+ *byte
+}
+
+var infoPtr10 = []byte{typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer}
type ScalarPtr struct {
q int
@@ -102,6 +110,8 @@ type ScalarPtr struct {
var infoScalarPtr = []byte{typeScalar, typePointer, typeScalar, typePointer, typeScalar, typePointer}
+var infoScalarPtr4 = append(append(append(append([]byte(nil), infoScalarPtr...), infoScalarPtr...), infoScalarPtr...), infoScalarPtr...)
+
type PtrScalar struct {
q *int
w int
@@ -166,6 +176,7 @@ func (IfaceImpl) f() {
var (
// BSS
+ bssPtr Ptr
bssScalarPtr ScalarPtr
bssPtrScalar PtrScalar
bssBigStruct BigStruct
@@ -175,6 +186,7 @@ var (
bssIface Iface
// DATA
+ dataPtr = Ptr{new(byte)}
dataScalarPtr = ScalarPtr{q: 1}
dataPtrScalar = PtrScalar{w: 1}
dataBigStruct = BigStruct{w: 1}
diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go
index 9ca33992bb..b199330a1e 100644
--- a/src/runtime/hashmap.go
+++ b/src/runtime/hashmap.go
@@ -233,6 +233,9 @@ func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap {
throw("need padding in bucket (value)")
}
+ // make sure zero of element type is available.
+ mapzero(t.elem)
+
// find size parameter which will hold the requested # of elements
B := uint8(0)
for ; hint > bucketCnt && float32(hint) > loadFactor*float32(uintptr(1)<2GB zero on 32-bit machine
+ throw("map element too large")
+ }
+ }
+ zerobuf.p = (*byte)(persistentalloc(zerobuf.size, 64, &memstats.other_sys))
+ }
+ atomicstorep(unsafe.Pointer(&t.zero), unsafe.Pointer(zerobuf.p))
+ unlock(&zerobuf.lock)
+}
diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go
index e18aa79164..c0fff3f1ce 100644
--- a/src/runtime/heapdump.go
+++ b/src/runtime/heapdump.go
@@ -15,20 +15,13 @@ import "unsafe"
//go:linkname runtime_debug_WriteHeapDump runtime/debug.WriteHeapDump
func runtime_debug_WriteHeapDump(fd uintptr) {
- semacquire(&worldsema, false)
- gp := getg()
- gp.m.preemptoff = "write heap dump"
- systemstack(stoptheworld)
+ stopTheWorld("write heap dump")
systemstack(func() {
writeheapdump_m(fd)
})
- gp.m.preemptoff = ""
- gp.m.locks++
- semrelease(&worldsema)
- systemstack(starttheworld)
- gp.m.locks--
+ startTheWorld()
}
const (
@@ -730,14 +723,13 @@ func makeheapobjbv(p uintptr, size uintptr) bitvector {
i := uintptr(0)
hbits := heapBitsForAddr(p)
for ; i < nptr; i++ {
- bits := hbits.typeBits()
- if bits == typeDead {
+ if i >= 2 && !hbits.isMarked() {
break // end of object
}
- hbits = hbits.next()
- if bits == typePointer {
+ if hbits.isPointer() {
tmpbuf[i/8] |= 1 << (i % 8)
}
+ hbits = hbits.next()
}
return bitvector{int32(i), &tmpbuf[0]}
}
diff --git a/src/runtime/lfstack_test.go b/src/runtime/lfstack_test.go
index 68f221d6ef..4da4d88619 100644
--- a/src/runtime/lfstack_test.go
+++ b/src/runtime/lfstack_test.go
@@ -24,8 +24,12 @@ func toMyNode(node *LFNode) *MyNode {
return (*MyNode)(unsafe.Pointer(node))
}
+var global interface{}
+
func TestLFStack(t *testing.T) {
stack := new(uint64)
+ global = stack // force heap allocation
+
// Need to keep additional referenfces to nodes, the stack is not all that type-safe.
var nodes []*MyNode
diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go
index 1619ccb9f4..2d7e55643f 100644
--- a/src/runtime/malloc.go
+++ b/src/runtime/malloc.go
@@ -424,9 +424,6 @@ func mHeap_SysAlloc(h *mheap, n uintptr) unsafe.Pointer {
if raceenabled {
racemapshadow((unsafe.Pointer)(p), n)
}
- if mheap_.shadow_enabled {
- sysMap(unsafe.Pointer(p+mheap_.shadow_heap), n, h.shadow_reserved, &memstats.other_sys)
- }
if uintptr(p)&(_PageSize-1) != 0 {
throw("misrounded allocation in MHeap_SysAlloc")
@@ -512,6 +509,9 @@ func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer {
if mp.mallocing != 0 {
throw("malloc deadlock")
}
+ if mp.gsignal == getg() {
+ throw("malloc during signal")
+ }
mp.mallocing = 1
shouldhelpgc := false
@@ -669,10 +669,6 @@ func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer {
})
}
- if mheap_.shadow_enabled {
- clearshadow(uintptr(x), size)
- }
-
if raceenabled {
racemalloc(x, size)
}
diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go
index eb5881707b..53a0a00ae7 100644
--- a/src/runtime/mbarrier.go
+++ b/src/runtime/mbarrier.go
@@ -10,12 +10,6 @@
// implementation, markwb, and the various wrappers called by the
// compiler to implement pointer assignment, slice assignment,
// typed memmove, and so on.
-//
-// To check for missed write barriers, the GODEBUG=wbshadow debugging
-// mode allocates a second copy of the heap. Write barrier-based pointer
-// updates make changes to both the real heap and the shadow, and both
-// the pointer updates and the GC look for inconsistencies between the two,
-// indicating pointer writes that bypassed the barrier.
package runtime
@@ -66,7 +60,7 @@ func gcmarkwb_m(slot *uintptr, ptr uintptr) {
default:
throw("gcphasework in bad gcphase")
- case _GCoff, _GCquiesce, _GCstw, _GCsweep, _GCscan:
+ case _GCoff, _GCstw, _GCsweep, _GCscan:
// ok
case _GCmark, _GCmarktermination:
@@ -107,43 +101,19 @@ func writebarrierptr_nostore1(dst *uintptr, src uintptr) {
// but if we do that, Go inserts a write barrier on *dst = src.
//go:nosplit
func writebarrierptr(dst *uintptr, src uintptr) {
+ *dst = src
if !writeBarrierEnabled {
- *dst = src
return
}
-
if src != 0 && (src < _PhysPageSize || src == poisonStack) {
- systemstack(func() { throw("bad pointer in write barrier") })
+ systemstack(func() {
+ print("runtime: writebarrierptr *", dst, " = ", hex(src), "\n")
+ throw("bad pointer in write barrier")
+ })
}
-
- if mheap_.shadow_enabled {
- writebarrierptr_shadow(dst, src)
- }
-
- *dst = src
writebarrierptr_nostore1(dst, src)
}
-//go:nosplit
-func writebarrierptr_shadow(dst *uintptr, src uintptr) {
- systemstack(func() {
- addr := uintptr(unsafe.Pointer(dst))
- shadow := shadowptr(addr)
- if shadow == nil {
- return
- }
- // There is a race here but only if the program is using
- // racy writes instead of sync/atomic. In that case we
- // don't mind crashing.
- if *shadow != *dst && *shadow != noShadow && istrackedptr(*dst) {
- mheap_.shadow_enabled = false
- print("runtime: write barrier dst=", dst, " old=", hex(*dst), " shadow=", shadow, " old=", hex(*shadow), " new=", hex(src), "\n")
- throw("missed write barrier")
- }
- *shadow = src
- })
-}
-
// Like writebarrierptr, but the store has already been applied.
// Do not reapply.
//go:nosplit
@@ -151,44 +121,12 @@ func writebarrierptr_nostore(dst *uintptr, src uintptr) {
if !writeBarrierEnabled {
return
}
-
if src != 0 && (src < _PhysPageSize || src == poisonStack) {
systemstack(func() { throw("bad pointer in write barrier") })
}
-
- // Apply changes to shadow.
- // Since *dst has been overwritten already, we cannot check
- // whether there were any missed updates, but writebarrierptr_nostore
- // is only rarely used.
- if mheap_.shadow_enabled {
- systemstack(func() {
- addr := uintptr(unsafe.Pointer(dst))
- shadow := shadowptr(addr)
- if shadow == nil {
- return
- }
- *shadow = src
- })
- }
-
writebarrierptr_nostore1(dst, src)
}
-// writebarrierptr_noshadow records that the value in *dst
-// has been written to using an atomic operation and the shadow
-// has not been updated. (In general if dst must be manipulated
-// atomically we cannot get the right bits for use in the shadow.)
-//go:nosplit
-func writebarrierptr_noshadow(dst *uintptr) {
- addr := uintptr(unsafe.Pointer(dst))
- shadow := shadowptr(addr)
- if shadow == nil {
- return
- }
-
- *shadow = noShadow
-}
-
//go:nosplit
func writebarrierstring(dst *[2]uintptr, src [2]uintptr) {
writebarrierptr(&dst[0], src[0])
@@ -217,37 +155,11 @@ func writebarrieriface(dst *[2]uintptr, src [2]uintptr) {
// typedmemmove copies a value of type t to dst from src.
//go:nosplit
func typedmemmove(typ *_type, dst, src unsafe.Pointer) {
- if !writeBarrierEnabled || (typ.kind&kindNoPointers) != 0 {
- memmove(dst, src, typ.size)
+ memmove(dst, src, typ.size)
+ if typ.kind&kindNoPointers != 0 {
return
}
-
- systemstack(func() {
- mask := typeBitmapInHeapBitmapFormat(typ)
- nptr := typ.size / ptrSize
- for i := uintptr(0); i < nptr; i += 2 {
- bits := mask[i/2]
- if (bits>>2)&typeMask == typePointer {
- writebarrierptr((*uintptr)(dst), *(*uintptr)(src))
- } else {
- *(*uintptr)(dst) = *(*uintptr)(src)
- }
- // TODO(rsc): The noescape calls should be unnecessary.
- dst = add(noescape(dst), ptrSize)
- src = add(noescape(src), ptrSize)
- if i+1 == nptr {
- break
- }
- bits >>= 4
- if (bits>>2)&typeMask == typePointer {
- writebarrierptr((*uintptr)(dst), *(*uintptr)(src))
- } else {
- *(*uintptr)(dst) = *(*uintptr)(src)
- }
- dst = add(noescape(dst), ptrSize)
- src = add(noescape(src), ptrSize)
- }
- })
+ heapBitsBulkBarrier(uintptr(dst), typ.size)
}
//go:linkname reflect_typedmemmove reflect.typedmemmove
@@ -259,38 +171,16 @@ func reflect_typedmemmove(typ *_type, dst, src unsafe.Pointer) {
// dst and src point off bytes into the value and only copies size bytes.
//go:linkname reflect_typedmemmovepartial reflect.typedmemmovepartial
func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size uintptr) {
- if !writeBarrierEnabled || (typ.kind&kindNoPointers) != 0 || size < ptrSize {
- memmove(dst, src, size)
+ memmove(dst, src, size)
+ if !writeBarrierEnabled || typ.kind&kindNoPointers != 0 || size < ptrSize || !inheap(uintptr(dst)) {
return
}
- if off&(ptrSize-1) != 0 {
- frag := -off & (ptrSize - 1)
- // frag < size, because size >= ptrSize, checked above.
- memmove(dst, src, frag)
+ if frag := -off & (ptrSize - 1); frag != 0 {
+ dst = add(dst, frag)
size -= frag
- dst = add(noescape(dst), frag)
- src = add(noescape(src), frag)
- off += frag
- }
-
- mask := typeBitmapInHeapBitmapFormat(typ)
- nptr := (off + size) / ptrSize
- for i := uintptr(off / ptrSize); i < nptr; i++ {
- bits := mask[i/2] >> ((i & 1) << 2)
- if (bits>>2)&typeMask == typePointer {
- writebarrierptr((*uintptr)(dst), *(*uintptr)(src))
- } else {
- *(*uintptr)(dst) = *(*uintptr)(src)
- }
- // TODO(rsc): The noescape calls should be unnecessary.
- dst = add(noescape(dst), ptrSize)
- src = add(noescape(src), ptrSize)
- }
- size &= ptrSize - 1
- if size > 0 {
- memmove(dst, src, size)
}
+ heapBitsBulkBarrier(uintptr(dst), size&^(ptrSize-1))
}
// callwritebarrier is invoked at the end of reflectcall, to execute
@@ -302,29 +192,16 @@ func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size
// not to be preempted before the write barriers have been run.
//go:nosplit
func callwritebarrier(typ *_type, frame unsafe.Pointer, framesize, retoffset uintptr) {
- if !writeBarrierEnabled || typ == nil || (typ.kind&kindNoPointers) != 0 || framesize-retoffset < ptrSize {
+ if !writeBarrierEnabled || typ == nil || typ.kind&kindNoPointers != 0 || framesize-retoffset < ptrSize || !inheap(uintptr(frame)) {
return
}
-
- systemstack(func() {
- mask := typeBitmapInHeapBitmapFormat(typ)
- // retoffset is known to be pointer-aligned (at least).
- // TODO(rsc): The noescape call should be unnecessary.
- dst := add(noescape(frame), retoffset)
- nptr := framesize / ptrSize
- for i := uintptr(retoffset / ptrSize); i < nptr; i++ {
- bits := mask[i/2] >> ((i & 1) << 2)
- if (bits>>2)&typeMask == typePointer {
- writebarrierptr_nostore((*uintptr)(dst), *(*uintptr)(dst))
- }
- // TODO(rsc): The noescape call should be unnecessary.
- dst = add(noescape(dst), ptrSize)
- }
- })
+ heapBitsBulkBarrier(uintptr(add(frame, retoffset)), framesize-retoffset)
}
//go:nosplit
func typedslicecopy(typ *_type, dst, src slice) int {
+ // TODO(rsc): If typedslicecopy becomes faster than calling
+ // typedmemmove repeatedly, consider using during func growslice.
n := dst.len
if n > src.len {
n = src.len
@@ -342,6 +219,10 @@ func typedslicecopy(typ *_type, dst, src slice) int {
racereadrangepc(srcp, uintptr(n)*typ.size, callerpc, pc)
}
+ // Note: No point in checking typ.kind&kindNoPointers here:
+ // compiler only emits calls to typedslicecopy for types with pointers,
+ // and growslice and reflect_typedslicecopy check for pointers
+ // before calling typedslicecopy.
if !writeBarrierEnabled {
memmove(dstp, srcp, uintptr(n)*typ.size)
return n
@@ -382,134 +263,13 @@ func typedslicecopy(typ *_type, dst, src slice) int {
//go:linkname reflect_typedslicecopy reflect.typedslicecopy
func reflect_typedslicecopy(elemType *_type, dst, src slice) int {
+ if elemType.kind&kindNoPointers != 0 {
+ n := dst.len
+ if n > src.len {
+ n = src.len
+ }
+ memmove(dst.array, src.array, uintptr(n)*elemType.size)
+ return n
+ }
return typedslicecopy(elemType, dst, src)
}
-
-// Shadow heap for detecting missed write barriers.
-
-// noShadow is stored in as the shadow pointer to mark that there is no
-// shadow word recorded. It matches any actual pointer word.
-// noShadow is used when it is impossible to know the right word
-// to store in the shadow heap, such as when the real heap word
-// is being manipulated atomically.
-const noShadow uintptr = 1
-
-func wbshadowinit() {
- // Initialize write barrier shadow heap if we were asked for it
- // and we have enough address space (not on 32-bit).
- if debug.wbshadow == 0 {
- return
- }
- if ptrSize != 8 {
- print("runtime: GODEBUG=wbshadow=1 disabled on 32-bit system\n")
- return
- }
-
- var reserved bool
- p1 := sysReserveHigh(mheap_.arena_end-mheap_.arena_start, &reserved)
- if p1 == nil {
- throw("cannot map shadow heap")
- }
- mheap_.shadow_heap = uintptr(p1) - mheap_.arena_start
- sysMap(p1, mheap_.arena_used-mheap_.arena_start, reserved, &memstats.other_sys)
- memmove(p1, unsafe.Pointer(mheap_.arena_start), mheap_.arena_used-mheap_.arena_start)
-
- mheap_.shadow_reserved = reserved
-
- for datap := &firstmoduledata; datap != nil; datap = datap.next {
- start := ^uintptr(0)
- end := uintptr(0)
- if start > datap.noptrdata {
- start = datap.noptrdata
- }
- if start > datap.data {
- start = datap.data
- }
- if start > datap.noptrbss {
- start = datap.noptrbss
- }
- if start > datap.bss {
- start = datap.bss
- }
- if end < datap.enoptrdata {
- end = datap.enoptrdata
- }
- if end < datap.edata {
- end = datap.edata
- }
- if end < datap.enoptrbss {
- end = datap.enoptrbss
- }
- if end < datap.ebss {
- end = datap.ebss
- }
- start &^= _PhysPageSize - 1
- end = round(end, _PhysPageSize)
- datap.data_start = start
- datap.data_end = end
- reserved = false
- p1 = sysReserveHigh(end-start, &reserved)
- if p1 == nil {
- throw("cannot map shadow data")
- }
- datap.shadow_data = uintptr(p1) - start
- sysMap(p1, end-start, reserved, &memstats.other_sys)
- memmove(p1, unsafe.Pointer(start), end-start)
- }
-
- mheap_.shadow_enabled = true
- writeBarrierEnabled = true
-}
-
-// shadowptr returns a pointer to the shadow value for addr.
-//go:nosplit
-func shadowptr(addr uintptr) *uintptr {
- for datap := &firstmoduledata; datap != nil; datap = datap.next {
- if datap.data_start <= addr && addr < datap.data_end {
- return (*uintptr)(unsafe.Pointer(addr + datap.shadow_data))
- }
- }
- if inheap(addr) {
- return (*uintptr)(unsafe.Pointer(addr + mheap_.shadow_heap))
- }
- return nil
-}
-
-// istrackedptr reports whether the pointer value p requires a write barrier
-// when stored into the heap.
-func istrackedptr(p uintptr) bool {
- return inheap(p)
-}
-
-// checkwbshadow checks that p matches its shadow word.
-// The garbage collector calls checkwbshadow for each pointer during the checkmark phase.
-// It is only called when mheap_.shadow_enabled is true.
-func checkwbshadow(p *uintptr) {
- addr := uintptr(unsafe.Pointer(p))
- shadow := shadowptr(addr)
- if shadow == nil {
- return
- }
- // There is no race on the accesses here, because the world is stopped,
- // but there may be racy writes that lead to the shadow and the
- // heap being inconsistent. If so, we will detect that here as a
- // missed write barrier and crash. We don't mind.
- // Code should use sync/atomic instead of racy pointer writes.
- if *shadow != *p && *shadow != noShadow && istrackedptr(*p) {
- mheap_.shadow_enabled = false
- print("runtime: checkwritebarrier p=", p, " *p=", hex(*p), " shadow=", shadow, " *shadow=", hex(*shadow), "\n")
- throw("missed write barrier")
- }
-}
-
-// clearshadow clears the shadow copy associated with the n bytes of memory at addr.
-func clearshadow(addr, n uintptr) {
- if !mheap_.shadow_enabled {
- return
- }
- p := shadowptr(addr)
- if p == nil || n <= ptrSize {
- return
- }
- memclr(unsafe.Pointer(p), n)
-}
diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go
index f0c7520e38..b20908fb49 100644
--- a/src/runtime/mbitmap.go
+++ b/src/runtime/mbitmap.go
@@ -6,48 +6,40 @@
//
// Stack, data, and bss bitmaps
//
-// Not handled in this file, but worth mentioning: stack frames and global data
-// in the data and bss sections are described by 1-bit bitmaps in which 0 means
-// scalar or uninitialized or dead and 1 means pointer to visit during GC.
-//
-// Comparing this 1-bit form with the 2-bit form described below, 0 represents
-// both the 2-bit 00 and 01, while 1 represents the 2-bit 10.
-// Therefore conversions between the two (until the 2-bit form is gone)
-// can be done by x>>1 for 2-bit to 1-bit and x+1 for 1-bit to 2-bit.
-//
-// Type bitmaps
-//
-// Types that aren't too large
-// record information about the layout of their memory words using a type bitmap.
-// The bitmap holds two bits for each pointer-sized word. The two-bit values are:
-//
-// 00 - typeDead: not a pointer, and no pointers in the rest of the object
-// 01 - typeScalar: not a pointer
-// 10 - typePointer: a pointer that GC should trace
-// 11 - unused
-//
-// typeDead only appears in type bitmaps in Go type descriptors
-// and in type bitmaps embedded in the heap bitmap (see below).
+// Stack frames and global variables in the data and bss sections are described
+// by 1-bit bitmaps in which 0 means uninteresting and 1 means live pointer
+// to be visited during GC. The bits in each byte are consumed starting with
+// the low bit: 1<<0, 1<<1, and so on.
//
// Heap bitmap
//
// The allocated heap comes from a subset of the memory in the range [start, used),
// where start == mheap_.arena_start and used == mheap_.arena_used.
-// The heap bitmap comprises 4 bits for each pointer-sized word in that range,
+// The heap bitmap comprises 2 bits for each pointer-sized word in that range,
// stored in bytes indexed backward in memory from start.
-// That is, the byte at address start-1 holds the 4-bit entries for the two words
-// start, start+ptrSize, the byte at start-2 holds the entries for start+2*ptrSize,
-// start+3*ptrSize, and so on.
-// In the byte holding the entries for addresses p and p+ptrSize, the low 4 bits
-// describe p and the high 4 bits describe p+ptrSize.
+// That is, the byte at address start-1 holds the 2-bit entries for the four words
+// start through start+3*ptrSize, the byte at start-2 holds the entries for
+// start+4*ptrSize through start+7*ptrSize, and so on.
//
-// The 4 bits for each word are:
-// 0001 - not used
-// 0010 - bitMarked: this object has been marked by GC
-// tt00 - word type bits, as in a type bitmap.
+// In each 2-bit entry, the lower bit holds the same information as in the 1-bit
+// bitmaps: 0 means uninteresting and 1 means live pointer to be visited during GC.
+// The meaning of the high bit depends on the position of the word being described
+// in its allocated object. In the first word, the high bit is the GC ``marked'' bit.
+// In the second word, the high bit is the GC ``checkmarked'' bit (see below).
+// In the third and later words, the high bit indicates that the object is still
+// being described. In these words, if a bit pair with a high bit 0 is encountered,
+// the low bit can also be assumed to be 0, and the object description is over.
+// This 00 is called the ``dead'' encoding: it signals that the rest of the words
+// in the object are uninteresting to the garbage collector.
//
-// The code makes use of the fact that the zero value for a heap bitmap nibble
-// has no boundary bit set, no marked bit set, and type bits == typeDead.
+// The 2-bit entries are split when written into the byte, so that the top half
+// of the byte contains 4 mark bits and the bottom half contains 4 pointer bits.
+// This form allows a copy from the 1-bit to the 4-bit form to keep the
+// pointer bits contiguous, instead of having to space them out.
+//
+// The code makes use of the fact that the zero value for a heap bitmap
+// has no live pointer bit set and is (depending on position), not marked,
+// not checkmarked, and is the dead encoding.
// These properties must be preserved when modifying the encoding.
//
// Checkmarks
@@ -57,55 +49,71 @@
// collector implementation. As a sanity check, the GC has a 'checkmark'
// mode that retraverses the object graph with the world stopped, to make
// sure that everything that should be marked is marked.
-// In checkmark mode, in the heap bitmap, the type bits for the first word
-// of an object are redefined:
+// In checkmark mode, in the heap bitmap, the high bit of the 2-bit entry
+// for the second word of the object holds the checkmark bit.
+// When not in checkmark mode, this bit is set to 1.
//
-// 00 - typeScalarCheckmarked // typeScalar, checkmarked
-// 01 - typeScalar // typeScalar, not checkmarked
-// 10 - typePointer // typePointer, not checkmarked
-// 11 - typePointerCheckmarked // typePointer, checkmarked
-//
-// That is, typeDead is redefined to be typeScalar + a checkmark, and the
-// previously unused 11 pattern is redefined to be typePointer + a checkmark.
-// To prepare for this mode, we must move any typeDead in the first word of
-// a multiword object to the second word.
+// The smallest possible allocation is 8 bytes. On a 32-bit machine, that
+// means every allocated object has two words, so there is room for the
+// checkmark bit. On a 64-bit machine, however, the 8-byte allocation is
+// just one word, so the second bit pair is not available for encoding the
+// checkmark. However, because non-pointer allocations are combined
+// into larger 16-byte (maxTinySize) allocations, a plain 8-byte allocation
+// must be a pointer, so the type bit in the first word is not actually needed.
+// It is still used in general, except in checkmark the type bit is repurposed
+// as the checkmark bit and then reinitialized (to 1) as the type bit when
+// finished.
package runtime
import "unsafe"
const (
- typeDead = 0
- typeScalarCheckmarked = 0
- typeScalar = 1
- typePointer = 2
- typePointerCheckmarked = 3
+ bitPointer = 1 << 0
+ bitMarked = 1 << 4
- typeBitsWidth = 2 // # of type bits per pointer-sized word
- typeMask = 1<> h.shift
}
// isMarked reports whether the heap bits have the marked bit set.
+// h must describe the initial word of the object.
func (h heapBits) isMarked() bool {
return *h.bitp&(bitMarked<> (h.shift + typeShift)) & typeMask
+// isPointer reports whether the heap bits describe a pointer word.
+// h must describe the initial word of the object.
+func (h heapBits) isPointer() bool {
+ return (*h.bitp>>h.shift)&bitPointer != 0
+}
+
+// hasPointers reports whether the given object has any pointers.
+// It must be told how large the object at h is, so that it does not read too
+// far into the bitmap.
+// h must describe the initial word of the object.
+func (h heapBits) hasPointers(size uintptr) bool {
+ if size == ptrSize { // 1-word objects are always pointers
+ return true
+ }
+ // Otherwise, at least a 2-word object, and at least 2-word aligned,
+ // so h.shift is either 0 or 4, so we know we can get the bits for the
+ // first two words out of *h.bitp.
+ // If either of the first two words is a pointer, not pointer free.
+ b := uint32(*h.bitp >> h.shift)
+ if b&(bitPointer|bitPointer<>h.shift)&bitPointer != 0
+ }
+ // All multiword objects are 2-word aligned,
+ // so we know that the initial word's 2-bit pair
+ // and the second word's 2-bit pair are in the
+ // same heap bitmap byte, *h.bitp.
+ return (*h.bitp>>(heapBitsShift+h.shift))&bitMarked != 0
}
// setCheckmarked sets the checkmarked bit.
-func (h heapBits) setCheckmarked() {
- typ := h.typeBits()
- if typ == typeScalar {
- // Clear low type bit to turn 01 into 00.
- atomicand8(h.bitp, ^((1 << typeShift) << h.shift))
- } else if typ == typePointer {
- // Set low type bit to turn 10 into 11.
- atomicor8(h.bitp, (1<>typeShift)&typeMask == typeDead {
- x += (typeScalar - typeDead) << typeShift
- }
- if (x>>(4+typeShift))&typeMask == typeDead {
- x += (typeScalar - typeDead) << (4 + typeShift)
- }
- *bitp = uint8(x)
- bitp = subtractb(bitp, 1)
+ for i := uintptr(0); i < n; i += 4 {
+ *bitp &^= bitPointerAll
+ bitp = subtract1(bitp)
}
return
}
-
- // Update bottom nibble for first word of each object.
- // If the bottom nibble says typeDead, change to typeScalar
- // and clear top nibble to mark as typeDead.
- bitp := h.bitp
- step := size / heapBitmapScale
for i := uintptr(0); i < n; i++ {
- x := *bitp
- if (x>>typeShift)&typeMask == typeDead {
- x += (typeScalar - typeDead) << typeShift
- x &= 0x0f // clear top nibble to typeDead
- }
- bitp = subtractb(bitp, step)
+ *h.bitp &^= bitMarked << (heapBitsShift + h.shift)
+ h = h.forward(size / ptrSize)
}
}
-// clearCheckmarkSpan removes all the checkmarks from a span.
-// If it finds a multiword object starting with typeScalar typeDead,
-// it rewrites the heap bits to the simpler typeDead typeDead.
+// clearCheckmarkSpan undoes all the checkmarking in a span.
+// The actual checkmark bits are ignored, so the only work to do
+// is to fix the pointer bits. (Pointer bits are ignored by scanobject
+// but consulted by typedmemmove.)
func (h heapBits) clearCheckmarkSpan(size, n, total uintptr) {
- if size == ptrSize {
+ // The ptrSize == 8 is a compile-time constant false on 32-bit and eliminates this code entirely.
+ if ptrSize == 8 && size == ptrSize {
+ // Checkmark bit is type bit, bottom bit of every 2-bit entry.
// Only possible on 64-bit system, since minimum size is 8.
- // Must update both top and bottom nibble of each byte.
- // typeScalarCheckmarked can be left as typeDead,
- // but we want to change typeScalar back to typeDead.
+ // Must clear type bit (checkmark bit) of every word.
+ // The type bit is the lower of every two-bit pair.
bitp := h.bitp
- for i := uintptr(0); i < n; i += 2 {
- x := int(*bitp)
- switch typ := (x >> typeShift) & typeMask; typ {
- case typeScalar:
- x += (typeDead - typeScalar) << typeShift
- case typePointerCheckmarked:
- x += (typePointer - typePointerCheckmarked) << typeShift
- }
-
- switch typ := (x >> (4 + typeShift)) & typeMask; typ {
- case typeScalar:
- x += (typeDead - typeScalar) << (4 + typeShift)
- case typePointerCheckmarked:
- x += (typePointer - typePointerCheckmarked) << (4 + typeShift)
- }
-
- *bitp = uint8(x)
- bitp = subtractb(bitp, 1)
+ for i := uintptr(0); i < n; i += 4 {
+ *bitp |= bitPointerAll
+ bitp = subtract1(bitp)
}
- return
- }
-
- // Update bottom nibble for first word of each object.
- // If the bottom nibble says typeScalarCheckmarked and the top is not typeDead,
- // change to typeScalar. Otherwise leave, since typeScalarCheckmarked == typeDead.
- // If the bottom nibble says typePointerCheckmarked, change to typePointer.
- bitp := h.bitp
- step := size / heapBitmapScale
- for i := uintptr(0); i < n; i++ {
- x := int(*bitp)
- switch typ := (x >> typeShift) & typeMask; {
- case typ == typeScalarCheckmarked && (x>>(4+typeShift))&typeMask != typeDead:
- x += (typeScalar - typeScalarCheckmarked) << typeShift
- case typ == typePointerCheckmarked:
- x += (typePointer - typePointerCheckmarked) << typeShift
- }
-
- *bitp = uint8(x)
- bitp = subtractb(bitp, step)
}
}
@@ -393,348 +457,1046 @@ func (h heapBits) clearCheckmarkSpan(size, n, total uintptr) {
// bits for the first two words (or one for single-word objects) to typeDead
// and then calls f(p), where p is the object's base address.
// f is expected to add the object to a free list.
+// For non-free objects, heapBitsSweepSpan turns off the marked bit.
func heapBitsSweepSpan(base, size, n uintptr, f func(uintptr)) {
h := heapBitsForSpan(base)
- if size == ptrSize {
- // Only possible on 64-bit system, since minimum size is 8.
- // Must read and update both top and bottom nibble of each byte.
+ switch {
+ default:
+ throw("heapBitsSweepSpan")
+ case ptrSize == 8 && size == ptrSize:
+ // Consider mark bits in all four 2-bit entries of each bitmap byte.
bitp := h.bitp
- for i := uintptr(0); i < n; i += 2 {
- x := int(*bitp)
+ for i := uintptr(0); i < n; i += 4 {
+ x := uint32(*bitp)
+ // Note that unlike the other size cases, we leave the pointer bits set here.
+ // These are initialized during initSpan when the span is created and left
+ // in place the whole time the span is used for pointer-sized objects.
+ // That lets heapBitsSetType avoid an atomic update to set the pointer bit
+ // during allocation.
if x&bitMarked != 0 {
x &^= bitMarked
} else {
- x &^= typeMask << typeShift
f(base + i*ptrSize)
}
- if x&(bitMarked<<4) != 0 {
- x &^= bitMarked << 4
+ if x&(bitMarked< 2*ptrSize {
+ x = 0
+ }
+ }
+ *bitp = uint8(x)
+ if i+1 >= n {
+ break
+ }
+ bitp = subtractb(bitp, step)
+ x = uint32(*bitp)
+ if x&(bitMarked<<(2*heapBitsShift)) != 0 {
+ x &^= bitMarked << (2 * heapBitsShift)
+ } else {
+ x &^= (bitMarked|bitPointer)<<(2*heapBitsShift) | (bitMarked|bitPointer)<<(3*heapBitsShift)
+ f(base + (i+1)*size)
+ if size > 2*ptrSize {
+ *subtract1(bitp) = 0
+ }
+ }
+ *bitp = uint8(x)
+ bitp = subtractb(bitp, step+1)
}
- *bitp = uint8(x)
- bitp = subtractb(bitp, step)
}
}
-// TODO(rsc): Clean up the next two functions.
-
// heapBitsSetType records that the new allocation [x, x+size)
// holds in [x, x+dataSize) one or more values of type typ.
// (The number of values is given by dataSize / typ.size.)
// If dataSize < size, the fragment [x+dataSize, x+size) is
// recorded as non-pointer data.
+// It is known that the type has pointers somewhere;
+// malloc does not call heapBitsSetType when there are no pointers,
+// because all free objects are marked as noscan during
+// heapBitsSweepSpan.
+// There can only be one allocation from a given span active at a time,
+// so this code is not racing with other instances of itself,
+// and we don't allocate from a span until it has been swept,
+// so this code is not racing with heapBitsSweepSpan.
+// It is, however, racing with the concurrent GC mark phase,
+// which can be setting the mark bit in the leading 2-bit entry
+// of an allocated block. The block we are modifying is not quite
+// allocated yet, so the GC marker is not racing with updates to x's bits,
+// but if the start or end of x shares a bitmap byte with an adjacent
+// object, the GC marker is racing with updates to those object's mark bits.
func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
- // From here till marked label marking the object as allocated
- // and storing type info in the GC bitmap.
- h := heapBitsForAddr(x)
+ const doubleCheck = false // slow but helpful; enable to test modifications to this code
- var ti, te uintptr
- var ptrmask *uint8
- if size == ptrSize {
+ // dataSize is always size rounded up to the next malloc size class,
+ // except in the case of allocating a defer block, in which case
+ // size is sizeof(_defer{}) (at least 6 words) and dataSize may be
+ // arbitrarily larger.
+ //
+ // The checks for size == ptrSize and size == 2*ptrSize can therefore
+ // assume that dataSize == size without checking it explicitly.
+
+ if ptrSize == 8 && size == ptrSize {
// It's one word and it has pointers, it must be a pointer.
- // The bitmap byte is shared with the one-word object
- // next to it, and concurrent GC might be marking that
- // object, so we must use an atomic update.
- atomicor8(h.bitp, typePointer<<(typeShift+h.shift))
+ // In general we'd need an atomic update here if the
+ // concurrent GC were marking objects in this span,
+ // because each bitmap byte describes 3 other objects
+ // in addition to the one being allocated.
+ // However, since all allocated one-word objects are pointers
+ // (non-pointers are aggregated into tinySize allocations),
+ // initSpan sets the pointer bits for us. Nothing to do here.
+ if doubleCheck {
+ h := heapBitsForAddr(x)
+ if !h.isPointer() {
+ throw("heapBitsSetType: pointer bit missing")
+ }
+ }
return
}
- if typ.kind&kindGCProg != 0 {
- nptr := (uintptr(typ.size) + ptrSize - 1) / ptrSize
- masksize := nptr
- if masksize%2 != 0 {
- masksize *= 2 // repeated
- }
- const typeBitsPerByte = 8 / typeBitsWidth
- masksize = masksize * typeBitsPerByte / 8 // 4 bits per word
- masksize++ // unroll flag in the beginning
- if masksize > maxGCMask && typ.gc[1] != 0 {
- // write barriers have not been updated to deal with this case yet.
- throw("maxGCMask too small for now")
- // If the mask is too large, unroll the program directly
- // into the GC bitmap. It's 7 times slower than copying
- // from the pre-unrolled mask, but saves 1/16 of type size
- // memory for the mask.
- systemstack(func() {
- unrollgcproginplace_m(unsafe.Pointer(x), typ, size, dataSize)
- })
- return
- }
- ptrmask = (*uint8)(unsafe.Pointer(uintptr(typ.gc[0])))
- // Check whether the program is already unrolled
- // by checking if the unroll flag byte is set
- maskword := uintptr(atomicloadp(unsafe.Pointer(ptrmask)))
- if *(*uint8)(unsafe.Pointer(&maskword)) == 0 {
- systemstack(func() {
- unrollgcprog_m(typ)
- })
- }
- ptrmask = (*uint8)(add(unsafe.Pointer(ptrmask), 1)) // skip the unroll flag byte
- } else {
- ptrmask = (*uint8)(unsafe.Pointer(typ.gc[0])) // pointer to unrolled mask
- }
+
+ h := heapBitsForAddr(x)
+ ptrmask := typ.gcdata // start of 1-bit pointer mask (or GC program, handled below)
+
+ // Heap bitmap bits for 2-word object are only 4 bits,
+ // so also shared with objects next to it; use atomic updates.
+ // This is called out as a special case primarily for 32-bit systems,
+ // so that on 32-bit systems the code below can assume all objects
+ // are 4-word aligned (because they're all 16-byte aligned).
if size == 2*ptrSize {
- // h.shift is 0 for all sizes > ptrSize.
- *h.bitp = *ptrmask
- return
- }
- te = uintptr(typ.size) / ptrSize
- // If the type occupies odd number of words, its mask is repeated.
- if te%2 == 0 {
- te /= 2
- }
- // Copy pointer bitmask into the bitmap.
- // TODO(rlh): add comment addressing the following concerns:
- // If size > 2*ptrSize, is x guaranteed to be at least 2*ptrSize-aligned?
- // And if type occupies and odd number of words, why are we only going through half
- // of ptrmask and why don't we have to shift everything by 4 on odd iterations?
-
- for i := uintptr(0); i < dataSize; i += 2 * ptrSize {
- v := *(*uint8)(add(unsafe.Pointer(ptrmask), ti))
- ti++
- if ti == te {
- ti = 0
- }
- if i+ptrSize == dataSize {
- v &^= typeMask << (4 + typeShift)
- }
-
- *h.bitp = v
- h.bitp = subtractb(h.bitp, 1)
- }
- if dataSize%(2*ptrSize) == 0 && dataSize < size {
- // Mark the word after last object's word as typeDead.
- *h.bitp = 0
- }
-}
-
-// typeBitmapInHeapBitmapFormat returns a bitmap holding
-// the type bits for the type typ, but expanded into heap bitmap format
-// to make it easier to copy them into the heap bitmap.
-// TODO(rsc): Change clients to use the type bitmap format instead,
-// which can be stored more densely (especially if we drop to 1 bit per pointer).
-//
-// To make it easier to replicate the bits when filling out the heap
-// bitmap for an array of typ, if typ holds an odd number of words
-// (meaning the heap bitmap would stop halfway through a byte),
-// typeBitmapInHeapBitmapFormat returns the bitmap for two instances
-// of typ in a row.
-// TODO(rsc): Remove doubling.
-func typeBitmapInHeapBitmapFormat(typ *_type) []uint8 {
- var ptrmask *uint8
- nptr := (uintptr(typ.size) + ptrSize - 1) / ptrSize
- if typ.kind&kindGCProg != 0 {
- masksize := nptr
- if masksize%2 != 0 {
- masksize *= 2 // repeated
- }
- const typeBitsPerByte = 8 / typeBitsWidth
- masksize = masksize * typeBitsPerByte / 8 // 4 bits per word
- masksize++ // unroll flag in the beginning
- if masksize > maxGCMask && typ.gc[1] != 0 {
- // write barriers have not been updated to deal with this case yet.
- throw("maxGCMask too small for now")
- }
- ptrmask = (*uint8)(unsafe.Pointer(uintptr(typ.gc[0])))
- // Check whether the program is already unrolled
- // by checking if the unroll flag byte is set
- maskword := uintptr(atomicloadp(unsafe.Pointer(ptrmask)))
- if *(*uint8)(unsafe.Pointer(&maskword)) == 0 {
- systemstack(func() {
- unrollgcprog_m(typ)
- })
- }
- ptrmask = (*uint8)(add(unsafe.Pointer(ptrmask), 1)) // skip the unroll flag byte
- } else {
- ptrmask = (*uint8)(unsafe.Pointer(typ.gc[0])) // pointer to unrolled mask
- }
- return (*[1 << 30]byte)(unsafe.Pointer(ptrmask))[:(nptr+1)/2]
-}
-
-// GC type info programs
-//
-// TODO(rsc): Clean up and enable.
-
-const (
- // GC type info programs.
- // The programs allow to store type info required for GC in a compact form.
- // Most importantly arrays take O(1) space instead of O(n).
- // The program grammar is:
- //
- // Program = {Block} "insEnd"
- // Block = Data | Array
- // Data = "insData" DataSize DataBlock
- // DataSize = int // size of the DataBlock in bit pairs, 1 byte
- // DataBlock = binary // dense GC mask (2 bits per word) of size ]DataSize/4[ bytes
- // Array = "insArray" ArrayLen Block "insArrayEnd"
- // ArrayLen = int // length of the array, 8 bytes (4 bytes for 32-bit arch)
- //
- // Each instruction (insData, insArray, etc) is 1 byte.
- // For example, for type struct { x []byte; y [20]struct{ z int; w *byte }; }
- // the program looks as:
- //
- // insData 3 (typePointer typeScalar typeScalar)
- // insArray 20 insData 2 (typeScalar typePointer) insArrayEnd insEnd
- //
- // Total size of the program is 17 bytes (13 bytes on 32-bits).
- // The corresponding GC mask would take 43 bytes (it would be repeated
- // because the type has odd number of words).
- insData = 1 + iota
- insArray
- insArrayEnd
- insEnd
-
- // 64 bytes cover objects of size 1024/512 on 64/32 bits, respectively.
- maxGCMask = 65536 // TODO(rsc): change back to 64
-)
-
-// Recursively unrolls GC program in prog.
-// mask is where to store the result.
-// If inplace is true, store the result not in mask but in the heap bitmap for mask.
-// ppos is a pointer to position in mask, in bits.
-// sparse says to generate 4-bits per word mask for heap (1-bit for data/bss otherwise).
-//go:nowritebarrier
-func unrollgcprog1(maskp *byte, prog *byte, ppos *uintptr, inplace, sparse bool) *byte {
- pos := *ppos
- mask := (*[1 << 30]byte)(unsafe.Pointer(maskp))
- for {
- switch *prog {
- default:
- throw("unrollgcprog: unknown instruction")
-
- case insData:
- prog = addb(prog, 1)
- siz := int(*prog)
- prog = addb(prog, 1)
- p := (*[1 << 30]byte)(unsafe.Pointer(prog))
- for i := 0; i < siz; i++ {
- const typeBitsPerByte = 8 / typeBitsWidth
- v := p[i/typeBitsPerByte]
- v >>= (uint(i) % typeBitsPerByte) * typeBitsWidth
- v &= typeMask
- if inplace {
- // Store directly into GC bitmap.
- h := heapBitsForAddr(uintptr(unsafe.Pointer(&mask[pos])))
- if h.shift == 0 {
- *h.bitp = v << typeShift
- } else {
- *h.bitp |= v << (4 + typeShift)
- }
- pos += ptrSize
- } else if sparse {
- // 4-bits per word, type bits in high bits
- v <<= (pos % 8) + typeShift
- mask[pos/8] |= v
- pos += heapBitsWidth
+ if typ.size == ptrSize {
+ // We're allocating a block big enough to hold two pointers.
+ // On 64-bit, that means the actual object must be two pointers,
+ // or else we'd have used the one-pointer-sized block.
+ // On 32-bit, however, this is the 8-byte block, the smallest one.
+ // So it could be that we're allocating one pointer and this was
+ // just the smallest block available. Distinguish by checking dataSize.
+ // (In general the number of instances of typ being allocated is
+ // dataSize/typ.size.)
+ if ptrSize == 4 && dataSize == ptrSize {
+ // 1 pointer.
+ if gcphase == _GCoff {
+ *h.bitp |= bitPointer << h.shift
} else {
- // 1 bit per word, for data/bss bitmap
- v >>= 1 // convert typePointer to 1, others to 0
- mask[pos/8] |= v << (pos % 8)
- pos++
+ atomicor8(h.bitp, bitPointer<= nw {
+ goto Phase3
+ }
+ *hbitp = uint8(hb)
+ hbitp = subtract1(hbitp)
+ b >>= 4
+ nb -= 4
+
+ case ptrSize == 8 && h.shift == 2:
+ // Ptrmask and heap bitmap are misaligned.
+ // The bits for the first two words are in a byte shared with another object
+ // and must be updated atomically.
+ // NOTE(rsc): The atomic here may not be necessary.
+ // We took care of 1-word and 2-word objects above,
+ // so this is at least a 6-word object, so our start bits
+ // are shared only with the type bits of another object,
+ // not with its mark bit. Since there is only one allocation
+ // from a given span at a time, we should be able to set
+ // these bits non-atomically. Not worth the risk right now.
+ hb = (b & 3) << (2 * heapBitsShift)
+ b >>= 2
+ nb -= 2
+ // Note: no bitMarker in hb because the first two words don't get markers from us.
+ if gcphase == _GCoff {
+ *hbitp |= uint8(hb)
+ } else {
+ atomicor8(hbitp, uint8(hb))
+ }
+ hbitp = subtract1(hbitp)
+ if w += 2; w >= nw {
+ // We know that there is more data, because we handled 2-word objects above.
+ // This must be at least a 6-word object. If we're out of pointer words,
+ // mark no scan in next bitmap byte and finish.
+ hb = 0
+ w += 4
+ goto Phase3
+ }
+ }
+
+ // Phase 2: Full bytes in bitmap, up to but not including write to last byte (full or partial) in bitmap.
+ // The loop computes the bits for that last write but does not execute the write;
+ // it leaves the bits in hb for processing by phase 3.
+ // To avoid repeated adjustment of nb, we subtract out the 4 bits we're going to
+ // use in the first half of the loop right now, and then we only adjust nb explicitly
+ // if the 8 bits used by each iteration isn't balanced by 8 bits loaded mid-loop.
+ nb -= 4
+ for {
+ // Emit bitmap byte.
+ // b has at least nb+4 bits, with one exception:
+ // if w+4 >= nw, then b has only nw-w bits,
+ // but we'll stop at the break and then truncate
+ // appropriately in Phase 3.
+ hb = b & bitPointerAll
+ hb |= bitMarkedAll
+ if w += 4; w >= nw {
+ break
+ }
+ *hbitp = uint8(hb)
+ hbitp = subtract1(hbitp)
+ b >>= 4
+
+ // Load more bits. b has nb right now.
+ if p != endp {
+ // Fast path: keep reading from ptrmask.
+ // nb unmodified: we just loaded 8 bits,
+ // and the next iteration will consume 8 bits,
+ // leaving us with the same nb the next time we're here.
+ b |= uintptr(*p) << nb
+ p = add1(p)
+ } else if p == nil {
+ // Almost as fast path: track bit count and refill from pbits.
+ // For short repetitions.
+ if nb < 8 {
+ b |= pbits << nb
+ nb += endnb
+ }
+ nb -= 8 // for next iteration
+ } else {
+ // Slow path: reached end of ptrmask.
+ // Process final partial byte and rewind to start.
+ b |= uintptr(*p) << nb
+ nb += endnb
+ if nb < 8 {
+ b |= uintptr(*ptrmask) << nb
+ p = add1(ptrmask)
+ } else {
+ nb -= 8
+ p = ptrmask
+ }
+ }
+
+ // Emit bitmap byte.
+ hb = b & bitPointerAll
+ hb |= bitMarkedAll
+ if w += 4; w >= nw {
+ break
+ }
+ *hbitp = uint8(hb)
+ hbitp = subtract1(hbitp)
+ b >>= 4
+ }
+
+Phase3:
+ // Phase 3: Write last byte or partial byte and zero the rest of the bitmap entries.
+ if w > nw {
+ // Counting the 4 entries in hb not yet written to memory,
+ // there are more entries than possible pointer slots.
+ // Discard the excess entries (can't be more than 3).
+ mask := uintptr(1)<<(4-(w-nw)) - 1
+ hb &= mask | mask<<4 // apply mask to both pointer bits and mark bits
+ }
+
+ // Change nw from counting possibly-pointer words to total words in allocation.
+ nw = size / ptrSize
+
+ // Write whole bitmap bytes.
+ // The first is hb, the rest are zero.
+ if w <= nw {
+ *hbitp = uint8(hb)
+ hbitp = subtract1(hbitp)
+ hb = 0 // for possible final half-byte below
+ for w += 4; w <= nw; w += 4 {
+ *hbitp = 0
+ hbitp = subtract1(hbitp)
+ }
+ }
+
+ // Write final partial bitmap byte if any.
+ // We know w > nw, or else we'd still be in the loop above.
+ // It can be bigger only due to the 4 entries in hb that it counts.
+ // If w == nw+4 then there's nothing left to do: we wrote all nw entries
+ // and can discard the 4 sitting in hb.
+ // But if w == nw+2, we need to write first two in hb.
+ // The byte is shared with the next object so we may need an atomic.
+ if w == nw+2 {
+ if gcphase == _GCoff {
+ *hbitp = *hbitp&^(bitPointer|bitMarked|(bitPointer|bitMarked)<> h.shift) & (bitPointer | bitMarked)
+ if i >= totalptr {
+ want = 0 // deadmarker
+ if typ.kind&kindGCProg != 0 && i < (totalptr+3)/4*4 {
+ want = bitMarked
+ }
+ } else {
+ if j < nptr && (*addb(ptrmask, j/8)>>(j%8))&1 != 0 {
+ want |= bitPointer
+ }
+ if i >= 2 {
+ want |= bitMarked
+ } else {
+ have &^= bitMarked
+ }
+ }
+ if have != want {
+ println("mismatch writing bits for", *typ._string, "x", dataSize/typ.size)
+ print("typ.size=", typ.size, " typ.ptrdata=", typ.ptrdata, " dataSize=", dataSize, " size=", size, "\n")
+ print("kindGCProg=", typ.kind&kindGCProg != 0, "\n")
+ print("w=", w, " nw=", nw, " b=", hex(b), " nb=", nb, " hb=", hex(hb), "\n")
+ h0 := heapBitsForAddr(x)
+ print("initial bits h0.bitp=", h0.bitp, " h0.shift=", h0.shift, "\n")
+ print("current bits h.bitp=", h.bitp, " h.shift=", h.shift, " *h.bitp=", hex(*h.bitp), "\n")
+ print("ptrmask=", ptrmask, " p=", p, " endp=", endp, " endnb=", endnb, " pbits=", hex(pbits), " b=", hex(b), " nb=", nb, "\n")
+ println("at word", i, "offset", i*ptrSize, "have", have, "want", want)
+ if typ.kind&kindGCProg != 0 {
+ println("GC program:")
+ dumpGCProg(addb(typ.gcdata, 4))
+ }
+ throw("bad heapBitsSetType")
+ }
+ h = h.next()
+ }
+ if ptrmask == debugPtrmask.data {
+ unlock(&debugPtrmask.lock)
}
}
}
-// Unrolls GC program prog for data/bss, returns dense GC mask.
-func unrollglobgcprog(prog *byte, size uintptr) bitvector {
- masksize := round(round(size, ptrSize)/ptrSize, 8) / 8
- mask := (*[1 << 30]byte)(persistentalloc(masksize+1, 0, &memstats.gc_sys))
- mask[masksize] = 0xa1
- pos := uintptr(0)
- prog = unrollgcprog1(&mask[0], prog, &pos, false, false)
- if pos != size/ptrSize {
- print("unrollglobgcprog: bad program size, got ", pos, ", expect ", size/ptrSize, "\n")
- throw("unrollglobgcprog: bad program size")
- }
- if *prog != insEnd {
- throw("unrollglobgcprog: program does not end with insEnd")
- }
- if mask[masksize] != 0xa1 {
- throw("unrollglobgcprog: overflow")
- }
- return bitvector{int32(masksize * 8), &mask[0]}
+var debugPtrmask struct {
+ lock mutex
+ data *byte
}
-func unrollgcproginplace_m(v unsafe.Pointer, typ *_type, size, size0 uintptr) {
- // TODO(rsc): Explain why these non-atomic updates are okay.
- pos := uintptr(0)
- prog := (*byte)(unsafe.Pointer(uintptr(typ.gc[1])))
- for pos != size0 {
- unrollgcprog1((*byte)(v), prog, &pos, true, true)
+// heapBitsSetTypeGCProg implements heapBitsSetType using a GC program.
+// progSize is the size of the memory described by the program.
+// elemSize is the size of the element that the GC program describes (a prefix of).
+// dataSize is the total size of the intended data, a multiple of elemSize.
+// allocSize is the total size of the allocated memory.
+//
+// GC programs are only used for large allocations.
+// heapBitsSetType requires that allocSize is a multiple of 4 words,
+// so that the relevant bitmap bytes are not shared with surrounding
+// objects and need not be accessed with atomic instructions.
+func heapBitsSetTypeGCProg(h heapBits, progSize, elemSize, dataSize, allocSize uintptr, prog *byte) {
+ if ptrSize == 8 && allocSize%(4*ptrSize) != 0 {
+ // Alignment will be wrong.
+ throw("heapBitsSetTypeGCProg: small allocation")
}
+ var totalBits uintptr
+ if elemSize == dataSize {
+ totalBits = runGCProg(prog, nil, h.bitp, 2)
+ if totalBits*ptrSize != progSize {
+ println("runtime: heapBitsSetTypeGCProg: total bits", totalBits, "but progSize", progSize)
+ throw("heapBitsSetTypeGCProg: unexpected bit count")
+ }
+ } else {
+ count := dataSize / elemSize
- // Mark first word as bitAllocated.
- // Mark word after last as typeDead.
- if size0 < size {
- h := heapBitsForAddr(uintptr(v) + size0)
- *h.bitp &^= typeMask << typeShift
+ // Piece together program trailer to run after prog that does:
+ // literal(0)
+ // repeat(1, elemSize-progSize-1) // zeros to fill element size
+ // repeat(elemSize, count-1) // repeat that element for count
+ // This zero-pads the data remaining in the first element and then
+ // repeats that first element to fill the array.
+ var trailer [40]byte // 3 varints (max 10 each) + some bytes
+ i := 0
+ if n := elemSize/ptrSize - progSize/ptrSize; n > 0 {
+ // literal(0)
+ trailer[i] = 0x01
+ i++
+ trailer[i] = 0
+ i++
+ if n > 1 {
+ // repeat(1, n-1)
+ trailer[i] = 0x81
+ i++
+ n--
+ for ; n >= 0x80; n >>= 7 {
+ trailer[i] = byte(n | 0x80)
+ i++
+ }
+ trailer[i] = byte(n)
+ i++
+ }
+ }
+ // repeat(elemSize/ptrSize, count-1)
+ trailer[i] = 0x80
+ i++
+ n := elemSize / ptrSize
+ for ; n >= 0x80; n >>= 7 {
+ trailer[i] = byte(n | 0x80)
+ i++
+ }
+ trailer[i] = byte(n)
+ i++
+ n = count
+ for ; n >= 0x80; n >>= 7 {
+ trailer[i] = byte(n | 0x80)
+ i++
+ }
+ trailer[i] = byte(n)
+ i++
+ trailer[i] = 0
+ i++
+
+ runGCProg(prog, &trailer[0], h.bitp, 2)
+
+ // Even though we filled in the full array just now,
+ // record that we only filled in up to the ptrdata of the
+ // last element. This will cause the code below to
+ // memclr the dead section of the final array element,
+ // so that scanobject can stop early in the final element.
+ totalBits = (elemSize*(count-1) + progSize) / ptrSize
}
+ endProg := unsafe.Pointer(subtractb(h.bitp, (totalBits+3)/4))
+ endAlloc := unsafe.Pointer(subtractb(h.bitp, allocSize/heapBitmapScale))
+ memclr(add(endAlloc, 1), uintptr(endProg)-uintptr(endAlloc))
}
-var unroll mutex
-
-// Unrolls GC program in typ.gc[1] into typ.gc[0]
-//go:nowritebarrier
-func unrollgcprog_m(typ *_type) {
- lock(&unroll)
- mask := (*byte)(unsafe.Pointer(uintptr(typ.gc[0])))
- if *mask == 0 {
- pos := uintptr(8) // skip the unroll flag
- prog := (*byte)(unsafe.Pointer(uintptr(typ.gc[1])))
- prog = unrollgcprog1(mask, prog, &pos, false, true)
- if *prog != insEnd {
- throw("unrollgcprog: program does not end with insEnd")
- }
- if typ.size/ptrSize%2 != 0 {
- // repeat the program
- prog := (*byte)(unsafe.Pointer(uintptr(typ.gc[1])))
- unrollgcprog1(mask, prog, &pos, false, true)
- }
-
- // atomic way to say mask[0] = 1
- atomicor8(mask, 1)
+// progToPointerMask returns the 1-bit pointer mask output by the GC program prog.
+// size the size of the region described by prog, in bytes.
+// The resulting bitvector will have no more than size/ptrSize bits.
+func progToPointerMask(prog *byte, size uintptr) bitvector {
+ n := (size/ptrSize + 7) / 8
+ x := (*[1 << 30]byte)(persistentalloc(n+1, 1, &memstats.buckhash_sys))[:n+1]
+ x[len(x)-1] = 0xa1 // overflow check sentinel
+ n = runGCProg(prog, nil, &x[0], 1)
+ if x[len(x)-1] != 0xa1 {
+ throw("progToPointerMask: overflow")
+ }
+ return bitvector{int32(n), &x[0]}
+}
+
+// Packed GC pointer bitmaps, aka GC programs.
+//
+// For large types containing arrays, the type information has a
+// natural repetition that can be encoded to save space in the
+// binary and in the memory representation of the type information.
+//
+// The encoding is a simple Lempel-Ziv style bytecode machine
+// with the following instructions:
+//
+// 00000000: stop
+// 0nnnnnnn: emit n bits copied from the next (n+7)/8 bytes
+// 10000000 n c: repeat the previous n bits c times; n, c are varints
+// 1nnnnnnn c: repeat the previous n bits c times; c is a varint
+
+// runGCProg executes the GC program prog, and then trailer if non-nil,
+// writing to dst with entries of the given size.
+// If size == 1, dst is a 1-bit pointer mask laid out moving forward from dst.
+// If size == 2, dst is the 2-bit heap bitmap, and writes move backward
+// starting at dst (because the heap bitmap does). In this case, the caller guarantees
+// that only whole bytes in dst need to be written.
+//
+// runGCProg returns the number of 1- or 2-bit entries written to memory.
+func runGCProg(prog, trailer, dst *byte, size int) uintptr {
+ dstStart := dst
+
+ // Bits waiting to be written to memory.
+ var bits uintptr
+ var nbits uintptr
+
+ p := prog
+Run:
+ for {
+ // Flush accumulated full bytes.
+ // The rest of the loop assumes that nbits <= 7.
+ for ; nbits >= 8; nbits -= 8 {
+ if size == 1 {
+ *dst = uint8(bits)
+ dst = add1(dst)
+ bits >>= 8
+ } else {
+ v := bits&bitPointerAll | bitMarkedAll
+ *dst = uint8(v)
+ dst = subtract1(dst)
+ bits >>= 4
+ v = bits&bitPointerAll | bitMarkedAll
+ *dst = uint8(v)
+ dst = subtract1(dst)
+ bits >>= 4
+ }
+ }
+
+ // Process one instruction.
+ inst := uintptr(*p)
+ p = add1(p)
+ n := inst & 0x7F
+ if inst&0x80 == 0 {
+ // Literal bits; n == 0 means end of program.
+ if n == 0 {
+ // Program is over; continue in trailer if present.
+ if trailer != nil {
+ //println("trailer")
+ p = trailer
+ trailer = nil
+ continue
+ }
+ //println("done")
+ break Run
+ }
+ //println("lit", n, dst)
+ nbyte := n / 8
+ for i := uintptr(0); i < nbyte; i++ {
+ bits |= uintptr(*p) << nbits
+ p = add1(p)
+ if size == 1 {
+ *dst = uint8(bits)
+ dst = add1(dst)
+ bits >>= 8
+ } else {
+ v := bits&0xf | bitMarkedAll
+ *dst = uint8(v)
+ dst = subtract1(dst)
+ bits >>= 4
+ v = bits&0xf | bitMarkedAll
+ *dst = uint8(v)
+ dst = subtract1(dst)
+ bits >>= 4
+ }
+ }
+ if n %= 8; n > 0 {
+ bits |= uintptr(*p) << nbits
+ p = add1(p)
+ nbits += n
+ }
+ continue Run
+ }
+
+ // Repeat. If n == 0, it is encoded in a varint in the next bytes.
+ if n == 0 {
+ for off := uint(0); ; off += 7 {
+ x := uintptr(*p)
+ p = add1(p)
+ n |= (x & 0x7F) << off
+ if x&0x80 == 0 {
+ break
+ }
+ }
+ }
+
+ // Count is encoded in a varint in the next bytes.
+ c := uintptr(0)
+ for off := uint(0); ; off += 7 {
+ x := uintptr(*p)
+ p = add1(p)
+ c |= (x & 0x7F) << off
+ if x&0x80 == 0 {
+ break
+ }
+ }
+ c *= n // now total number of bits to copy
+
+ // If the number of bits being repeated is small, load them
+ // into a register and use that register for the entire loop
+ // instead of repeatedly reading from memory.
+ // Handling fewer than 8 bits here makes the general loop simpler.
+ // The cutoff is ptrSize*8 - 7 to guarantee that when we add
+ // the pattern to a bit buffer holding at most 7 bits (a partial byte)
+ // it will not overflow.
+ src := dst
+ const maxBits = ptrSize*8 - 7
+ if n <= maxBits {
+ // Start with bits in output buffer.
+ pattern := bits
+ npattern := nbits
+
+ // If we need more bits, fetch them from memory.
+ if size == 1 {
+ src = subtract1(src)
+ for npattern < n {
+ pattern <<= 8
+ pattern |= uintptr(*src)
+ src = subtract1(src)
+ npattern += 8
+ }
+ } else {
+ src = add1(src)
+ for npattern < n {
+ pattern <<= 4
+ pattern |= uintptr(*src) & 0xf
+ src = add1(src)
+ npattern += 4
+ }
+ }
+
+ // We started with the whole bit output buffer,
+ // and then we loaded bits from whole bytes.
+ // Either way, we might now have too many instead of too few.
+ // Discard the extra.
+ if npattern > n {
+ pattern >>= npattern - n
+ npattern = n
+ }
+
+ // Replicate pattern to at most maxBits.
+ if npattern == 1 {
+ // One bit being repeated.
+ // If the bit is 1, make the pattern all 1s.
+ // If the bit is 0, the pattern is already all 0s,
+ // but we can claim that the number of bits
+ // in the word is equal to the number we need (c),
+ // because right shift of bits will zero fill.
+ if pattern == 1 {
+ pattern = 1<8 bits, there will be full bytes to flush
+ // on each iteration.
+ for ; c >= npattern; c -= npattern {
+ bits |= pattern << nbits
+ nbits += npattern
+ if size == 1 {
+ for nbits >= 8 {
+ *dst = uint8(bits)
+ dst = add1(dst)
+ bits >>= 8
+ nbits -= 8
+ }
+ } else {
+ for nbits >= 4 {
+ *dst = uint8(bits&0xf | bitMarkedAll)
+ dst = subtract1(dst)
+ bits >>= 4
+ nbits -= 4
+ }
+ }
+ }
+
+ // Add final fragment to bit buffer.
+ if c > 0 {
+ pattern &= 1< nbits because n > maxBits and nbits <= 7
+ if size == 1 {
+ // Leading src fragment.
+ src = subtractb(src, (off+7)/8)
+ if frag := off & 7; frag != 0 {
+ bits |= uintptr(*src) >> (8 - frag) << nbits
+ src = add1(src)
+ nbits += frag
+ c -= frag
+ }
+ // Main loop: load one byte, write another.
+ // The bits are rotating through the bit buffer.
+ for i := c / 8; i > 0; i-- {
+ bits |= uintptr(*src) << nbits
+ src = add1(src)
+ *dst = uint8(bits)
+ dst = add1(dst)
+ bits >>= 8
+ }
+ // Final src fragment.
+ if c %= 8; c > 0 {
+ bits |= (uintptr(*src) & (1<> (4 - frag) << nbits
+ src = subtract1(src)
+ nbits += frag
+ c -= frag
+ }
+ // Main loop: load one byte, write another.
+ // The bits are rotating through the bit buffer.
+ for i := c / 4; i > 0; i-- {
+ bits |= (uintptr(*src) & 0xf) << nbits
+ src = subtract1(src)
+ *dst = uint8(bits&0xf | bitMarkedAll)
+ dst = subtract1(dst)
+ bits >>= 4
+ }
+ // Final src fragment.
+ if c %= 4; c > 0 {
+ bits |= (uintptr(*src) & (1< 0; nbits -= 8 {
+ *dst = uint8(bits)
+ dst = add1(dst)
+ bits >>= 8
+ }
+ } else {
+ totalBits = (uintptr(unsafe.Pointer(dstStart))-uintptr(unsafe.Pointer(dst)))*4 + nbits
+ nbits += -nbits & 3
+ for ; nbits > 0; nbits -= 4 {
+ v := bits&0xf | bitMarkedAll
+ *dst = uint8(v)
+ dst = subtract1(dst)
+ bits >>= 4
+ }
+ // Clear the mark bits in the first two entries.
+ // They are the actual mark and checkmark bits,
+ // not non-dead markers. It simplified the code
+ // above to set the marker in every bit written and
+ // then clear these two as a special case at the end.
+ *dstStart &^= bitMarked | bitMarked< nptr && ret[len(ret)-1] == 0 {
+ ret = ret[:len(ret)-1]
+ }
+ return ret
+}
- // data
+// Returns GC type info for object p for testing.
+func getgcmask(ep interface{}) (mask []byte) {
+ e := *(*eface)(unsafe.Pointer(&ep))
+ p := e.data
+ t := e._type
+ // data or bss
for datap := &firstmoduledata; datap != nil; datap = datap.next {
+ // data
if datap.data <= uintptr(p) && uintptr(p) < datap.edata {
+ bitmap := datap.gcdatamask.bytedata
n := (*ptrtype)(unsafe.Pointer(t)).elem.size
- *len = n / ptrSize
- *mask = &make([]byte, *len)[0]
+ mask = make([]byte, n/ptrSize)
for i := uintptr(0); i < n; i += ptrSize {
off := (uintptr(p) + i - datap.data) / ptrSize
- bits := (*addb(datap.gcdatamask.bytedata, off/8) >> (off % 8)) & 1
- bits += 1 // convert 1-bit to 2-bit
- *addb(*mask, i/ptrSize) = bits
+ mask[i/ptrSize] = (*addb(bitmap, off/8) >> (off % 8)) & 1
}
return
}
// bss
if datap.bss <= uintptr(p) && uintptr(p) < datap.ebss {
+ bitmap := datap.gcbssmask.bytedata
n := (*ptrtype)(unsafe.Pointer(t)).elem.size
- *len = n / ptrSize
- *mask = &make([]byte, *len)[0]
+ mask = make([]byte, n/ptrSize)
for i := uintptr(0); i < n; i += ptrSize {
off := (uintptr(p) + i - datap.bss) / ptrSize
- bits := (*addb(datap.gcbssmask.bytedata, off/8) >> (off % 8)) & 1
- bits += 1 // convert 1-bit to 2-bit
- *addb(*mask, i/ptrSize) = bits
+ mask[i/ptrSize] = (*addb(bitmap, off/8) >> (off % 8)) & 1
}
return
}
@@ -787,47 +1559,58 @@ func getgcmask(p unsafe.Pointer, t *_type, mask **byte, len *uintptr) {
var n uintptr
var base uintptr
if mlookup(uintptr(p), &base, &n, nil) != 0 {
- *len = n / ptrSize
- *mask = &make([]byte, *len)[0]
+ mask = make([]byte, n/ptrSize)
for i := uintptr(0); i < n; i += ptrSize {
- bits := heapBitsForAddr(base + i).typeBits()
- *addb(*mask, i/ptrSize) = bits
+ hbits := heapBitsForAddr(base + i)
+ if hbits.isPointer() {
+ mask[i/ptrSize] = 1
+ }
+ if i >= 2*ptrSize && !hbits.isMarked() {
+ mask = mask[:i/ptrSize]
+ break
+ }
}
return
}
// stack
- var frame stkframe
- frame.sp = uintptr(p)
- _g_ := getg()
- gentraceback(_g_.m.curg.sched.pc, _g_.m.curg.sched.sp, 0, _g_.m.curg, 0, nil, 1000, getgcmaskcb, noescape(unsafe.Pointer(&frame)), 0)
- if frame.fn != nil {
- f := frame.fn
- targetpc := frame.continpc
- if targetpc == 0 {
- return
- }
- if targetpc != f.entry {
- targetpc--
- }
- pcdata := pcdatavalue(f, _PCDATA_StackMapIndex, targetpc)
- if pcdata == -1 {
- return
- }
- stkmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps))
- if stkmap == nil || stkmap.n <= 0 {
- return
- }
- bv := stackmapdata(stkmap, pcdata)
- size := uintptr(bv.n) * ptrSize
- n := (*ptrtype)(unsafe.Pointer(t)).elem.size
- *len = n / ptrSize
- *mask = &make([]byte, *len)[0]
- for i := uintptr(0); i < n; i += ptrSize {
- off := (uintptr(p) + i - frame.varp + size) / ptrSize
- bits := (*addb(bv.bytedata, off/8) >> (off % 8)) & 1
- bits += 1 // convert 1-bit to 2-bit
- *addb(*mask, i/ptrSize) = bits
+ if _g_ := getg(); _g_.m.curg.stack.lo <= uintptr(p) && uintptr(p) < _g_.m.curg.stack.hi {
+ var frame stkframe
+ frame.sp = uintptr(p)
+ _g_ := getg()
+ gentraceback(_g_.m.curg.sched.pc, _g_.m.curg.sched.sp, 0, _g_.m.curg, 0, nil, 1000, getgcmaskcb, noescape(unsafe.Pointer(&frame)), 0)
+ if frame.fn != nil {
+ f := frame.fn
+ targetpc := frame.continpc
+ if targetpc == 0 {
+ return
+ }
+ if targetpc != f.entry {
+ targetpc--
+ }
+ pcdata := pcdatavalue(f, _PCDATA_StackMapIndex, targetpc)
+ if pcdata == -1 {
+ return
+ }
+ stkmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps))
+ if stkmap == nil || stkmap.n <= 0 {
+ return
+ }
+ bv := stackmapdata(stkmap, pcdata)
+ size := uintptr(bv.n) * ptrSize
+ n := (*ptrtype)(unsafe.Pointer(t)).elem.size
+ mask = make([]byte, n/ptrSize)
+ for i := uintptr(0); i < n; i += ptrSize {
+ bitmap := bv.bytedata
+ off := (uintptr(p) + i - frame.varp + size) / ptrSize
+ mask[i/ptrSize] = (*addb(bitmap, off/8) >> (off % 8)) & 1
+ }
}
+ return
}
+
+ // otherwise, not something the GC knows about.
+ // possibly read-only data, like malloc(0).
+ // must not have pointers
+ return
}
diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go
index 9bd36d1a5e..db5b2dcd36 100644
--- a/src/runtime/mgc.go
+++ b/src/runtime/mgc.go
@@ -127,13 +127,22 @@ const (
_RootCount = 5
)
-// heapminimum is the minimum number of bytes in the heap.
-// This cleans up the corner case of where we have a very small live set but a lot
-// of allocations and collecting every GOGC * live set is expensive.
-// heapminimum is adjust by multiplying it by GOGC/100. In
-// the special case of GOGC==0 this will set heapminimum to 0 resulting
-// collecting at every allocation even when the heap size is small.
-var heapminimum = uint64(4 << 20)
+// heapminimum is the minimum heap size at which to trigger GC.
+// For small heaps, this overrides the usual GOGC*live set rule.
+//
+// When there is a very small live set but a lot of allocation, simply
+// collecting when the heap reaches GOGC*live results in many GC
+// cycles and high total per-GC overhead. This minimum amortizes this
+// per-GC overhead while keeping the heap reasonably small.
+//
+// During initialization this is set to 4MB*GOGC/100. In the case of
+// GOGC==0, this will set heapminimum to 0, resulting in constant
+// collection even when the heap size is small, which is useful for
+// debugging.
+var heapminimum uint64 = defaultHeapMinimum
+
+// defaultHeapMinimum is the value of heapminimum for GOGC==100.
+const defaultHeapMinimum = 4 << 20
// Initialized from $GOGC. GOGC=off means no GC.
var gcpercent int32
@@ -146,8 +155,8 @@ func gcinit() {
work.markfor = parforalloc(_MaxGcproc)
_ = setGCPercent(readgogc())
for datap := &firstmoduledata; datap != nil; datap = datap.next {
- datap.gcdatamask = unrollglobgcprog((*byte)(unsafe.Pointer(datap.gcdata)), datap.edata-datap.data)
- datap.gcbssmask = unrollglobgcprog((*byte)(unsafe.Pointer(datap.gcbss)), datap.ebss-datap.bss)
+ datap.gcdatamask = progToPointerMask((*byte)(unsafe.Pointer(datap.gcdata)), datap.edata-datap.data)
+ datap.gcbssmask = progToPointerMask((*byte)(unsafe.Pointer(datap.gcbss)), datap.ebss-datap.bss)
}
memstats.next_gc = heapminimum
}
@@ -180,7 +189,7 @@ func setGCPercent(in int32) (out int32) {
in = -1
}
gcpercent = in
- heapminimum = heapminimum * uint64(gcpercent) / 100
+ heapminimum = defaultHeapMinimum * uint64(gcpercent) / 100
unlock(&mheap_.lock)
return out
}
@@ -197,7 +206,6 @@ var gcBlackenEnabled uint32
const (
_GCoff = iota // GC not running, write barrier disabled
- _GCquiesce // unused state
_GCstw // unused state
_GCscan // GC collecting roots into workbufs, write barrier disabled
_GCmark // GC marking from workbufs, write barrier ENABLED
@@ -208,7 +216,7 @@ const (
//go:nosplit
func setGCPhase(x uint32) {
atomicstore(&gcphase, x)
- writeBarrierEnabled = gcphase == _GCmark || gcphase == _GCmarktermination || mheap_.shadow_enabled
+ writeBarrierEnabled = gcphase == _GCmark || gcphase == _GCmarktermination
}
// gcMarkWorkerMode represents the mode that a concurrent mark worker
@@ -699,11 +707,11 @@ const (
func startGC(mode int) {
// The gc is turned off (via enablegc) until the bootstrap has completed.
// Also, malloc gets called in the guts of a number of libraries that might be
- // holding locks. To avoid deadlocks during stoptheworld, don't bother
+ // holding locks. To avoid deadlocks during stop-the-world, don't bother
// trying to run gc while holding a lock. The next mallocgc without a lock
// will do the gc instead.
mp := acquirem()
- if gp := getg(); gp == mp.g0 || mp.locks > 1 || !memstats.enablegc || panicking != 0 || gcpercent < 0 {
+ if gp := getg(); gp == mp.g0 || mp.locks > 1 || mp.preemptoff != "" || !memstats.enablegc || panicking != 0 || gcpercent < 0 {
releasem(mp)
return
}
@@ -797,7 +805,7 @@ func gc(mode int) {
traceGCStart()
}
- systemstack(stoptheworld)
+ systemstack(stopTheWorldWithSema)
systemstack(finishsweep_m) // finish sweep before we start concurrent scan.
// clearpools before we start the GC. If we wait they memory will not be
// reclaimed until the next GC cycle.
@@ -814,7 +822,7 @@ func gc(mode int) {
setGCPhase(_GCscan)
// Concurrent scan.
- starttheworld()
+ startTheWorldWithSema()
if debug.gctrace > 0 {
tScan = nanotime()
}
@@ -858,7 +866,7 @@ func gc(mode int) {
if debug.gctrace > 0 {
tMarkTerm = nanotime()
}
- systemstack(stoptheworld)
+ systemstack(stopTheWorldWithSema)
// The gcphase is _GCmark, it will transition to _GCmarktermination
// below. The important thing is that the wb remains active until
// all marking is complete. This includes writes made by the GC.
@@ -952,13 +960,12 @@ func gc(mode int) {
// all done
mp.preemptoff = ""
- semrelease(&worldsema)
-
if gcphase != _GCoff {
throw("gc done but gcphase != _GCoff")
}
- systemstack(starttheworld)
+ systemstack(startTheWorldWithSema)
+ semrelease(&worldsema)
releasem(mp)
mp = nil
@@ -1160,6 +1167,18 @@ func gcBgMarkDone() {
}
}
+// gcMarkWorkAvailable determines if mark work is readily available.
+// It is used by the scheduler to decide if this p run a mark work.
+func gcMarkWorkAvailable(p *p) bool {
+ if !p.gcw.empty() {
+ return true
+ }
+ if atomicload64(&work.full) != 0 || atomicload64(&work.partial) != 0 {
+ return true // global work available
+ }
+ return false
+}
+
// gcFlushGCWork disposes the gcWork caches of all Ps. The world must
// be stopped.
//go:nowritebarrier
diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go
index 9d78ddecae..62fa33895b 100644
--- a/src/runtime/mgcmark.go
+++ b/src/runtime/mgcmark.go
@@ -261,7 +261,7 @@ func gcphasework(gp *g) {
switch gcphase {
default:
throw("gcphasework in bad gcphase")
- case _GCoff, _GCquiesce, _GCstw, _GCsweep:
+ case _GCoff, _GCstw, _GCsweep:
// No work.
case _GCscan:
// scan the stack, mark the objects, put pointers in work buffers
@@ -557,9 +557,6 @@ func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork) {
// Same work as in scanobject; see comments there.
obj := *(*uintptr)(unsafe.Pointer(b + i))
if obj != 0 && arena_start <= obj && obj < arena_used {
- if mheap_.shadow_enabled && debug.wbshadow >= 2 && debug.gccheckmark > 0 && useCheckmark {
- checkwbshadow((*uintptr)(unsafe.Pointer(b + i)))
- }
if obj, hbits, span := heapBitsForObject(obj); obj != 0 {
greyobject(obj, b, i, hbits, span, gcw)
}
@@ -597,32 +594,25 @@ func scanobject(b uintptr, gcw *gcWork) {
// Avoid needless hbits.next() on last iteration.
hbits = hbits.next()
}
- bits := uintptr(hbits.typeBits())
- if bits == typeDead {
+ // During checkmarking, 1-word objects store the checkmark
+ // in the type bit for the one word. The only one-word objects
+ // are pointers, or else they'd be merged with other non-pointer
+ // data into larger allocations.
+ bits := hbits.bits()
+ if i >= 2*ptrSize && bits&bitMarked == 0 {
break // no more pointers in this object
}
-
- if bits <= typeScalar { // typeScalar, typeDead, typeScalarMarked
- continue
+ if bits&bitPointer == 0 {
+ continue // not a pointer
}
- if bits&typePointer != typePointer {
- print("gc useCheckmark=", useCheckmark, " b=", hex(b), "\n")
- throw("unexpected garbage collection bits")
- }
-
- // Work here is duplicated in scanblock.
+ // Work here is duplicated in scanblock and above.
// If you make changes here, make changes there too.
-
obj := *(*uintptr)(unsafe.Pointer(b + i))
// At this point we have extracted the next potential pointer.
- // Check if it points into heap.
- if obj != 0 && arena_start <= obj && obj < arena_used {
- if mheap_.shadow_enabled && debug.wbshadow >= 2 && debug.gccheckmark > 0 && useCheckmark {
- checkwbshadow((*uintptr)(unsafe.Pointer(b + i)))
- }
-
+ // Check if it points into heap and not back at the current object.
+ if obj != 0 && arena_start <= obj && obj < arena_used && obj-b >= n {
// Mark the object.
if obj, hbits, span := heapBitsForObject(obj); obj != 0 {
greyobject(obj, b, i, hbits, span, gcw)
@@ -673,11 +663,11 @@ func greyobject(obj, base, off uintptr, hbits heapBits, span *mspan, gcw *gcWork
throw("checkmark found unmarked object")
}
- if hbits.isCheckmarked() {
+ if hbits.isCheckmarked(span.elemsize) {
return
}
- hbits.setCheckmarked()
- if !hbits.isCheckmarked() {
+ hbits.setCheckmarked(span.elemsize)
+ if !hbits.isCheckmarked(span.elemsize) {
throw("setCheckmarked and isCheckmarked disagree")
}
} else {
@@ -685,12 +675,11 @@ func greyobject(obj, base, off uintptr, hbits heapBits, span *mspan, gcw *gcWork
if hbits.isMarked() {
return
}
-
hbits.setMarked()
// If this is a noscan object, fast-track it to black
// instead of greying it.
- if hbits.typeBits() == typeDead {
+ if !hbits.hasPointers(span.elemsize) {
gcw.bytesMarked += uint64(span.elemsize)
return
}
diff --git a/src/runtime/mgcwork.go b/src/runtime/mgcwork.go
index 9c32ae8880..b7feb847b4 100644
--- a/src/runtime/mgcwork.go
+++ b/src/runtime/mgcwork.go
@@ -7,7 +7,7 @@ package runtime
import "unsafe"
const (
- _Debugwbufs = true // if true check wbufs consistency
+ _Debugwbufs = false // if true check wbufs consistency
_WorkbufSize = 1 * 256 // in bytes - if small wbufs are passed to GC in a timely fashion.
)
@@ -182,6 +182,13 @@ func (w *gcWork) balance() {
}
}
+// empty returns true if w has no mark work available.
+//go:nowritebarrier
+func (w *gcWork) empty() bool {
+ wbuf := w.wbuf
+ return wbuf == 0 || wbuf.ptr().nobj == 0
+}
+
// Internally, the GC work pool is kept in arrays in work buffers.
// The gcWork interface caches a work buffer until full (or empty) to
// avoid contending on the global work buffer lists.
diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go
index 10878ee5cf..04fa050bc5 100644
--- a/src/runtime/mheap.go
+++ b/src/runtime/mheap.go
@@ -28,6 +28,15 @@ type mheap struct {
spans **mspan
spans_mapped uintptr
+ // Proportional sweep
+ pagesSwept uint64 // pages swept this cycle; updated atomically
+ sweepPagesPerByte float64 // proportional sweep ratio; written with lock, read without
+
+ // Malloc stats.
+ largefree uint64 // bytes freed for large objects (>maxsmallsize)
+ nlargefree uint64 // number of frees for large objects (>maxsmallsize)
+ nsmallfree [_NumSizeClasses]uint64 // number of frees for small objects (<=maxsmallsize)
+
// range of addresses we might see in the heap
bitmap uintptr
bitmap_mapped uintptr
@@ -36,14 +45,6 @@ type mheap struct {
arena_end uintptr
arena_reserved bool
- // write barrier shadow heap.
- // 64-bit systems only, enabled by GODEBUG=wbshadow=1.
- // See also shadow_data, data_start, data_end fields on moduledata in
- // symtab.go.
- shadow_enabled bool // shadow should be updated and checked
- shadow_reserved bool // shadow memory is reserved
- shadow_heap uintptr // heap-addr + shadow_heap = shadow heap addr
-
// central free lists for small size classes.
// the padding makes sure that the MCentrals are
// spaced CacheLineSize bytes apart, so that each MCentral.lock
@@ -58,15 +59,6 @@ type mheap struct {
specialfinalizeralloc fixalloc // allocator for specialfinalizer*
specialprofilealloc fixalloc // allocator for specialprofile*
speciallock mutex // lock for sepcial record allocators.
-
- // Proportional sweep
- pagesSwept uint64 // pages swept this cycle; updated atomically
- sweepPagesPerByte float64 // proportional sweep ratio; written with lock, read without
-
- // Malloc stats.
- largefree uint64 // bytes freed for large objects (>maxsmallsize)
- nlargefree uint64 // number of frees for large objects (>maxsmallsize)
- nsmallfree [_NumSizeClasses]uint64 // number of frees for small objects (<=maxsmallsize)
}
var mheap_ mheap
@@ -176,7 +168,9 @@ func recordspan(vh unsafe.Pointer, p unsafe.Pointer) {
// inheap reports whether b is a pointer into a (potentially dead) heap object.
// It returns false for pointers into stack spans.
+// Non-preemptible because it is used by write barriers.
//go:nowritebarrier
+//go:nosplit
func inheap(b uintptr) bool {
if b == 0 || b < mheap_.arena_start || b >= mheap_.arena_used {
return false
diff --git a/src/runtime/mprof.go b/src/runtime/mprof.go
index 4544344780..a618bd5e81 100644
--- a/src/runtime/mprof.go
+++ b/src/runtime/mprof.go
@@ -521,9 +521,7 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) {
n = NumGoroutine()
if n <= len(p) {
gp := getg()
- semacquire(&worldsema, false)
- gp.m.preemptoff = "profile"
- systemstack(stoptheworld)
+ stopTheWorld("profile")
n = NumGoroutine()
if n <= len(p) {
@@ -544,9 +542,7 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) {
}
}
- gp.m.preemptoff = ""
- semrelease(&worldsema)
- systemstack(starttheworld)
+ startTheWorld()
}
return n, ok
@@ -565,10 +561,7 @@ func saveg(pc, sp uintptr, gp *g, r *StackRecord) {
// into buf after the trace for the current goroutine.
func Stack(buf []byte, all bool) int {
if all {
- semacquire(&worldsema, false)
- gp := getg()
- gp.m.preemptoff = "stack trace"
- systemstack(stoptheworld)
+ stopTheWorld("stack trace")
}
n := 0
@@ -590,10 +583,7 @@ func Stack(buf []byte, all bool) int {
}
if all {
- gp := getg()
- gp.m.preemptoff = ""
- semrelease(&worldsema)
- systemstack(starttheworld)
+ startTheWorld()
}
return n
}
diff --git a/src/runtime/mstats.go b/src/runtime/mstats.go
index c8e5249156..3eff7f6b3e 100644
--- a/src/runtime/mstats.go
+++ b/src/runtime/mstats.go
@@ -153,24 +153,13 @@ func init() {
// ReadMemStats populates m with memory allocator statistics.
func ReadMemStats(m *MemStats) {
- // Have to acquire worldsema to stop the world,
- // because stoptheworld can only be used by
- // one goroutine at a time, and there might be
- // a pending garbage collection already calling it.
- semacquire(&worldsema, false)
- gp := getg()
- gp.m.preemptoff = "read mem stats"
- systemstack(stoptheworld)
+ stopTheWorld("read mem stats")
systemstack(func() {
readmemstats_m(m)
})
- gp.m.preemptoff = ""
- gp.m.locks++
- semrelease(&worldsema)
- systemstack(starttheworld)
- gp.m.locks--
+ startTheWorld()
}
func readmemstats_m(stats *MemStats) {
diff --git a/src/runtime/os1_darwin.go b/src/runtime/os1_darwin.go
index 10cf460f7f..1b74e3e653 100644
--- a/src/runtime/os1_darwin.go
+++ b/src/runtime/os1_darwin.go
@@ -8,7 +8,6 @@ import "unsafe"
//extern SigTabTT runtime·sigtab[];
-var sigset_none = uint32(0)
var sigset_all = ^uint32(0)
func unimplemented(name string) {
@@ -126,17 +125,36 @@ func mpreinit(mp *m) {
mp.gsignal.m = mp
}
+func msigsave(mp *m) {
+ smask := (*uint32)(unsafe.Pointer(&mp.sigmask))
+ if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
+ throw("insufficient storage for signal mask")
+ }
+ sigprocmask(_SIG_SETMASK, nil, smask)
+}
+
// Called to initialize a new m (including the bootstrap m).
// Called on the new thread, can not allocate memory.
func minit() {
// Initialize signal handling.
_g_ := getg()
signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
- sigprocmask(_SIG_SETMASK, &sigset_none, nil)
+
+ // restore signal mask from m.sigmask and unblock essential signals
+ nmask := *(*uint32)(unsafe.Pointer(&_g_.m.sigmask))
+ for i := range sigtable {
+ if sigtable[i].flags&_SigUnblock != 0 {
+ nmask &^= 1 << (uint32(i) - 1)
+ }
+ }
+ sigprocmask(_SIG_SETMASK, &nmask, nil)
}
// Called from dropm to undo the effect of an minit.
func unminit() {
+ _g_ := getg()
+ smask := (*uint32)(unsafe.Pointer(&_g_.m.sigmask))
+ sigprocmask(_SIG_SETMASK, smask, nil)
signalstack(nil, 0)
}
@@ -447,6 +465,6 @@ func signalstack(p *byte, n int32) {
sigaltstack(&st, nil)
}
-func unblocksignals() {
- sigprocmask(_SIG_SETMASK, &sigset_none, nil)
+func updatesigmask(m sigmask) {
+ sigprocmask(_SIG_SETMASK, &m[0], nil)
}
diff --git a/src/runtime/os1_dragonfly.go b/src/runtime/os1_dragonfly.go
index a590aea39b..eb42b54e2b 100644
--- a/src/runtime/os1_dragonfly.go
+++ b/src/runtime/os1_dragonfly.go
@@ -12,7 +12,6 @@ const (
_HW_NCPU = 3
)
-var sigset_none = sigset{}
var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
func getncpu() int32 {
@@ -120,6 +119,14 @@ func mpreinit(mp *m) {
mp.gsignal.m = mp
}
+func msigsave(mp *m) {
+ smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
+ if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
+ throw("insufficient storage for signal mask")
+ }
+ sigprocmask(nil, smask)
+}
+
// Called to initialize a new m (including the bootstrap m).
// Called on the new thread, can not allocate memory.
func minit() {
@@ -130,11 +137,22 @@ func minit() {
// Initialize signal handling
signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
- sigprocmask(&sigset_none, nil)
+
+ // restore signal mask from m.sigmask and unblock essential signals
+ nmask := *(*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+ for i := range sigtable {
+ if sigtable[i].flags&_SigUnblock != 0 {
+ nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
+ }
+ }
+ sigprocmask(&nmask, nil)
}
// Called from dropm to undo the effect of an minit.
func unminit() {
+ _g_ := getg()
+ smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+ sigprocmask(smask, nil)
signalstack(nil, 0)
}
@@ -215,6 +233,8 @@ func signalstack(p *byte, n int32) {
sigaltstack(&st, nil)
}
-func unblocksignals() {
- sigprocmask(&sigset_none, nil)
+func updatesigmask(m sigmask) {
+ var mask sigset
+ copy(mask.__bits[:], m[:])
+ sigprocmask(&mask, nil)
}
diff --git a/src/runtime/os1_freebsd.go b/src/runtime/os1_freebsd.go
index 8719a49286..f7f34bd386 100644
--- a/src/runtime/os1_freebsd.go
+++ b/src/runtime/os1_freebsd.go
@@ -12,7 +12,6 @@ const (
_HW_NCPU = 3
)
-var sigset_none = sigset{}
var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
func getncpu() int32 {
@@ -119,6 +118,14 @@ func mpreinit(mp *m) {
mp.gsignal.m = mp
}
+func msigsave(mp *m) {
+ smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
+ if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
+ throw("insufficient storage for signal mask")
+ }
+ sigprocmask(nil, smask)
+}
+
// Called to initialize a new m (including the bootstrap m).
// Called on the new thread, can not allocate memory.
func minit() {
@@ -132,11 +139,22 @@ func minit() {
// Initialize signal handling.
signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
- sigprocmask(&sigset_none, nil)
+
+ // restore signal mask from m.sigmask and unblock essential signals
+ nmask := *(*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+ for i := range sigtable {
+ if sigtable[i].flags&_SigUnblock != 0 {
+ nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
+ }
+ }
+ sigprocmask(&nmask, nil)
}
// Called from dropm to undo the effect of an minit.
func unminit() {
+ _g_ := getg()
+ smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+ sigprocmask(smask, nil)
signalstack(nil, 0)
}
@@ -217,6 +235,8 @@ func signalstack(p *byte, n int32) {
sigaltstack(&st, nil)
}
-func unblocksignals() {
- sigprocmask(&sigset_none, nil)
+func updatesigmask(m [(_NSIG + 31) / 32]uint32) {
+ var mask sigset
+ copy(mask.__bits[:], m[:])
+ sigprocmask(&mask, nil)
}
diff --git a/src/runtime/os1_linux.go b/src/runtime/os1_linux.go
index e4b18c79b3..02f98d7c5f 100644
--- a/src/runtime/os1_linux.go
+++ b/src/runtime/os1_linux.go
@@ -6,7 +6,6 @@ package runtime
import "unsafe"
-var sigset_none sigset
var sigset_all sigset = sigset{^uint32(0), ^uint32(0)}
// Linux futex.
@@ -190,17 +189,36 @@ func mpreinit(mp *m) {
mp.gsignal.m = mp
}
+func msigsave(mp *m) {
+ smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
+ if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
+ throw("insufficient storage for signal mask")
+ }
+ rtsigprocmask(_SIG_SETMASK, nil, smask, int32(unsafe.Sizeof(*smask)))
+}
+
// Called to initialize a new m (including the bootstrap m).
// Called on the new thread, can not allocate memory.
func minit() {
// Initialize signal handling.
_g_ := getg()
signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
- rtsigprocmask(_SIG_SETMASK, &sigset_none, nil, int32(unsafe.Sizeof(sigset_none)))
+
+ // restore signal mask from m.sigmask and unblock essential signals
+ nmask := *(*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+ for i := range sigtable {
+ if sigtable[i].flags&_SigUnblock != 0 {
+ nmask[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
+ }
+ }
+ rtsigprocmask(_SIG_SETMASK, &nmask, nil, int32(unsafe.Sizeof(nmask)))
}
// Called from dropm to undo the effect of an minit.
func unminit() {
+ _g_ := getg()
+ smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+ rtsigprocmask(_SIG_SETMASK, smask, nil, int32(unsafe.Sizeof(*smask)))
signalstack(nil, 0)
}
@@ -304,6 +322,8 @@ func signalstack(p *byte, n int32) {
sigaltstack(&st, nil)
}
-func unblocksignals() {
- rtsigprocmask(_SIG_SETMASK, &sigset_none, nil, int32(unsafe.Sizeof(sigset_none)))
+func updatesigmask(m sigmask) {
+ var mask sigset
+ copy(mask[:], m[:])
+ rtsigprocmask(_SIG_SETMASK, &mask, nil, int32(unsafe.Sizeof(mask)))
}
diff --git a/src/runtime/os1_nacl.go b/src/runtime/os1_nacl.go
index dbb5dec2fd..66e60f8b12 100644
--- a/src/runtime/os1_nacl.go
+++ b/src/runtime/os1_nacl.go
@@ -15,6 +15,9 @@ func mpreinit(mp *m) {
func sigtramp()
+func msigsave(mp *m) {
+}
+
// Called to initialize a new m (including the bootstrap m).
// Called on the new thread, can not allocate memory.
func minit() {
diff --git a/src/runtime/os1_netbsd.go b/src/runtime/os1_netbsd.go
index 8df74b5593..3fb05989e7 100644
--- a/src/runtime/os1_netbsd.go
+++ b/src/runtime/os1_netbsd.go
@@ -17,7 +17,6 @@ const (
_CLOCK_MONOTONIC = 3
)
-var sigset_none = sigset{}
var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
// From NetBSD's
@@ -139,6 +138,14 @@ func mpreinit(mp *m) {
mp.gsignal.m = mp
}
+func msigsave(mp *m) {
+ smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
+ if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
+ throw("insufficient storage for signal mask")
+ }
+ sigprocmask(_SIG_SETMASK, nil, smask)
+}
+
// Called to initialize a new m (including the bootstrap m).
// Called on the new thread, can not allocate memory.
func minit() {
@@ -147,11 +154,23 @@ func minit() {
// Initialize signal handling
signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
- sigprocmask(_SIG_SETMASK, &sigset_none, nil)
+
+ // restore signal mask from m.sigmask and unblock essential signals
+ nmask := *(*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+ for i := range sigtable {
+ if sigtable[i].flags&_SigUnblock != 0 {
+ nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
+ }
+ }
+ sigprocmask(_SIG_SETMASK, &nmask, nil)
}
// Called from dropm to undo the effect of an minit.
func unminit() {
+ _g_ := getg()
+ smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+ sigprocmask(_SIG_SETMASK, smask, nil)
+
signalstack(nil, 0)
}
@@ -206,6 +225,8 @@ func signalstack(p *byte, n int32) {
sigaltstack(&st, nil)
}
-func unblocksignals() {
- sigprocmask(_SIG_SETMASK, &sigset_none, nil)
+func updatesigmask(m sigmask) {
+ var mask sigset
+ copy(mask.__bits[:], m[:])
+ sigprocmask(_SIG_SETMASK, &mask, nil)
}
diff --git a/src/runtime/os1_openbsd.go b/src/runtime/os1_openbsd.go
index 95729a56df..5ccf642468 100644
--- a/src/runtime/os1_openbsd.go
+++ b/src/runtime/os1_openbsd.go
@@ -148,6 +148,14 @@ func mpreinit(mp *m) {
mp.gsignal.m = mp
}
+func msigsave(mp *m) {
+ smask := (*uint32)(unsafe.Pointer(&mp.sigmask))
+ if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
+ throw("insufficient storage for signal mask")
+ }
+ *smask = sigprocmask(_SIG_BLOCK, 0)
+}
+
// Called to initialize a new m (including the bootstrap m).
// Called on the new thread, can not allocate memory.
func minit() {
@@ -158,11 +166,22 @@ func minit() {
// Initialize signal handling
signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
- sigprocmask(_SIG_SETMASK, sigset_none)
+
+ // restore signal mask from m.sigmask and unblock essential signals
+ nmask := *(*uint32)(unsafe.Pointer(&_g_.m.sigmask))
+ for i := range sigtable {
+ if sigtable[i].flags&_SigUnblock != 0 {
+ nmask &^= 1 << (uint32(i) - 1)
+ }
+ }
+ sigprocmask(_SIG_SETMASK, nmask)
}
// Called from dropm to undo the effect of an minit.
func unminit() {
+ _g_ := getg()
+ smask := *(*uint32)(unsafe.Pointer(&_g_.m.sigmask))
+ sigprocmask(_SIG_SETMASK, smask)
signalstack(nil, 0)
}
@@ -217,6 +236,6 @@ func signalstack(p *byte, n int32) {
sigaltstack(&st, nil)
}
-func unblocksignals() {
- sigprocmask(_SIG_SETMASK, sigset_none)
+func updatesigmask(m sigmask) {
+ sigprocmask(_SIG_SETMASK, m[0])
}
diff --git a/src/runtime/os1_plan9.go b/src/runtime/os1_plan9.go
index c026218241..bda7057f44 100644
--- a/src/runtime/os1_plan9.go
+++ b/src/runtime/os1_plan9.go
@@ -18,6 +18,9 @@ func mpreinit(mp *m) {
mp.errstr = (*byte)(mallocgc(_ERRMAX, nil, _FlagNoScan))
}
+func msigsave(mp *m) {
+}
+
// Called to initialize a new m (including the bootstrap m).
// Called on the new thread, can not allocate memory.
func minit() {
@@ -177,7 +180,7 @@ func exit(e int) {
} else {
// build error string
var tmp [32]byte
- status = []byte(gostringnocopy(&itoa(tmp[:len(tmp)-1], uint64(e))[0]))
+ status = append(itoa(tmp[:len(tmp)-1], uint64(e)), 0)
}
goexitsall(&status[0])
exits(&status[0])
diff --git a/src/runtime/os1_windows.go b/src/runtime/os1_windows.go
index 5719b320f5..bc472d0de9 100644
--- a/src/runtime/os1_windows.go
+++ b/src/runtime/os1_windows.go
@@ -292,6 +292,9 @@ func newosproc(mp *m, stk unsafe.Pointer) {
func mpreinit(mp *m) {
}
+func msigsave(mp *m) {
+}
+
// Called to initialize a new m (including the bootstrap m).
// Called on the new thread, can not allocate memory.
func minit() {
diff --git a/src/runtime/os3_solaris.go b/src/runtime/os3_solaris.go
index 69ac5b4970..e4fe92de41 100644
--- a/src/runtime/os3_solaris.go
+++ b/src/runtime/os3_solaris.go
@@ -114,7 +114,6 @@ var (
libc_write libcFunc
)
-var sigset_none = sigset{}
var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
func getncpu() int32 {
@@ -190,6 +189,14 @@ func mpreinit(mp *m) {
func miniterrno()
+func msigsave(mp *m) {
+ smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
+ if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
+ throw("insufficient storage for signal mask")
+ }
+ sigprocmask(_SIG_SETMASK, nil, smask)
+}
+
// Called to initialize a new m (including the bootstrap m).
// Called on the new thread, can not allocate memory.
func minit() {
@@ -197,11 +204,23 @@ func minit() {
asmcgocall(unsafe.Pointer(funcPC(miniterrno)), unsafe.Pointer(&libc____errno))
// Initialize signal handling
signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
- sigprocmask(_SIG_SETMASK, &sigset_none, nil)
+
+ // restore signal mask from m.sigmask and unblock essential signals
+ nmask := *(*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+ for i := range sigtable {
+ if sigtable[i].flags&_SigUnblock != 0 {
+ nmask.__sigbits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
+ }
+ }
+ sigprocmask(_SIG_SETMASK, &nmask, nil)
}
// Called from dropm to undo the effect of an minit.
func unminit() {
+ _g_ := getg()
+ smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+ sigprocmask(_SIG_SETMASK, smask, nil)
+
signalstack(nil, 0)
}
@@ -278,8 +297,10 @@ func signalstack(p *byte, n int32) {
sigaltstack(&st, nil)
}
-func unblocksignals() {
- sigprocmask(_SIG_SETMASK, &sigset_none, nil)
+func updatesigmask(m sigmask) {
+ var mask sigset
+ copy(mask.__sigbits[:], m[:])
+ sigprocmask(_SIG_SETMASK, &mask, nil)
}
//go:nosplit
diff --git a/src/runtime/panic.go b/src/runtime/panic.go
index 0e4086c7ef..47563f450e 100644
--- a/src/runtime/panic.go
+++ b/src/runtime/panic.go
@@ -188,16 +188,6 @@ func newdefer(siz int32) *_defer {
d = (*_defer)(mallocgc(total, deferType, 0))
}
d.siz = siz
- if mheap_.shadow_enabled {
- // This memory will be written directly, with no write barrier,
- // and then scanned like stacks during collection.
- // Unlike real stacks, it is from heap spans, so mark the
- // shadow as explicitly unusable.
- p := deferArgs(d)
- for i := uintptr(0); i+ptrSize <= uintptr(siz); i += ptrSize {
- writebarrierptr_noshadow((*uintptr)(add(p, i)))
- }
- }
gp := mp.curg
d.link = gp._defer
gp._defer = d
@@ -214,12 +204,6 @@ func freedefer(d *_defer) {
if d.fn != nil {
freedeferfn()
}
- if mheap_.shadow_enabled {
- // Undo the marking in newdefer.
- systemstack(func() {
- clearshadow(uintptr(deferArgs(d)), uintptr(d.siz))
- })
- }
sc := deferclass(uintptr(d.siz))
if sc < uintptr(len(p{}.deferpool)) {
mp := acquirem()
diff --git a/src/runtime/pprof/pprof.go b/src/runtime/pprof/pprof.go
index b3d0ae9b64..4290edb7be 100644
--- a/src/runtime/pprof/pprof.go
+++ b/src/runtime/pprof/pprof.go
@@ -442,35 +442,33 @@ func writeHeap(w io.Writer, debug int) error {
// Print memstats information too.
// Pprof will ignore, but useful for people
- if debug > 0 {
- s := new(runtime.MemStats)
- runtime.ReadMemStats(s)
- fmt.Fprintf(w, "\n# runtime.MemStats\n")
- fmt.Fprintf(w, "# Alloc = %d\n", s.Alloc)
- fmt.Fprintf(w, "# TotalAlloc = %d\n", s.TotalAlloc)
- fmt.Fprintf(w, "# Sys = %d\n", s.Sys)
- fmt.Fprintf(w, "# Lookups = %d\n", s.Lookups)
- fmt.Fprintf(w, "# Mallocs = %d\n", s.Mallocs)
- fmt.Fprintf(w, "# Frees = %d\n", s.Frees)
+ s := new(runtime.MemStats)
+ runtime.ReadMemStats(s)
+ fmt.Fprintf(w, "\n# runtime.MemStats\n")
+ fmt.Fprintf(w, "# Alloc = %d\n", s.Alloc)
+ fmt.Fprintf(w, "# TotalAlloc = %d\n", s.TotalAlloc)
+ fmt.Fprintf(w, "# Sys = %d\n", s.Sys)
+ fmt.Fprintf(w, "# Lookups = %d\n", s.Lookups)
+ fmt.Fprintf(w, "# Mallocs = %d\n", s.Mallocs)
+ fmt.Fprintf(w, "# Frees = %d\n", s.Frees)
- fmt.Fprintf(w, "# HeapAlloc = %d\n", s.HeapAlloc)
- fmt.Fprintf(w, "# HeapSys = %d\n", s.HeapSys)
- fmt.Fprintf(w, "# HeapIdle = %d\n", s.HeapIdle)
- fmt.Fprintf(w, "# HeapInuse = %d\n", s.HeapInuse)
- fmt.Fprintf(w, "# HeapReleased = %d\n", s.HeapReleased)
- fmt.Fprintf(w, "# HeapObjects = %d\n", s.HeapObjects)
+ fmt.Fprintf(w, "# HeapAlloc = %d\n", s.HeapAlloc)
+ fmt.Fprintf(w, "# HeapSys = %d\n", s.HeapSys)
+ fmt.Fprintf(w, "# HeapIdle = %d\n", s.HeapIdle)
+ fmt.Fprintf(w, "# HeapInuse = %d\n", s.HeapInuse)
+ fmt.Fprintf(w, "# HeapReleased = %d\n", s.HeapReleased)
+ fmt.Fprintf(w, "# HeapObjects = %d\n", s.HeapObjects)
- fmt.Fprintf(w, "# Stack = %d / %d\n", s.StackInuse, s.StackSys)
- fmt.Fprintf(w, "# MSpan = %d / %d\n", s.MSpanInuse, s.MSpanSys)
- fmt.Fprintf(w, "# MCache = %d / %d\n", s.MCacheInuse, s.MCacheSys)
- fmt.Fprintf(w, "# BuckHashSys = %d\n", s.BuckHashSys)
+ fmt.Fprintf(w, "# Stack = %d / %d\n", s.StackInuse, s.StackSys)
+ fmt.Fprintf(w, "# MSpan = %d / %d\n", s.MSpanInuse, s.MSpanSys)
+ fmt.Fprintf(w, "# MCache = %d / %d\n", s.MCacheInuse, s.MCacheSys)
+ fmt.Fprintf(w, "# BuckHashSys = %d\n", s.BuckHashSys)
- fmt.Fprintf(w, "# NextGC = %d\n", s.NextGC)
- fmt.Fprintf(w, "# PauseNs = %d\n", s.PauseNs)
- fmt.Fprintf(w, "# NumGC = %d\n", s.NumGC)
- fmt.Fprintf(w, "# EnableGC = %v\n", s.EnableGC)
- fmt.Fprintf(w, "# DebugGC = %v\n", s.DebugGC)
- }
+ fmt.Fprintf(w, "# NextGC = %d\n", s.NextGC)
+ fmt.Fprintf(w, "# PauseNs = %d\n", s.PauseNs)
+ fmt.Fprintf(w, "# NumGC = %d\n", s.NumGC)
+ fmt.Fprintf(w, "# EnableGC = %v\n", s.EnableGC)
+ fmt.Fprintf(w, "# DebugGC = %v\n", s.DebugGC)
if tw != nil {
tw.Flush()
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index f725fc890b..805b96e627 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -203,7 +203,7 @@ func acquireSudog() *sudog {
// acquireSudog, acquireSudog calls new(sudog),
// new calls malloc, malloc can call the garbage collector,
// and the garbage collector calls the semaphore implementation
- // in stoptheworld.
+ // in stopTheWorld.
// Break the cycle by doing acquirem/releasem around new(sudog).
// The acquirem/releasem increments m.locks during new(sudog),
// which keeps the garbage collector from being invoked.
diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go
index 00535da77d..c070f7d773 100644
--- a/src/runtime/proc1.go
+++ b/src/runtime/proc1.go
@@ -59,7 +59,6 @@ func schedinit() {
goargs()
goenvs()
parsedebugvars()
- wbshadowinit()
gcinit()
sched.lastpoll = uint64(nanotime())
@@ -212,7 +211,7 @@ func helpgc(nproc int32) {
// sched.stopwait to in order to request that all Gs permanently stop.
const freezeStopWait = 0x7fffffff
-// Similar to stoptheworld but best-effort and can be called several times.
+// Similar to stopTheWorld but best-effort and can be called several times.
// There is no reverse operation, used during crashing.
// This function must not lock any mutexes.
func freezetheworld() {
@@ -466,94 +465,68 @@ func stopscanstart(gp *g) {
}
}
-// Runs on g0 and does the actual work after putting the g back on the run queue.
-func mquiesce(gpmaster *g) {
- // enqueue the calling goroutine.
- restartg(gpmaster)
-
- activeglen := len(allgs)
- for i := 0; i < activeglen; i++ {
- gp := allgs[i]
- if readgstatus(gp) == _Gdead {
- gp.gcworkdone = true // noop scan.
- } else {
- gp.gcworkdone = false
- }
- stopscanstart(gp)
- }
-
- // Check that the G's gcwork (such as scanning) has been done. If not do it now.
- // You can end up doing work here if the page trap on a Grunning Goroutine has
- // not been sprung or in some race situations. For example a runnable goes dead
- // and is started up again with a gp->gcworkdone set to false.
- for i := 0; i < activeglen; i++ {
- gp := allgs[i]
- for !gp.gcworkdone {
- status := readgstatus(gp)
- if status == _Gdead {
- //do nothing, scan not needed.
- gp.gcworkdone = true // scan is a noop
- break
- }
- if status == _Grunning && gp.stackguard0 == uintptr(stackPreempt) && notetsleep(&sched.stopnote, 100*1000) { // nanosecond arg
- noteclear(&sched.stopnote)
- } else {
- stopscanstart(gp)
- }
- }
- }
-
- for i := 0; i < activeglen; i++ {
- gp := allgs[i]
- status := readgstatus(gp)
- if isscanstatus(status) {
- print("mstopandscang:bottom: post scan bad status gp=", gp, " has status ", hex(status), "\n")
- dumpgstatus(gp)
- }
- if !gp.gcworkdone && status != _Gdead {
- print("mstopandscang:bottom: post scan gp=", gp, "->gcworkdone still false\n")
- dumpgstatus(gp)
- }
- }
-
- schedule() // Never returns.
+// stopTheWorld stops all P's from executing goroutines, interrupting
+// all goroutines at GC safe points and records reason as the reason
+// for the stop. On return, only the current goroutine's P is running.
+// stopTheWorld must not be called from a system stack and the caller
+// must not hold worldsema. The caller must call startTheWorld when
+// other P's should resume execution.
+//
+// stopTheWorld is safe for multiple goroutines to call at the
+// same time. Each will execute its own stop, and the stops will
+// be serialized.
+//
+// This is also used by routines that do stack dumps. If the system is
+// in panic or being exited, this may not reliably stop all
+// goroutines.
+func stopTheWorld(reason string) {
+ semacquire(&worldsema, false)
+ getg().m.preemptoff = reason
+ systemstack(stopTheWorldWithSema)
}
-// quiesce moves all the goroutines to a GC safepoint which for now is a at preemption point.
-// If the global gcphase is GCmark quiesce will ensure that all of the goroutine's stacks
-// have been scanned before it returns.
-func quiesce(mastergp *g) {
- castogscanstatus(mastergp, _Grunning, _Gscanenqueue)
- // Now move this to the g0 (aka m) stack.
- // g0 will potentially scan this thread and put mastergp on the runqueue
- mcall(mquiesce)
+// startTheWorld undoes the effects of stopTheWorld.
+func startTheWorld() {
+ systemstack(startTheWorldWithSema)
+ // worldsema must be held over startTheWorldWithSema to ensure
+ // gomaxprocs cannot change while worldsema is held.
+ semrelease(&worldsema)
+ getg().m.preemptoff = ""
}
-// Holding worldsema grants an M the right to try to stop the world.
-// The procedure is:
-//
-// semacquire(&worldsema);
-// m.preemptoff = "reason";
-// stoptheworld();
-//
-// ... do stuff ...
-//
-// m.preemptoff = "";
-// semrelease(&worldsema);
-// starttheworld();
-//
+// Holding worldsema grants an M the right to try to stop the world
+// and prevents gomaxprocs from changing concurrently.
var worldsema uint32 = 1
-// This is used by the GC as well as the routines that do stack dumps. In the case
-// of GC all the routines can be reliably stopped. This is not always the case
-// when the system is in panic or being exited.
-func stoptheworld() {
+// stopTheWorldWithSema is the core implementation of stopTheWorld.
+// The caller is responsible for acquiring worldsema and disabling
+// preemption first and then should stopTheWorldWithSema on the system
+// stack:
+//
+// semacquire(&worldsema, false)
+// m.preemptoff = "reason"
+// systemstack(stopTheWorldWithSema)
+//
+// When finished, the caller must either call startTheWorld or undo
+// these three operations separately:
+//
+// m.preemptoff = ""
+// systemstack(startTheWorldWithSema)
+// semrelease(&worldsema)
+//
+// It is allowed to acquire worldsema once and then execute multiple
+// startTheWorldWithSema/stopTheWorldWithSema pairs.
+// Other P's are able to execute between successive calls to
+// startTheWorldWithSema and stopTheWorldWithSema.
+// Holding worldsema causes any other goroutines invoking
+// stopTheWorld to block.
+func stopTheWorldWithSema() {
_g_ := getg()
// If we hold a lock, then we won't be able to stop another M
// that is blocked trying to acquire the lock.
if _g_.m.locks > 0 {
- throw("stoptheworld: holding locks")
+ throw("stopTheWorld: holding locks")
}
lock(&sched.lock)
@@ -600,12 +573,12 @@ func stoptheworld() {
}
}
if sched.stopwait != 0 {
- throw("stoptheworld: not stopped")
+ throw("stopTheWorld: not stopped")
}
for i := 0; i < int(gomaxprocs); i++ {
p := allp[i]
if p.status != _Pgcstop {
- throw("stoptheworld: not stopped")
+ throw("stopTheWorld: not stopped")
}
}
}
@@ -615,7 +588,7 @@ func mhelpgc() {
_g_.m.helpgc = -1
}
-func starttheworld() {
+func startTheWorldWithSema() {
_g_ := getg()
_g_.m.locks++ // disable preemption because it can be holding p in a local var
@@ -644,7 +617,7 @@ func starttheworld() {
mp := p.m.ptr()
p.m = 0
if mp.nextp != 0 {
- throw("starttheworld: inconsistent mp->nextp")
+ throw("startTheWorld: inconsistent mp->nextp")
}
mp.nextp.set(p)
notewakeup(&mp.park)
@@ -754,10 +727,10 @@ func forEachP(fn func(*p)) {
_p_ := getg().m.p.ptr()
lock(&sched.lock)
- if sched.stopwait != 0 {
- throw("forEachP: sched.stopwait != 0")
+ if sched.safePointWait != 0 {
+ throw("forEachP: sched.safePointWait != 0")
}
- sched.stopwait = gomaxprocs - 1
+ sched.safePointWait = gomaxprocs - 1
sched.safePointFn = fn
// Ask all Ps to run the safe point function.
@@ -777,11 +750,11 @@ func forEachP(fn func(*p)) {
for p := sched.pidle.ptr(); p != nil; p = p.link.ptr() {
if cas(&p.runSafePointFn, 1, 0) {
fn(p)
- sched.stopwait--
+ sched.safePointWait--
}
}
- wait := sched.stopwait > 0
+ wait := sched.safePointWait > 0
unlock(&sched.lock)
// Run fn for the current P.
@@ -807,15 +780,15 @@ func forEachP(fn func(*p)) {
for {
// Wait for 100us, then try to re-preempt in
// case of any races.
- if notetsleep(&sched.stopnote, 100*1000) {
- noteclear(&sched.stopnote)
+ if notetsleep(&sched.safePointNote, 100*1000) {
+ noteclear(&sched.safePointNote)
break
}
preemptall()
}
}
- if sched.stopwait != 0 {
- throw("forEachP: not stopped")
+ if sched.safePointWait != 0 {
+ throw("forEachP: not done")
}
for i := 0; i < int(gomaxprocs); i++ {
p := allp[i]
@@ -851,9 +824,9 @@ func runSafePointFn() {
}
sched.safePointFn(p)
lock(&sched.lock)
- sched.stopwait--
- if sched.stopwait == 0 {
- notewakeup(&sched.stopnote)
+ sched.safePointWait--
+ if sched.safePointWait == 0 {
+ notewakeup(&sched.safePointNote)
}
unlock(&sched.lock)
}
@@ -971,6 +944,7 @@ func needm(x byte) {
_g_.stack.lo = uintptr(noescape(unsafe.Pointer(&x))) - 32*1024
_g_.stackguard0 = _g_.stack.lo + _StackGuard
+ msigsave(mp)
// Initialize this thread to use the m.
asminit()
minit()
@@ -1098,6 +1072,7 @@ func unlockextra(mp *m) {
func newm(fn func(), _p_ *p) {
mp := allocm(_p_, fn)
mp.nextp.set(_p_)
+ msigsave(mp)
if iscgo {
var ts cgothreadstart
if _cgo_thread_start == nil {
@@ -1226,9 +1201,9 @@ func handoffp(_p_ *p) {
}
if _p_.runSafePointFn != 0 && cas(&_p_.runSafePointFn, 1, 0) {
sched.safePointFn(_p_)
- sched.stopwait--
- if sched.stopwait == 0 {
- notewakeup(&sched.stopnote)
+ sched.safePointWait--
+ if sched.safePointWait == 0 {
+ notewakeup(&sched.safePointNote)
}
}
if sched.runqsize != 0 {
@@ -1305,7 +1280,7 @@ func startlockedm(gp *g) {
stopm()
}
-// Stops the current m for stoptheworld.
+// Stops the current m for stopTheWorld.
// Returns when the world is restarted.
func gcstopm() {
_g_ := getg()
@@ -1421,7 +1396,7 @@ top:
xadd(&sched.nmspinning, 1)
}
// random steal from other P's
- for i := 0; i < int(2*gomaxprocs); i++ {
+ for i := 0; i < int(4*gomaxprocs); i++ {
if sched.gcwaiting != 0 {
goto top
}
@@ -1430,18 +1405,20 @@ top:
if _p_ == _g_.m.p.ptr() {
gp, _ = runqget(_p_)
} else {
- gp = runqsteal(_g_.m.p.ptr(), _p_)
+ stealRunNextG := i > 2*int(gomaxprocs) // first look for ready queues with more than 1 g
+ gp = runqsteal(_g_.m.p.ptr(), _p_, stealRunNextG)
}
if gp != nil {
return gp, false
}
}
+
stop:
- // We have nothing to do. If we're in the GC mark phaseand can
+ // We have nothing to do. If we're in the GC mark phase and can
// safely scan and blacken objects, run idle-time marking
// rather than give up the P.
- if _p_ := _g_.m.p.ptr(); gcBlackenEnabled != 0 && _p_.gcBgMarkWorker != nil {
+ if _p_ := _g_.m.p.ptr(); gcBlackenEnabled != 0 && _p_.gcBgMarkWorker != nil && gcMarkWorkAvailable(_p_) {
_p_.gcMarkWorkerMode = gcMarkWorkerIdleMode
gp := _p_.gcBgMarkWorker
casgstatus(gp, _Gwaiting, _Grunnable)
@@ -2484,11 +2461,9 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
mp.mallocing++
// Define that a "user g" is a user-created goroutine, and a "system g"
- // is one that is m->g0 or m->gsignal. We've only made sure that we
- // can unwind user g's, so exclude the system g's.
+ // is one that is m->g0 or m->gsignal.
//
- // It is not quite as easy as testing gp == m->curg (the current user g)
- // because we might be interrupted for profiling halfway through a
+ // We might be interrupted for profiling halfway through a
// goroutine switch. The switch involves updating three (or four) values:
// g, PC, SP, and (on arm) LR. The PC must be the last to be updated,
// because once it gets updated the new g is running.
@@ -2497,8 +2472,7 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
// so the update only affects g, SP, and PC. Since PC must be last, there
// the possible partial transitions in ordinary execution are (1) g alone is updated,
// (2) both g and SP are updated, and (3) SP alone is updated.
- // If g is updated, we'll see a system g and not look closer.
- // If SP alone is updated, we can detect the partial transition by checking
+ // If SP or g alone is updated, we can detect the partial transition by checking
// whether the SP is within g's stack bounds. (We could also require that SP
// be changed only after g, but the stack bounds check is needed by other
// cases, so there is no need to impose an additional requirement.)
@@ -2527,15 +2501,11 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
// disabled, so a profiling signal cannot arrive then anyway.
//
// Third, the common case: it may be that the switch updates g, SP, and PC
- // separately, as in gogo.
- //
- // Because gogo is the only instance, we check whether the PC lies
- // within that function, and if so, not ask for a traceback. This approach
- // requires knowing the size of the gogo function, which we
- // record in arch_*.h and check in runtime_test.go.
+ // separately. If the PC is within any of the functions that does this,
+ // we don't ask for a traceback. C.F. the function setsSP for more about this.
//
// There is another apparently viable approach, recorded here in case
- // the "PC within gogo" check turns out not to be usable.
+ // the "PC within setsSP function" check turns out not to be usable.
// It would be possible to delay the update of either g or SP until immediately
// before the PC update instruction. Then, because of the stack bounds check,
// the only problematic interrupt point is just before that PC update instruction,
@@ -2556,28 +2526,23 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
// transition. We simply require that g and SP match and that the PC is not
// in gogo.
traceback := true
- gogo := funcPC(gogo)
- if gp == nil || gp != mp.curg ||
- sp < gp.stack.lo || gp.stack.hi < sp ||
- (gogo <= pc && pc < gogo+_RuntimeGogoBytes) {
+ if gp == nil || sp < gp.stack.lo || gp.stack.hi < sp || setsSP(pc) {
traceback = false
}
-
var stk [maxCPUProfStack]uintptr
n := 0
- if traceback {
- n = gentraceback(pc, sp, lr, gp, 0, &stk[0], len(stk), nil, nil, _TraceTrap)
+ if mp.ncgo > 0 && mp.curg != nil && mp.curg.syscallpc != 0 && mp.curg.syscallsp != 0 {
+ // Cgo, we can't unwind and symbolize arbitrary C code,
+ // so instead collect Go stack that leads to the cgo call.
+ // This is especially important on windows, since all syscalls are cgo calls.
+ n = gentraceback(mp.curg.syscallpc, mp.curg.syscallsp, 0, mp.curg, 0, &stk[0], len(stk), nil, nil, 0)
+ } else if traceback {
+ n = gentraceback(pc, sp, lr, gp, 0, &stk[0], len(stk), nil, nil, _TraceTrap|_TraceJumpStack)
}
if !traceback || n <= 0 {
// Normal traceback is impossible or has failed.
// See if it falls into several common cases.
n = 0
- if mp.ncgo > 0 && mp.curg != nil && mp.curg.syscallpc != 0 && mp.curg.syscallsp != 0 {
- // Cgo, we can't unwind and symbolize arbitrary C code,
- // so instead collect Go stack that leads to the cgo call.
- // This is especially important on windows, since all syscalls are cgo calls.
- n = gentraceback(mp.curg.syscallpc, mp.curg.syscallsp, 0, mp.curg, 0, &stk[0], len(stk), nil, nil, 0)
- }
if GOOS == "windows" && n == 0 && mp.libcallg != 0 && mp.libcallpc != 0 && mp.libcallsp != 0 {
// Libcall, i.e. runtime syscall on windows.
// Collect Go stack that leads to the call.
@@ -2612,6 +2577,30 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
mp.mallocing--
}
+// Reports whether a function will set the SP
+// to an absolute value. Important that
+// we don't traceback when these are at the bottom
+// of the stack since we can't be sure that we will
+// find the caller.
+//
+// If the function is not on the bottom of the stack
+// we assume that it will have set it up so that traceback will be consistent,
+// either by being a traceback terminating function
+// or putting one on the stack at the right offset.
+func setsSP(pc uintptr) bool {
+ f := findfunc(pc)
+ if f == nil {
+ // couldn't find the function for this PC,
+ // so assume the worst and stop traceback
+ return true
+ }
+ switch f.entry {
+ case gogoPC, systemstackPC, mcallPC, morestackPC:
+ return true
+ }
+ return false
+}
+
// Arrange to call fn with a traceback hz times a second.
func setcpuprofilerate_m(hz int32) {
// Force sane arguments.
@@ -3447,23 +3436,34 @@ func runqget(_p_ *p) (gp *g, inheritTime bool) {
}
}
-// Grabs a batch of goroutines from local runnable queue.
-// batch array must be of size len(p->runq)/2. Returns number of grabbed goroutines.
+// Grabs a batch of goroutines from _p_'s runnable queue into batch.
+// Batch is a ring buffer starting at batchHead.
+// Returns number of grabbed goroutines.
// Can be executed by any P.
-func runqgrab(_p_ *p, batch []*g) uint32 {
+func runqgrab(_p_ *p, batch *[256]*g, batchHead uint32, stealRunNextG bool) uint32 {
for {
h := atomicload(&_p_.runqhead) // load-acquire, synchronize with other consumers
t := atomicload(&_p_.runqtail) // load-acquire, synchronize with the producer
n := t - h
n = n - n/2
if n == 0 {
- // Try to steal from _p_.runnext.
- if next := _p_.runnext; next != 0 {
- if !_p_.runnext.cas(next, 0) {
- continue
+ if stealRunNextG {
+ // Try to steal from _p_.runnext.
+ if next := _p_.runnext; next != 0 {
+ // Sleep to ensure that _p_ isn't about to run the g we
+ // are about to steal.
+ // The important use case here is when the g running on _p_
+ // ready()s another g and then almost immediately blocks.
+ // Instead of stealing runnext in this window, back off
+ // to give _p_ a chance to schedule runnext. This will avoid
+ // thrashing gs between different Ps.
+ usleep(100)
+ if !_p_.runnext.cas(next, 0) {
+ continue
+ }
+ batch[batchHead%uint32(len(batch))] = next.ptr()
+ return 1
}
- batch[0] = next.ptr()
- return 1
}
return 0
}
@@ -3471,7 +3471,8 @@ func runqgrab(_p_ *p, batch []*g) uint32 {
continue
}
for i := uint32(0); i < n; i++ {
- batch[i] = _p_.runq[(h+i)%uint32(len(_p_.runq))]
+ g := _p_.runq[(h+i)%uint32(len(_p_.runq))]
+ batch[(batchHead+i)%uint32(len(batch))] = g
}
if cas(&_p_.runqhead, h, h+n) { // cas-release, commits consume
return n
@@ -3482,26 +3483,21 @@ func runqgrab(_p_ *p, batch []*g) uint32 {
// Steal half of elements from local runnable queue of p2
// and put onto local runnable queue of p.
// Returns one of the stolen elements (or nil if failed).
-func runqsteal(_p_, p2 *p) *g {
- var batch [len(_p_.runq) / 2]*g
-
- n := runqgrab(p2, batch[:])
+func runqsteal(_p_, p2 *p, stealRunNextG bool) *g {
+ t := _p_.runqtail
+ n := runqgrab(p2, &_p_.runq, t, stealRunNextG)
if n == 0 {
return nil
}
n--
- gp := batch[n]
+ gp := _p_.runq[(t+n)%uint32(len(_p_.runq))]
if n == 0 {
return gp
}
h := atomicload(&_p_.runqhead) // load-acquire, synchronize with consumers
- t := _p_.runqtail
if t-h+n >= uint32(len(_p_.runq)) {
throw("runqsteal: runq overflow")
}
- for i := uint32(0); i < n; i++ {
- _p_.runq[(t+i)%uint32(len(_p_.runq))] = batch[i]
- }
atomicstore(&_p_.runqtail, t+n) // store-release, makes the item available for consumption
return gp
}
@@ -3528,20 +3524,16 @@ func testSchedLocalQueue() {
}
}
-var pSink *p
-
func testSchedLocalQueueSteal() {
p1 := new(p)
p2 := new(p)
- pSink = p1 // Force to heap, too large to allocate on system stack ("G0 stack")
- pSink = p2 // Force to heap, too large to allocate on system stack ("G0 stack")
gs := make([]g, len(p1.runq))
for i := 0; i < len(p1.runq); i++ {
for j := 0; j < i; j++ {
gs[j].sig = 0
runqput(p1, &gs[j], false)
}
- gp := runqsteal(p2, p1)
+ gp := runqsteal(p2, p1, true)
s := 0
if gp != nil {
s++
diff --git a/src/runtime/proc_test.go b/src/runtime/proc_test.go
index 4c5712d32f..4471ee5afb 100644
--- a/src/runtime/proc_test.go
+++ b/src/runtime/proc_test.go
@@ -7,6 +7,7 @@ package runtime_test
import (
"math"
"runtime"
+ "runtime/debug"
"sync"
"sync/atomic"
"syscall"
@@ -104,8 +105,8 @@ func TestGoroutineParallelism(t *testing.T) {
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P))
// If runtime triggers a forced GC during this test then it will deadlock,
// since the goroutines can't be stopped/preempted.
- // So give this test as much time as possible.
- runtime.GC()
+ // Disable GC for this test (see issue #10958).
+ defer debug.SetGCPercent(debug.SetGCPercent(-1))
for try := 0; try < N; try++ {
done := make(chan bool)
x := uint32(0)
diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go
index fe7d38a39c..f4014b2e05 100644
--- a/src/runtime/runtime-gdb_test.go
+++ b/src/runtime/runtime-gdb_test.go
@@ -59,7 +59,7 @@ func TestGdbPython(t *testing.T) {
cmd := exec.Command("go", "build", "-o", "a.exe")
cmd.Dir = dir
- out, err := cmd.CombinedOutput()
+ out, err := testEnv(cmd).CombinedOutput()
if err != nil {
t.Fatalf("building source %v\n%s", err, out)
}
@@ -85,7 +85,7 @@ func TestGdbPython(t *testing.T) {
// stack frames on RISC architectures.
canBackTrace := false
switch runtime.GOARCH {
- case "amd64", "386":
+ case "amd64", "386", "ppc64", "ppc64le", "arm", "arm64":
canBackTrace = true
args = append(args,
"-ex", "echo BEGIN goroutine 2 bt\n",
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index ac539b9a9d..3ee5d5d29d 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -266,6 +266,7 @@ type m struct {
// Fields not known to debuggers.
procid uint64 // for debuggers, but offset not hard-coded
gsignal *g // signal-handling g
+ sigmask [4]uintptr // storage for saved signal mask
tls [4]uintptr // thread-local storage (for x86 extern register)
mstartfn func()
curg *g // current running goroutine
@@ -441,7 +442,9 @@ type schedt struct {
// safepointFn should be called on each P at the next GC
// safepoint if p.runSafePointFn is set.
- safePointFn func(*p)
+ safePointFn func(*p)
+ safePointWait int32
+ safePointNote note
profilehz int32 // cpu profiling rate
@@ -467,15 +470,16 @@ type sigtabtt struct {
}
const (
- _SigNotify = 1 << 0 // let signal.Notify have signal, even if from kernel
- _SigKill = 1 << 1 // if signal.Notify doesn't take it, exit quietly
- _SigThrow = 1 << 2 // if signal.Notify doesn't take it, exit loudly
- _SigPanic = 1 << 3 // if the signal is from the kernel, panic
- _SigDefault = 1 << 4 // if the signal isn't explicitly requested, don't monitor it
- _SigHandling = 1 << 5 // our signal handler is registered
- _SigIgnored = 1 << 6 // the signal was ignored before we registered for it
- _SigGoExit = 1 << 7 // cause all runtime procs to exit (only used on Plan 9).
- _SigSetStack = 1 << 8 // add SA_ONSTACK to libc handler
+ _SigNotify = 1 << iota // let signal.Notify have signal, even if from kernel
+ _SigKill // if signal.Notify doesn't take it, exit quietly
+ _SigThrow // if signal.Notify doesn't take it, exit loudly
+ _SigPanic // if the signal is from the kernel, panic
+ _SigDefault // if the signal isn't explicitly requested, don't monitor it
+ _SigHandling // our signal handler is registered
+ _SigIgnored // the signal was ignored before we registered for it
+ _SigGoExit // cause all runtime procs to exit (only used on Plan 9).
+ _SigSetStack // add SA_ONSTACK to libc handler
+ _SigUnblock // unblocked in minit
)
// Layout of in-memory per-function information prepared by linker
@@ -594,8 +598,9 @@ type stkframe struct {
}
const (
- _TraceRuntimeFrames = 1 << 0 // include frames for internal runtime functions.
- _TraceTrap = 1 << 1 // the initial PC, SP are from a trap, not a return PC from a call
+ _TraceRuntimeFrames = 1 << iota // include frames for internal runtime functions.
+ _TraceTrap // the initial PC, SP are from a trap, not a return PC from a call
+ _TraceJumpStack // if traceback is on a systemstack, resume trace at g that called into it
)
const (
diff --git a/src/runtime/runtime_test.go b/src/runtime/runtime_test.go
index d4cccbf084..f65562ab91 100644
--- a/src/runtime/runtime_test.go
+++ b/src/runtime/runtime_test.go
@@ -6,13 +6,8 @@ package runtime_test
import (
"io"
- "io/ioutil"
- "os"
- "os/exec"
. "runtime"
"runtime/debug"
- "strconv"
- "strings"
"testing"
"unsafe"
)
@@ -88,53 +83,6 @@ func BenchmarkDeferMany(b *testing.B) {
}
}
-// The profiling signal handler needs to know whether it is executing runtime.gogo.
-// The constant RuntimeGogoBytes in arch_*.h gives the size of the function;
-// we don't have a way to obtain it from the linker (perhaps someday).
-// Test that the constant matches the size determined by 'go tool nm -S'.
-// The value reported will include the padding between runtime.gogo and the
-// next function in memory. That's fine.
-func TestRuntimeGogoBytes(t *testing.T) {
- switch GOOS {
- case "android", "nacl":
- t.Skipf("skipping on %s", GOOS)
- case "darwin":
- switch GOARCH {
- case "arm", "arm64":
- t.Skipf("skipping on %s/%s, no fork", GOOS, GOARCH)
- }
- }
-
- dir, err := ioutil.TempDir("", "go-build")
- if err != nil {
- t.Fatalf("failed to create temp directory: %v", err)
- }
- defer os.RemoveAll(dir)
-
- out, err := exec.Command("go", "build", "-o", dir+"/hello", "../../test/helloworld.go").CombinedOutput()
- if err != nil {
- t.Fatalf("building hello world: %v\n%s", err, out)
- }
-
- out, err = exec.Command("go", "tool", "nm", "-size", dir+"/hello").CombinedOutput()
- if err != nil {
- t.Fatalf("go tool nm: %v\n%s", err, out)
- }
-
- for _, line := range strings.Split(string(out), "\n") {
- f := strings.Fields(line)
- if len(f) == 4 && f[3] == "runtime.gogo" {
- size, _ := strconv.Atoi(f[1])
- if GogoBytes() != int32(size) {
- t.Fatalf("RuntimeGogoBytes = %d, should be %d", GogoBytes(), size)
- }
- return
- }
- }
-
- t.Fatalf("go tool nm did not report size for runtime.gogo")
-}
-
// golang.org/issue/7063
func TestStopCPUProfilingWithProfilerOff(t *testing.T) {
SetCPUProfileRate(0)
diff --git a/src/runtime/signal1_unix.go b/src/runtime/signal1_unix.go
index 7577d43a64..d3e9dac097 100644
--- a/src/runtime/signal1_unix.go
+++ b/src/runtime/signal1_unix.go
@@ -19,6 +19,19 @@ const (
// Signal forwarding is currently available only on Linux.
var fwdSig [_NSIG]uintptr
+// sigmask represents a general signal mask compatible with the GOOS
+// specific sigset types: the signal numbered x is represented by bit x-1
+// to match the representation expected by sigprocmask.
+type sigmask [(_NSIG + 31) / 32]uint32
+
+// channels for synchronizing signal mask updates with the signal mask
+// thread
+var (
+ disableSigChan chan uint32
+ enableSigChan chan uint32
+ maskUpdatedChan chan struct{}
+)
+
func initsig() {
// _NSIG is the number of signals on this operating system.
// sigtable should describe what to do for all the possible signals.
@@ -61,12 +74,17 @@ func sigenable(sig uint32) {
}
t := &sigtable[sig]
- if t.flags&_SigNotify != 0 && t.flags&_SigHandling == 0 {
- t.flags |= _SigHandling
- if getsig(int32(sig)) == _SIG_IGN {
- t.flags |= _SigIgnored
+ if t.flags&_SigNotify != 0 {
+ ensureSigM()
+ enableSigChan <- sig
+ <-maskUpdatedChan
+ if t.flags&_SigHandling == 0 {
+ t.flags |= _SigHandling
+ if getsig(int32(sig)) == _SIG_IGN {
+ t.flags |= _SigIgnored
+ }
+ setsig(int32(sig), funcPC(sighandler), true)
}
- setsig(int32(sig), funcPC(sighandler), true)
}
}
@@ -76,12 +94,17 @@ func sigdisable(sig uint32) {
}
t := &sigtable[sig]
- if t.flags&_SigNotify != 0 && t.flags&_SigHandling != 0 {
- t.flags &^= _SigHandling
- if t.flags&_SigIgnored != 0 {
- setsig(int32(sig), _SIG_IGN, true)
- } else {
- setsig(int32(sig), _SIG_DFL, true)
+ if t.flags&_SigNotify != 0 {
+ ensureSigM()
+ disableSigChan <- sig
+ <-maskUpdatedChan
+ if t.flags&_SigHandling != 0 {
+ t.flags &^= _SigHandling
+ if t.flags&_SigIgnored != 0 {
+ setsig(int32(sig), _SIG_IGN, true)
+ } else {
+ setsig(int32(sig), _SIG_DFL, true)
+ }
}
}
}
@@ -130,7 +153,52 @@ func crash() {
}
}
- unblocksignals()
+ updatesigmask(sigmask{})
setsig(_SIGABRT, _SIG_DFL, false)
raise(_SIGABRT)
}
+
+// createSigM starts one global, sleeping thread to make sure at least one thread
+// is available to catch signals enabled for os/signal.
+func ensureSigM() {
+ if maskUpdatedChan != nil {
+ return
+ }
+ maskUpdatedChan = make(chan struct{})
+ disableSigChan = make(chan uint32)
+ enableSigChan = make(chan uint32)
+ go func() {
+ // Signal masks are per-thread, so make sure this goroutine stays on one
+ // thread.
+ LockOSThread()
+ defer UnlockOSThread()
+ // The sigBlocked mask contains the signals not active for os/signal,
+ // initially all signals except the essential. When signal.Notify()/Stop is called,
+ // sigenable/sigdisable in turn notify this thread to update its signal
+ // mask accordingly.
+ var sigBlocked sigmask
+ for i := range sigBlocked {
+ sigBlocked[i] = ^uint32(0)
+ }
+ for i := range sigtable {
+ if sigtable[i].flags&_SigUnblock != 0 {
+ sigBlocked[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
+ }
+ }
+ updatesigmask(sigBlocked)
+ for {
+ select {
+ case sig := <-enableSigChan:
+ if b := sig - 1; b >= 0 {
+ sigBlocked[b/32] &^= (1 << (b & 31))
+ }
+ case sig := <-disableSigChan:
+ if b := sig - 1; b >= 0 {
+ sigBlocked[b/32] |= (1 << (b & 31))
+ }
+ }
+ updatesigmask(sigBlocked)
+ maskUpdatedChan <- struct{}{}
+ }
+ }()
+}
diff --git a/src/runtime/signal_darwin.go b/src/runtime/signal_darwin.go
index 32ecce0d7d..6cd18653d5 100644
--- a/src/runtime/signal_darwin.go
+++ b/src/runtime/signal_darwin.go
@@ -16,14 +16,14 @@ var sigtable = [...]sigTabT{
/* 1 */ {_SigNotify + _SigKill, "SIGHUP: terminal line hangup"},
/* 2 */ {_SigNotify + _SigKill, "SIGINT: interrupt"},
/* 3 */ {_SigNotify + _SigThrow, "SIGQUIT: quit"},
- /* 4 */ {_SigThrow, "SIGILL: illegal instruction"},
- /* 5 */ {_SigThrow, "SIGTRAP: trace trap"},
+ /* 4 */ {_SigThrow + _SigUnblock, "SIGILL: illegal instruction"},
+ /* 5 */ {_SigThrow + _SigUnblock, "SIGTRAP: trace trap"},
/* 6 */ {_SigNotify + _SigThrow, "SIGABRT: abort"},
/* 7 */ {_SigThrow, "SIGEMT: emulate instruction executed"},
- /* 8 */ {_SigPanic, "SIGFPE: floating-point exception"},
+ /* 8 */ {_SigPanic + _SigUnblock, "SIGFPE: floating-point exception"},
/* 9 */ {0, "SIGKILL: kill"},
- /* 10 */ {_SigPanic, "SIGBUS: bus error"},
- /* 11 */ {_SigPanic, "SIGSEGV: segmentation violation"},
+ /* 10 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
+ /* 11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
/* 12 */ {_SigThrow, "SIGSYS: bad system call"},
/* 13 */ {_SigNotify, "SIGPIPE: write to broken pipe"},
/* 14 */ {_SigNotify, "SIGALRM: alarm clock"},
@@ -32,14 +32,14 @@ var sigtable = [...]sigTabT{
/* 17 */ {0, "SIGSTOP: stop"},
/* 18 */ {_SigNotify + _SigDefault, "SIGTSTP: keyboard stop"},
/* 19 */ {0, "SIGCONT: continue after stop"},
- /* 20 */ {_SigNotify, "SIGCHLD: child status has changed"},
+ /* 20 */ {_SigNotify + _SigUnblock, "SIGCHLD: child status has changed"},
/* 21 */ {_SigNotify + _SigDefault, "SIGTTIN: background read from tty"},
/* 22 */ {_SigNotify + _SigDefault, "SIGTTOU: background write to tty"},
/* 23 */ {_SigNotify, "SIGIO: i/o now possible"},
/* 24 */ {_SigNotify, "SIGXCPU: cpu limit exceeded"},
/* 25 */ {_SigNotify, "SIGXFSZ: file size limit exceeded"},
/* 26 */ {_SigNotify, "SIGVTALRM: virtual alarm clock"},
- /* 27 */ {_SigNotify, "SIGPROF: profiling alarm clock"},
+ /* 27 */ {_SigNotify + _SigUnblock, "SIGPROF: profiling alarm clock"},
/* 28 */ {_SigNotify, "SIGWINCH: window size change"},
/* 29 */ {_SigNotify, "SIGINFO: status request from keyboard"},
/* 30 */ {_SigNotify, "SIGUSR1: user-defined signal 1"},
diff --git a/src/runtime/signal_linux.go b/src/runtime/signal_linux.go
index f8250b9fa1..2f25b59663 100644
--- a/src/runtime/signal_linux.go
+++ b/src/runtime/signal_linux.go
@@ -16,20 +16,20 @@ var sigtable = [...]sigTabT{
/* 1 */ {_SigNotify + _SigKill, "SIGHUP: terminal line hangup"},
/* 2 */ {_SigNotify + _SigKill, "SIGINT: interrupt"},
/* 3 */ {_SigNotify + _SigThrow, "SIGQUIT: quit"},
- /* 4 */ {_SigThrow, "SIGILL: illegal instruction"},
- /* 5 */ {_SigThrow, "SIGTRAP: trace trap"},
+ /* 4 */ {_SigThrow + _SigUnblock, "SIGILL: illegal instruction"},
+ /* 5 */ {_SigThrow + _SigUnblock, "SIGTRAP: trace trap"},
/* 6 */ {_SigNotify + _SigThrow, "SIGABRT: abort"},
- /* 7 */ {_SigPanic, "SIGBUS: bus error"},
- /* 8 */ {_SigPanic, "SIGFPE: floating-point exception"},
+ /* 7 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
+ /* 8 */ {_SigPanic + _SigUnblock, "SIGFPE: floating-point exception"},
/* 9 */ {0, "SIGKILL: kill"},
/* 10 */ {_SigNotify, "SIGUSR1: user-defined signal 1"},
- /* 11 */ {_SigPanic, "SIGSEGV: segmentation violation"},
+ /* 11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
/* 12 */ {_SigNotify, "SIGUSR2: user-defined signal 2"},
/* 13 */ {_SigNotify, "SIGPIPE: write to broken pipe"},
/* 14 */ {_SigNotify, "SIGALRM: alarm clock"},
/* 15 */ {_SigNotify + _SigKill, "SIGTERM: termination"},
- /* 16 */ {_SigThrow, "SIGSTKFLT: stack fault"},
- /* 17 */ {_SigNotify, "SIGCHLD: child status has changed"},
+ /* 16 */ {_SigThrow + _SigUnblock, "SIGSTKFLT: stack fault"},
+ /* 17 */ {_SigNotify + _SigUnblock, "SIGCHLD: child status has changed"},
/* 18 */ {0, "SIGCONT: continue"},
/* 19 */ {0, "SIGSTOP: stop, unblockable"},
/* 20 */ {_SigNotify + _SigDefault, "SIGTSTP: keyboard stop"},
@@ -39,7 +39,7 @@ var sigtable = [...]sigTabT{
/* 24 */ {_SigNotify, "SIGXCPU: cpu limit exceeded"},
/* 25 */ {_SigNotify, "SIGXFSZ: file size limit exceeded"},
/* 26 */ {_SigNotify, "SIGVTALRM: virtual alarm clock"},
- /* 27 */ {_SigNotify, "SIGPROF: profiling alarm clock"},
+ /* 27 */ {_SigNotify + _SigUnblock, "SIGPROF: profiling alarm clock"},
/* 28 */ {_SigNotify, "SIGWINCH: window size change"},
/* 29 */ {_SigNotify, "SIGIO: i/o now possible"},
/* 30 */ {_SigNotify, "SIGPWR: power failure restart"},
diff --git a/src/runtime/signal_netbsd.go b/src/runtime/signal_netbsd.go
index 78afc59efa..d93a450d98 100644
--- a/src/runtime/signal_netbsd.go
+++ b/src/runtime/signal_netbsd.go
@@ -14,14 +14,14 @@ var sigtable = [...]sigTabT{
/* 1 */ {_SigNotify + _SigKill, "SIGHUP: terminal line hangup"},
/* 2 */ {_SigNotify + _SigKill, "SIGINT: interrupt"},
/* 3 */ {_SigNotify + _SigThrow, "SIGQUIT: quit"},
- /* 4 */ {_SigThrow, "SIGILL: illegal instruction"},
- /* 5 */ {_SigThrow, "SIGTRAP: trace trap"},
+ /* 4 */ {_SigThrow + _SigUnblock, "SIGILL: illegal instruction"},
+ /* 5 */ {_SigThrow + _SigUnblock, "SIGTRAP: trace trap"},
/* 6 */ {_SigNotify + _SigThrow, "SIGABRT: abort"},
/* 7 */ {_SigThrow, "SIGEMT: emulate instruction executed"},
- /* 8 */ {_SigPanic, "SIGFPE: floating-point exception"},
+ /* 8 */ {_SigPanic + _SigUnblock, "SIGFPE: floating-point exception"},
/* 9 */ {0, "SIGKILL: kill"},
- /* 10 */ {_SigPanic, "SIGBUS: bus error"},
- /* 11 */ {_SigPanic, "SIGSEGV: segmentation violation"},
+ /* 10 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
+ /* 11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
/* 12 */ {_SigThrow, "SIGSYS: bad system call"},
/* 13 */ {_SigNotify, "SIGPIPE: write to broken pipe"},
/* 14 */ {_SigNotify, "SIGALRM: alarm clock"},
@@ -30,14 +30,14 @@ var sigtable = [...]sigTabT{
/* 17 */ {0, "SIGSTOP: stop"},
/* 18 */ {_SigNotify + _SigDefault, "SIGTSTP: keyboard stop"},
/* 19 */ {0, "SIGCONT: continue after stop"},
- /* 20 */ {_SigNotify, "SIGCHLD: child status has changed"},
+ /* 20 */ {_SigNotify + _SigUnblock, "SIGCHLD: child status has changed"},
/* 21 */ {_SigNotify + _SigDefault, "SIGTTIN: background read from tty"},
/* 22 */ {_SigNotify + _SigDefault, "SIGTTOU: background write to tty"},
/* 23 */ {_SigNotify, "SIGIO: i/o now possible"},
/* 24 */ {_SigNotify, "SIGXCPU: cpu limit exceeded"},
/* 25 */ {_SigNotify, "SIGXFSZ: file size limit exceeded"},
/* 26 */ {_SigNotify, "SIGVTALRM: virtual alarm clock"},
- /* 27 */ {_SigNotify, "SIGPROF: profiling alarm clock"},
+ /* 27 */ {_SigNotify + _SigUnblock, "SIGPROF: profiling alarm clock"},
/* 28 */ {_SigNotify, "SIGWINCH: window size change"},
/* 29 */ {_SigNotify, "SIGINFO: status request from keyboard"},
/* 30 */ {_SigNotify, "SIGUSR1: user-defined signal 1"},
diff --git a/src/runtime/signal_solaris.go b/src/runtime/signal_solaris.go
index 2986c5aabc..d8ac676846 100644
--- a/src/runtime/signal_solaris.go
+++ b/src/runtime/signal_solaris.go
@@ -14,21 +14,21 @@ var sigtable = [...]sigTabT{
/* 1 */ {_SigNotify + _SigKill, "SIGHUP: hangup"},
/* 2 */ {_SigNotify + _SigKill, "SIGINT: interrupt (rubout)"},
/* 3 */ {_SigNotify + _SigThrow, "SIGQUIT: quit (ASCII FS)"},
- /* 4 */ {_SigThrow, "SIGILL: illegal instruction (not reset when caught)"},
- /* 5 */ {_SigThrow, "SIGTRAP: trace trap (not reset when caught)"},
+ /* 4 */ {_SigThrow + _SigUnblock, "SIGILL: illegal instruction (not reset when caught)"},
+ /* 5 */ {_SigThrow + _SigUnblock, "SIGTRAP: trace trap (not reset when caught)"},
/* 6 */ {_SigNotify + _SigThrow, "SIGABRT: used by abort, replace SIGIOT in the future"},
/* 7 */ {_SigThrow, "SIGEMT: EMT instruction"},
- /* 8 */ {_SigPanic, "SIGFPE: floating point exception"},
+ /* 8 */ {_SigPanic + _SigUnblock, "SIGFPE: floating point exception"},
/* 9 */ {0, "SIGKILL: kill (cannot be caught or ignored)"},
- /* 10 */ {_SigPanic, "SIGBUS: bus error"},
- /* 11 */ {_SigPanic, "SIGSEGV: segmentation violation"},
+ /* 10 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
+ /* 11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
/* 12 */ {_SigThrow, "SIGSYS: bad argument to system call"},
/* 13 */ {_SigNotify, "SIGPIPE: write on a pipe with no one to read it"},
/* 14 */ {_SigNotify, "SIGALRM: alarm clock"},
/* 15 */ {_SigNotify + _SigKill, "SIGTERM: software termination signal from kill"},
/* 16 */ {_SigNotify, "SIGUSR1: user defined signal 1"},
/* 17 */ {_SigNotify, "SIGUSR2: user defined signal 2"},
- /* 18 */ {_SigNotify, "SIGCHLD: child status change alias (POSIX)"},
+ /* 18 */ {_SigNotify + _SigUnblock, "SIGCHLD: child status change alias (POSIX)"},
/* 19 */ {_SigNotify, "SIGPWR: power-fail restart"},
/* 20 */ {_SigNotify, "SIGWINCH: window size change"},
/* 21 */ {_SigNotify, "SIGURG: urgent socket condition"},
@@ -39,7 +39,7 @@ var sigtable = [...]sigTabT{
/* 26 */ {_SigNotify + _SigDefault, "SIGTTIN: background tty read attempted"},
/* 27 */ {_SigNotify + _SigDefault, "SIGTTOU: background tty write attempted"},
/* 28 */ {_SigNotify, "SIGVTALRM: virtual timer expired"},
- /* 29 */ {_SigNotify, "SIGPROF: profiling timer expired"},
+ /* 29 */ {_SigNotify + _SigUnblock, "SIGPROF: profiling timer expired"},
/* 30 */ {_SigNotify, "SIGXCPU: exceeded cpu limit"},
/* 31 */ {_SigNotify, "SIGXFSZ: exceeded file size limit"},
/* 32 */ {_SigNotify, "SIGWAITING: reserved signal no longer used by"},
diff --git a/src/runtime/signal_windows.go b/src/runtime/signal_windows.go
index da8a1c5801..b2fce53534 100644
--- a/src/runtime/signal_windows.go
+++ b/src/runtime/signal_windows.go
@@ -131,7 +131,9 @@ func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
print("PC=", hex(r.ip()), "\n")
if _g_.m.lockedg != nil && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
- print("signal arrived during cgo execution\n")
+ if iscgo {
+ print("signal arrived during external code execution\n")
+ }
gp = _g_.m.lockedg
}
print("\n")
diff --git a/src/runtime/sigqueue_plan9.go b/src/runtime/sigqueue_plan9.go
index 38f0a57b90..f000fabd1a 100644
--- a/src/runtime/sigqueue_plan9.go
+++ b/src/runtime/sigqueue_plan9.go
@@ -17,21 +17,29 @@ var sig struct {
sleeping bool
}
+type noteData struct {
+ s [_ERRMAX]byte
+ n int // n bytes of s are valid
+}
+
type noteQueue struct {
lock mutex
- data [qsize]*byte
+ data [qsize]noteData
ri int
wi int
full bool
}
+// It is not allowed to allocate memory in the signal handler.
func (q *noteQueue) push(item *byte) bool {
lock(&q.lock)
if q.full {
unlock(&q.lock)
return false
}
- q.data[q.wi] = item
+ s := gostringnocopy(item)
+ copy(q.data[q.wi].s[:], s)
+ q.data[q.wi].n = len(s)
q.wi++
if q.wi == qsize {
q.wi = 0
@@ -43,14 +51,15 @@ func (q *noteQueue) push(item *byte) bool {
return true
}
-func (q *noteQueue) pop() *byte {
+func (q *noteQueue) pop() string {
lock(&q.lock)
q.full = false
if q.ri == q.wi {
unlock(&q.lock)
- return nil
+ return ""
}
- item := q.data[q.ri]
+ note := &q.data[q.ri]
+ item := string(note.s[:note.n])
q.ri++
if q.ri == qsize {
q.ri = 0
@@ -86,8 +95,8 @@ func sendNote(s *byte) bool {
func signal_recv() string {
for {
note := sig.q.pop()
- if note != nil {
- return gostring(note)
+ if note != "" {
+ return note
}
lock(&sig.lock)
diff --git a/src/runtime/slice.go b/src/runtime/slice.go
index 5ccc6592bf..79b611839d 100644
--- a/src/runtime/slice.go
+++ b/src/runtime/slice.go
@@ -84,10 +84,13 @@ func growslice(t *slicetype, old slice, n int) slice {
memclr(add(p, lenmem), capmem-lenmem)
} else {
// Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan unitialized memory.
- // TODO(rsc): Use memmove when !writeBarrierEnabled.
p = newarray(et, uintptr(newcap))
- for i := 0; i < old.len; i++ {
- typedmemmove(et, add(p, uintptr(i)*et.size), add(old.array, uintptr(i)*et.size))
+ if !writeBarrierEnabled {
+ memmove(p, old.array, lenmem)
+ } else {
+ for i := uintptr(0); i < lenmem; i += et.size {
+ typedmemmove(et, add(p, i), add(old.array, i))
+ }
}
}
diff --git a/src/runtime/stack1.go b/src/runtime/stack1.go
index f74694b7e9..27427af955 100644
--- a/src/runtime/stack1.go
+++ b/src/runtime/stack1.go
@@ -352,6 +352,12 @@ func adjustpointer(adjinfo *adjustinfo, vpp unsafe.Pointer) {
}
}
+// Information from the compiler about the layout of stack frames.
+type bitvector struct {
+ n int32 // # of bits
+ bytedata *uint8
+}
+
type gobitvector struct {
n uintptr
bytedata []uint8
@@ -381,20 +387,20 @@ func adjustpointers(scanp unsafe.Pointer, cbv *bitvector, adjinfo *adjustinfo, f
print(" ", add(scanp, i*ptrSize), ":", ptrnames[ptrbit(&bv, i)], ":", hex(*(*uintptr)(add(scanp, i*ptrSize))), " # ", i, " ", bv.bytedata[i/4], "\n")
}
if ptrbit(&bv, i) == 1 {
- p := *(*unsafe.Pointer)(add(scanp, i*ptrSize))
- up := uintptr(p)
- if f != nil && 0 < up && up < _PageSize && debug.invalidptr != 0 || up == poisonStack {
+ pp := (*uintptr)(add(scanp, i*ptrSize))
+ p := *pp
+ if f != nil && 0 < p && p < _PageSize && debug.invalidptr != 0 || p == poisonStack {
// Looks like a junk value in a pointer slot.
// Live analysis wrong?
getg().m.traceback = 2
- print("runtime: bad pointer in frame ", funcname(f), " at ", add(scanp, i*ptrSize), ": ", p, "\n")
+ print("runtime: bad pointer in frame ", funcname(f), " at ", pp, ": ", hex(p), "\n")
throw("invalid stack pointer")
}
- if minp <= up && up < maxp {
+ if minp <= p && p < maxp {
if stackDebug >= 3 {
print("adjust ptr ", p, " ", funcname(f), "\n")
}
- *(*unsafe.Pointer)(add(scanp, i*ptrSize)) = unsafe.Pointer(up + delta)
+ *pp = p + delta
}
}
}
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index 25f5bf46fb..687f067cb9 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -32,6 +32,8 @@ const (
// moduledata records information about the layout of the executable
// image. It is written by the linker. Any changes here must be
// matched changes to the code in cmd/internal/ld/symtab.go:symtab.
+// moduledata is stored in read-only memory; none of the pointers here
+// are visible to the garbage collector.
type moduledata struct {
pclntable []byte
ftab []functab
@@ -48,18 +50,24 @@ type moduledata struct {
typelinks []*_type
+ modulename string
+ modulehashes []modulehash
+
gcdatamask, gcbssmask bitvector
- // write barrier shadow data
- // 64-bit systems only, enabled by GODEBUG=wbshadow=1.
- // See also the shadow_* fields on mheap in mheap.go.
- shadow_data uintptr // data-addr + shadow_data = shadow data addr
- data_start uintptr // start of shadowed data addresses
- data_end uintptr // end of shadowed data addresses
-
next *moduledata
}
+// For each shared library a module links against, the linker creates an entry in the
+// moduledata.modulehashes slice containing the name of the module, the abi hash seen
+// at link time and a pointer to the runtime abi hash. These are checked in
+// moduledataverify1 below.
+type modulehash struct {
+ modulename string
+ linktimehash string
+ runtimehash *string
+}
+
var firstmoduledata moduledata // linker symbol
var lastmoduledatap *moduledata // linker symbol
@@ -124,6 +132,13 @@ func moduledataverify1(datap *moduledata) {
datap.maxpc != datap.ftab[nftab].entry {
throw("minpc or maxpc invalid")
}
+
+ for _, modulehash := range datap.modulehashes {
+ if modulehash.linktimehash != *modulehash.runtimehash {
+ println("abi mismatch detected between", datap.modulename, "and", modulehash.modulename)
+ throw("abi mismatch")
+ }
+ }
}
// FuncForPC returns a *Func describing the function that contains the
diff --git a/src/runtime/trace.go b/src/runtime/trace.go
index 3b7501b9b4..6da7baddc5 100644
--- a/src/runtime/trace.go
+++ b/src/runtime/trace.go
@@ -132,10 +132,7 @@ type traceBuf struct {
func StartTrace() error {
// Stop the world, so that we can take a consistent snapshot
// of all goroutines at the beginning of the trace.
- semacquire(&worldsema, false)
- _g_ := getg()
- _g_.m.preemptoff = "start tracing"
- systemstack(stoptheworld)
+ stopTheWorld("start tracing")
// We are in stop-the-world, but syscalls can finish and write to trace concurrently.
// Exitsyscall could check trace.enabled long before and then suddenly wake up
@@ -146,9 +143,7 @@ func StartTrace() error {
if trace.enabled || trace.shutdown {
unlock(&trace.bufLock)
- _g_.m.preemptoff = ""
- semrelease(&worldsema)
- systemstack(starttheworld)
+ startTheWorld()
return errorString("tracing is already enabled")
}
@@ -175,9 +170,7 @@ func StartTrace() error {
unlock(&trace.bufLock)
- _g_.m.preemptoff = ""
- semrelease(&worldsema)
- systemstack(starttheworld)
+ startTheWorld()
return nil
}
@@ -186,19 +179,14 @@ func StartTrace() error {
func StopTrace() {
// Stop the world so that we can collect the trace buffers from all p's below,
// and also to avoid races with traceEvent.
- semacquire(&worldsema, false)
- _g_ := getg()
- _g_.m.preemptoff = "stop tracing"
- systemstack(stoptheworld)
+ stopTheWorld("stop tracing")
// See the comment in StartTrace.
lock(&trace.bufLock)
if !trace.enabled {
unlock(&trace.bufLock)
- _g_.m.preemptoff = ""
- semrelease(&worldsema)
- systemstack(starttheworld)
+ startTheWorld()
return
}
@@ -236,9 +224,7 @@ func StopTrace() {
unlock(&trace.bufLock)
- _g_.m.preemptoff = ""
- semrelease(&worldsema)
- systemstack(starttheworld)
+ startTheWorld()
// The world is started but we've set trace.shutdown, so new tracing can't start.
// Wait for the trace reader to flush pending buffers and stop.
@@ -428,9 +414,9 @@ func traceEvent(ev byte, skip int, args ...uint64) {
// The caller checked that trace.enabled == true, but trace.enabled might have been
// turned off between the check and now. Check again. traceLockBuffer did mp.locks++,
- // StopTrace does stoptheworld, and stoptheworld waits for mp.locks to go back to zero,
+ // StopTrace does stopTheWorld, and stopTheWorld waits for mp.locks to go back to zero,
// so if we see trace.enabled == true now, we know it's true for the rest of the function.
- // Exitsyscall can run even during stoptheworld. The race with StartTrace/StopTrace
+ // Exitsyscall can run even during stopTheWorld. The race with StartTrace/StopTrace
// during tracing in exitsyscall is resolved by locking trace.bufLock in traceLockBuffer.
if !trace.enabled {
traceReleaseBuffer(pid)
@@ -733,7 +719,7 @@ func traceProcStart() {
}
func traceProcStop(pp *p) {
- // Sysmon and stoptheworld can stop Ps blocked in syscalls,
+ // Sysmon and stopTheWorld can stop Ps blocked in syscalls,
// to handle this we temporary employ the P.
mp := acquirem()
oldp := mp.p
@@ -807,7 +793,7 @@ func traceGoSysExit(ts int64) {
}
func traceGoSysBlock(pp *p) {
- // Sysmon and stoptheworld can declare syscalls running on remote Ps as blocked,
+ // Sysmon and stopTheWorld can declare syscalls running on remote Ps as blocked,
// to handle this we temporary employ the P.
mp := acquirem()
oldp := mp.p
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 9f34e37ea4..5ed601e6f3 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -46,6 +46,9 @@ var (
timerprocPC uintptr
gcBgMarkWorkerPC uintptr
systemstack_switchPC uintptr
+ systemstackPC uintptr
+
+ gogoPC uintptr
externalthreadhandlerp uintptr // initialized elsewhere
)
@@ -69,6 +72,10 @@ func tracebackinit() {
timerprocPC = funcPC(timerproc)
gcBgMarkWorkerPC = funcPC(gcBgMarkWorker)
systemstack_switchPC = funcPC(systemstack_switch)
+ systemstackPC = funcPC(systemstack)
+
+ // used by sigprof handler
+ gogoPC = funcPC(gogo)
}
// Traceback over the deferred function calls.
@@ -194,7 +201,14 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
// Found an actual function.
// Derive frame pointer and link register.
if frame.fp == 0 {
- frame.fp = frame.sp + uintptr(funcspdelta(f, frame.pc))
+ // We want to jump over the systemstack switch. If we're running on the
+ // g0, this systemstack is at the top of the stack.
+ // if we're not on g0 or there's a no curg, then this is a regular call.
+ sp := frame.sp
+ if flags&_TraceJumpStack != 0 && f.entry == systemstackPC && gp == g.m.g0 && gp.m.curg != nil {
+ sp = gp.m.curg.sched.sp
+ }
+ frame.fp = sp + uintptr(funcspdelta(f, frame.pc))
if !usesLR {
// On x86, call instruction pushes return PC before entering new function.
frame.fp += regSize
@@ -455,7 +469,7 @@ func setArgInfo(frame *stkframe, f *_func, needArgMap bool) {
throw("reflect mismatch")
}
bv := (*bitvector)(unsafe.Pointer(fn[1]))
- frame.arglen = uintptr(bv.n / 2 * ptrSize)
+ frame.arglen = uintptr(bv.n * ptrSize)
frame.argmap = bv
}
}
@@ -517,9 +531,10 @@ func traceback1(pc, sp, lr uintptr, gp *g, flags uint) {
func callers(skip int, pcbuf []uintptr) int {
sp := getcallersp(unsafe.Pointer(&skip))
pc := uintptr(getcallerpc(unsafe.Pointer(&skip)))
+ gp := getg()
var n int
systemstack(func() {
- n = gentraceback(pc, sp, 0, getg(), skip, &pcbuf[0], len(pcbuf), nil, nil, 0)
+ n = gentraceback(pc, sp, 0, gp, skip, &pcbuf[0], len(pcbuf), nil, nil, 0)
})
return n
}
diff --git a/src/runtime/type.go b/src/runtime/type.go
index 48df2a4382..45bdac8b91 100644
--- a/src/runtime/type.go
+++ b/src/runtime/type.go
@@ -20,17 +20,10 @@ type _type struct {
fieldalign uint8
kind uint8
alg *typeAlg
- // gc stores type info required for garbage collector.
- // If (kind&KindGCProg)==0, then gc[0] points at sparse GC bitmap
- // (no indirection), 4 bits per word.
- // If (kind&KindGCProg)!=0, then gc[1] points to a compiler-generated
- // read-only GC program; and gc[0] points to BSS space for sparse GC bitmap.
- // For huge types (>maxGCMask), runtime unrolls the program directly into
- // GC bitmap and gc[0] is not used. For moderately-sized types, runtime
- // unrolls the program into gc[0] space on first use. The first byte of gc[0]
- // (gc[0][0]) contains 'unroll' flag saying whether the program is already
- // unrolled into gc[0] or not.
- gc [2]uintptr
+ // gcdata stores the GC type data for the garbage collector.
+ // If the KindGCProg bit is set in kind, gcdata is a GC program.
+ // Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
+ gcdata *byte
_string *string
x *uncommontype
ptrto *_type
diff --git a/src/strconv/ftoa.go b/src/strconv/ftoa.go
index d59c78e493..468c37fafb 100644
--- a/src/strconv/ftoa.go
+++ b/src/strconv/ftoa.go
@@ -223,9 +223,8 @@ func formatDigits(dst []byte, shortest bool, neg bool, digs decimalSlice, prec i
return append(dst, '%', fmt)
}
-// Round d (= mant * 2^exp) to the shortest number of digits
-// that will let the original floating point value be precisely
-// reconstructed. Size is original floating point size (64 or 32).
+// roundShortest rounds d (= mant * 2^exp) to the shortest number of digits
+// that will let the original floating point value be precisely reconstructed.
func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
// If mantissa is zero, the number is zero; stop now.
if mant == 0 {
diff --git a/src/syscall/exec_linux.go b/src/syscall/exec_linux.go
index ced2ca862d..3aa30c7364 100644
--- a/src/syscall/exec_linux.go
+++ b/src/syscall/exec_linux.go
@@ -132,26 +132,6 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
}
}
- // Parent death signal
- if sys.Pdeathsig != 0 {
- _, _, err1 = RawSyscall6(SYS_PRCTL, PR_SET_PDEATHSIG, uintptr(sys.Pdeathsig), 0, 0, 0, 0)
- if err1 != 0 {
- goto childerror
- }
-
- // Signal self if parent is already dead. This might cause a
- // duplicate signal in rare cases, but it won't matter when
- // using SIGKILL.
- r1, _, _ = RawSyscall(SYS_GETPPID, 0, 0, 0)
- if r1 != ppid {
- pid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
- _, _, err1 := RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0)
- if err1 != 0 {
- goto childerror
- }
- }
- }
-
// Enable tracing if requested.
if sys.Ptrace {
_, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0)
@@ -232,6 +212,26 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
}
}
+ // Parent death signal
+ if sys.Pdeathsig != 0 {
+ _, _, err1 = RawSyscall6(SYS_PRCTL, PR_SET_PDEATHSIG, uintptr(sys.Pdeathsig), 0, 0, 0, 0)
+ if err1 != 0 {
+ goto childerror
+ }
+
+ // Signal self if parent is already dead. This might cause a
+ // duplicate signal in rare cases, but it won't matter when
+ // using SIGKILL.
+ r1, _, _ = RawSyscall(SYS_GETPPID, 0, 0, 0)
+ if r1 != ppid {
+ pid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
+ _, _, err1 := RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0)
+ if err1 != 0 {
+ goto childerror
+ }
+ }
+ }
+
// Pass 1: look for fd[i] < i and move those up above len(fd)
// so that pass 2 won't stomp on an fd it needs later.
if pipe < nextfd {
diff --git a/src/syscall/mkall.sh b/src/syscall/mkall.sh
index 1b7cd64c8d..85fab4ff3e 100755
--- a/src/syscall/mkall.sh
+++ b/src/syscall/mkall.sh
@@ -179,7 +179,7 @@ linux_amd64)
linux_arm)
mkerrors="$mkerrors"
mksyscall="./mksyscall.pl -l32 -arm"
- mksysnum="curl -s 'http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/arch/arm/include/uapi/asm/unistd.h' | ./mksysnum_linux.pl"
+ mksysnum="curl -s 'http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/arch/arm/include/uapi/asm/unistd.h' | ./mksysnum_linux.pl -"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
linux_arm64)
@@ -285,7 +285,7 @@ esac
syscall_goos="syscall_bsd.go $syscall_goos"
;;
esac
- if [ -n "$mksyscall" ]; then echo "$mksyscall $syscall_goos syscall_$GOOSARCH.go |gofmt >zsyscall_$GOOSARCH.go"; fi
+ if [ -n "$mksyscall" ]; then echo "$mksyscall $syscall_goos $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.go"; fi
if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi
if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi
if [ -n "$mktypes" ]; then echo "$mktypes types_$GOOS.go |gofmt >ztypes_$GOOSARCH.go"; fi
diff --git a/src/syscall/mkerrors.sh b/src/syscall/mkerrors.sh
index d25527bbf9..438de6e5d8 100755
--- a/src/syscall/mkerrors.sh
+++ b/src/syscall/mkerrors.sh
@@ -87,7 +87,9 @@ includes_FreeBSD='
includes_Linux='
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
+#ifndef __LP64__
#define _FILE_OFFSET_BITS 64
+#endif
#define _GNU_SOURCE
#include
diff --git a/src/syscall/mksysnum_linux.pl b/src/syscall/mksysnum_linux.pl
index 7a8add8bab..b6fbcb599b 100755
--- a/src/syscall/mksysnum_linux.pl
+++ b/src/syscall/mksysnum_linux.pl
@@ -28,7 +28,8 @@ sub fmt {
}
my $prev;
-while(<>){
+open(GCC, "gcc -E -dD $ARGV[0] |") || die "can't run gcc";
+while(){
if(/^#define __NR_syscalls\s+/) {
# ignore redefinitions of __NR_syscalls
}
diff --git a/src/syscall/syscall_linux.go b/src/syscall/syscall_linux.go
index 05d4044635..4f88d517e4 100644
--- a/src/syscall/syscall_linux.go
+++ b/src/syscall/syscall_linux.go
@@ -852,7 +852,6 @@ func Getpgrp() (pid int) {
//sysnb Gettid() (tid int)
//sys Getxattr(path string, attr string, dest []byte) (sz int, err error)
//sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error)
-//sysnb InotifyInit() (fd int, err error)
//sysnb InotifyInit1(flags int) (fd int, err error)
//sysnb InotifyRmWatch(fd int, watchdesc uint32) (success int, err error)
//sysnb Kill(pid int, sig Signal) (err error)
diff --git a/src/syscall/syscall_linux_386.go b/src/syscall/syscall_linux_386.go
index 98636a53ad..9ee1c1cd16 100644
--- a/src/syscall/syscall_linux_386.go
+++ b/src/syscall/syscall_linux_386.go
@@ -66,6 +66,7 @@ func Pipe2(p []int, flags int) (err error) {
//sysnb Geteuid() (euid int) = SYS_GETEUID32
//sysnb Getgid() (gid int) = SYS_GETGID32
//sysnb Getuid() (uid int) = SYS_GETUID32
+//sysnb InotifyInit() (fd int, err error)
//sys Ioperm(from int, num int, on int) (err error)
//sys Iopl(level int) (err error)
//sys Lchown(path string, uid int, gid int) (err error) = SYS_LCHOWN32
diff --git a/src/syscall/syscall_linux_amd64.go b/src/syscall/syscall_linux_amd64.go
index fad9c32580..6fbef21120 100644
--- a/src/syscall/syscall_linux_amd64.go
+++ b/src/syscall/syscall_linux_amd64.go
@@ -16,6 +16,7 @@ const _SYS_dup = SYS_DUP2
//sysnb Getgid() (gid int)
//sysnb Getrlimit(resource int, rlim *Rlimit) (err error)
//sysnb Getuid() (uid int)
+//sysnb InotifyInit() (fd int, err error)
//sys Ioperm(from int, num int, on int) (err error)
//sys Iopl(level int) (err error)
//sys Lchown(path string, uid int, gid int) (err error)
diff --git a/src/syscall/syscall_linux_arm.go b/src/syscall/syscall_linux_arm.go
index f0cc25ebed..218d6b86d4 100644
--- a/src/syscall/syscall_linux_arm.go
+++ b/src/syscall/syscall_linux_arm.go
@@ -87,6 +87,7 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
//sysnb Geteuid() (euid int) = SYS_GETEUID32
//sysnb Getgid() (gid int) = SYS_GETGID32
//sysnb Getuid() (uid int) = SYS_GETUID32
+//sysnb InotifyInit() (fd int, err error)
//sys Lchown(path string, uid int, gid int) (err error) = SYS_LCHOWN32
//sys Listen(s int, n int) (err error)
//sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64
diff --git a/src/syscall/syscall_linux_arm64.go b/src/syscall/syscall_linux_arm64.go
index 3c4eabca5f..7ca4164544 100644
--- a/src/syscall/syscall_linux_arm64.go
+++ b/src/syscall/syscall_linux_arm64.go
@@ -124,11 +124,14 @@ func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint64(length)
}
+func InotifyInit() (fd int, err error) {
+ return InotifyInit1(0)
+}
+
// TODO(dfc): constants that should be in zsysnum_linux_arm64.go, remove
// these when the deprecated syscalls that the syscall package relies on
// are removed.
const (
- SYS_INOTIFY_INIT = 1043
SYS_GETPGRP = 1060
SYS_UTIMES = 1037
SYS_FUTIMESAT = 1066
diff --git a/src/syscall/syscall_linux_ppc64x.go b/src/syscall/syscall_linux_ppc64x.go
index 5318c6128c..10489d927f 100644
--- a/src/syscall/syscall_linux_ppc64x.go
+++ b/src/syscall/syscall_linux_ppc64x.go
@@ -19,6 +19,7 @@ const _SYS_dup = SYS_DUP2
//sysnb Getgid() (gid int)
//sysnb Getrlimit(resource int, rlim *Rlimit) (err error) = SYS_UGETRLIMIT
//sysnb Getuid() (uid int)
+//sysnb InotifyInit() (fd int, err error)
//sys Ioperm(from int, num int, on int) (err error)
//sys Iopl(level int) (err error)
//sys Lchown(path string, uid int, gid int) (err error)
diff --git a/src/syscall/syscall_linux_test.go b/src/syscall/syscall_linux_test.go
new file mode 100644
index 0000000000..40fce6d68c
--- /dev/null
+++ b/src/syscall/syscall_linux_test.go
@@ -0,0 +1,140 @@
+// 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 syscall_test
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "os/signal"
+ "path/filepath"
+ "syscall"
+ "testing"
+ "time"
+)
+
+func TestMain(m *testing.M) {
+ if os.Getenv("GO_DEATHSIG_PARENT") == "1" {
+ deathSignalParent()
+ } else if os.Getenv("GO_DEATHSIG_CHILD") == "1" {
+ deathSignalChild()
+ }
+
+ os.Exit(m.Run())
+}
+
+func TestLinuxDeathSignal(t *testing.T) {
+ if os.Getuid() != 0 {
+ t.Skip("skipping root only test")
+ }
+
+ // Copy the test binary to a location that a non-root user can read/execute
+ // after we drop privileges
+ tempDir, err := ioutil.TempDir("", "TestDeathSignal")
+ if err != nil {
+ t.Fatalf("cannot create temporary directory: %v", err)
+ }
+ defer os.RemoveAll(tempDir)
+ os.Chmod(tempDir, 0755)
+
+ tmpBinary := filepath.Join(tempDir, filepath.Base(os.Args[0]))
+
+ src, err := os.Open(os.Args[0])
+ if err != nil {
+ t.Fatalf("cannot open binary %q, %v", os.Args[0], err)
+ }
+ defer src.Close()
+
+ dst, err := os.OpenFile(tmpBinary, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
+ if err != nil {
+ t.Fatalf("cannot create temporary binary %q, %v", tmpBinary, err)
+ }
+ if _, err := io.Copy(dst, src); err != nil {
+ t.Fatalf("failed to copy test binary to %q, %v", tmpBinary, err)
+ }
+ err = dst.Close()
+ if err != nil {
+ t.Fatalf("failed to close test binary %q, %v", tmpBinary, err)
+ }
+
+ cmd := exec.Command(tmpBinary)
+ cmd.Env = []string{"GO_DEATHSIG_PARENT=1"}
+ chldStdin, err := cmd.StdinPipe()
+ if err != nil {
+ t.Fatal("failed to create new stdin pipe: %v", err)
+ }
+ chldStdout, err := cmd.StdoutPipe()
+ if err != nil {
+ t.Fatal("failed to create new stdout pipe: %v", err)
+ }
+ cmd.Stderr = os.Stderr
+
+ err = cmd.Start()
+ defer cmd.Wait()
+ if err != nil {
+ t.Fatalf("failed to start first child process: %v", err)
+ }
+
+ chldPipe := bufio.NewReader(chldStdout)
+
+ if got, err := chldPipe.ReadString('\n'); got == "start\n" {
+ syscall.Kill(cmd.Process.Pid, syscall.SIGTERM)
+
+ go func() {
+ time.Sleep(5 * time.Second)
+ chldStdin.Close()
+ }()
+
+ want := "ok\n"
+ if got, err = chldPipe.ReadString('\n'); got != want {
+ t.Fatalf("expected %q, received %q, %v", want, got, err)
+ }
+ } else {
+ t.Fatalf("did not receive start from child, received %q, %v", got, err)
+ }
+}
+
+func deathSignalParent() {
+ cmd := exec.Command(os.Args[0])
+ cmd.Env = []string{"GO_DEATHSIG_CHILD=1"}
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ attrs := syscall.SysProcAttr{
+ Pdeathsig: syscall.SIGUSR1,
+ // UID/GID 99 is the user/group "nobody" on RHEL/Fedora and is
+ // unused on Ubuntu
+ Credential: &syscall.Credential{Uid: 99, Gid: 99},
+ }
+ cmd.SysProcAttr = &attrs
+
+ err := cmd.Start()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "death signal parent error: %v\n")
+ os.Exit(1)
+ }
+ cmd.Wait()
+ os.Exit(0)
+}
+
+func deathSignalChild() {
+ c := make(chan os.Signal, 1)
+ signal.Notify(c, syscall.SIGUSR1)
+ go func() {
+ <-c
+ fmt.Println("ok")
+ os.Exit(0)
+ }()
+ fmt.Println("start")
+
+ buf := make([]byte, 32)
+ os.Stdin.Read(buf)
+
+ // We expected to be signaled before stdin closed
+ fmt.Println("not ok")
+ os.Exit(1)
+}
diff --git a/src/syscall/syscall_unix_test.go b/src/syscall/syscall_unix_test.go
index 01fc670aba..af92013739 100644
--- a/src/syscall/syscall_unix_test.go
+++ b/src/syscall/syscall_unix_test.go
@@ -60,20 +60,58 @@ func _() {
// TestFcntlFlock tests whether the file locking structure matches
// the calling convention of each kernel.
+// On some Linux systems, glibc uses another set of values for the
+// commands and translates them to the correct value that the kernel
+// expects just before the actual fcntl syscall. As Go uses raw
+// syscalls directly, it must use the real value, not the glibc value.
+// Thus this test also verifies that the Flock_t structure can be
+// roundtripped with F_SETLK and F_GETLK.
func TestFcntlFlock(t *testing.T) {
- name := filepath.Join(os.TempDir(), "TestFcntlFlock")
- fd, err := syscall.Open(name, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0)
- if err != nil {
- t.Fatalf("Open failed: %v", err)
+ if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
+ t.Skip("skipping; no child processes allowed on iOS")
}
- defer syscall.Unlink(name)
- defer syscall.Close(fd)
flock := syscall.Flock_t{
- Type: syscall.F_RDLCK,
- Start: 0, Len: 0, Whence: 1,
+ Type: syscall.F_WRLCK,
+ Start: 31415, Len: 271828, Whence: 1,
}
- if err := syscall.FcntlFlock(uintptr(fd), syscall.F_GETLK, &flock); err != nil {
- t.Fatalf("FcntlFlock failed: %v", err)
+ if os.Getenv("GO_WANT_HELPER_PROCESS") == "" {
+ // parent
+ name := filepath.Join(os.TempDir(), "TestFcntlFlock")
+ fd, err := syscall.Open(name, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0)
+ if err != nil {
+ t.Fatalf("Open failed: %v", err)
+ }
+ defer syscall.Unlink(name)
+ defer syscall.Close(fd)
+ if err := syscall.Ftruncate(fd, 1<<20); err != nil {
+ t.Fatalf("Ftruncate(1<<20) failed: %v", err)
+ }
+ if err := syscall.FcntlFlock(uintptr(fd), syscall.F_SETLK, &flock); err != nil {
+ t.Fatalf("FcntlFlock(F_SETLK) failed: %v", err)
+ }
+ cmd := exec.Command(os.Args[0], "-test.run=^TestFcntlFlock$")
+ cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
+ cmd.ExtraFiles = []*os.File{os.NewFile(uintptr(fd), name)}
+ out, err := cmd.CombinedOutput()
+ if len(out) > 0 || err != nil {
+ t.Fatalf("child process: %q, %v", out, err)
+ }
+ } else {
+ // child
+ got := flock
+ // make sure the child lock is conflicting with the parent lock
+ got.Start--
+ got.Len++
+ if err := syscall.FcntlFlock(3, syscall.F_GETLK, &got); err != nil {
+ t.Fatalf("FcntlFlock(F_GETLK) failed: %v", err)
+ }
+ flock.Pid = int32(syscall.Getppid())
+ // Linux kernel always set Whence to 0
+ flock.Whence = 0
+ if got.Type == flock.Type && got.Start == flock.Start && got.Len == flock.Len && got.Pid == flock.Pid && got.Whence == flock.Whence {
+ os.Exit(0)
+ }
+ t.Fatalf("FcntlFlock got %v, want %v", got, flock)
}
}
@@ -121,7 +159,7 @@ func TestPassFD(t *testing.T) {
defer readFile.Close()
cmd := exec.Command(os.Args[0], "-test.run=^TestPassFD$", "--", tempDir)
- cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
+ cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
cmd.ExtraFiles = []*os.File{writeFile}
out, err := cmd.CombinedOutput()
diff --git a/src/syscall/zerrors_darwin_386.go b/src/syscall/zerrors_darwin_386.go
index bb3a1610c0..debadaa9ce 100644
--- a/src/syscall/zerrors_darwin_386.go
+++ b/src/syscall/zerrors_darwin_386.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -m32 _const.go
+// +build 386,darwin
+
package syscall
const (
diff --git a/src/syscall/zerrors_darwin_amd64.go b/src/syscall/zerrors_darwin_amd64.go
index 05ab48ee32..d4262ba55a 100644
--- a/src/syscall/zerrors_darwin_amd64.go
+++ b/src/syscall/zerrors_darwin_amd64.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -m64 _const.go
+// +build amd64,darwin
+
package syscall
const (
diff --git a/src/syscall/zerrors_darwin_arm.go b/src/syscall/zerrors_darwin_arm.go
index 7e800d4259..a64f3735e2 100644
--- a/src/syscall/zerrors_darwin_arm.go
+++ b/src/syscall/zerrors_darwin_arm.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- _const.go
+// +build arm,darwin
+
package syscall
const (
diff --git a/src/syscall/zerrors_darwin_arm64.go b/src/syscall/zerrors_darwin_arm64.go
index a3841a277a..98f431c4e7 100644
--- a/src/syscall/zerrors_darwin_arm64.go
+++ b/src/syscall/zerrors_darwin_arm64.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -m64 _const.go
+// +build arm64,darwin
+
package syscall
const (
diff --git a/src/syscall/zerrors_dragonfly_amd64.go b/src/syscall/zerrors_dragonfly_amd64.go
index 59bff751cb..15d300fd2d 100644
--- a/src/syscall/zerrors_dragonfly_amd64.go
+++ b/src/syscall/zerrors_dragonfly_amd64.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -m64 _const.go
+// +build amd64,dragonfly
+
package syscall
const (
diff --git a/src/syscall/zerrors_freebsd_386.go b/src/syscall/zerrors_freebsd_386.go
index cd3aa80a9c..bbad05f069 100644
--- a/src/syscall/zerrors_freebsd_386.go
+++ b/src/syscall/zerrors_freebsd_386.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -m32 _const.go
+// +build 386,freebsd
+
package syscall
const (
diff --git a/src/syscall/zerrors_freebsd_amd64.go b/src/syscall/zerrors_freebsd_amd64.go
index 9edce6e2fa..b36125b6d3 100644
--- a/src/syscall/zerrors_freebsd_amd64.go
+++ b/src/syscall/zerrors_freebsd_amd64.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -m64 _const.go
+// +build amd64,freebsd
+
package syscall
const (
diff --git a/src/syscall/zerrors_freebsd_arm.go b/src/syscall/zerrors_freebsd_arm.go
index f29dd057b6..0c844f1387 100644
--- a/src/syscall/zerrors_freebsd_arm.go
+++ b/src/syscall/zerrors_freebsd_arm.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- _const.go
+// +build arm,freebsd
+
package syscall
const (
diff --git a/src/syscall/zerrors_linux_386.go b/src/syscall/zerrors_linux_386.go
index 7aa8ff07a7..d433a4f1a4 100644
--- a/src/syscall/zerrors_linux_386.go
+++ b/src/syscall/zerrors_linux_386.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -m32 _const.go
+// +build 386,linux
+
package syscall
const (
diff --git a/src/syscall/zerrors_linux_amd64.go b/src/syscall/zerrors_linux_amd64.go
index 94d051d8aa..dd86a3b0a1 100644
--- a/src/syscall/zerrors_linux_amd64.go
+++ b/src/syscall/zerrors_linux_amd64.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -m64 _const.go
+// +build amd64,linux
+
package syscall
const (
diff --git a/src/syscall/zerrors_linux_arm.go b/src/syscall/zerrors_linux_arm.go
index dcaaef7423..2a9c0f93c1 100644
--- a/src/syscall/zerrors_linux_arm.go
+++ b/src/syscall/zerrors_linux_arm.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- _const.go
+// +build arm,linux
+
package syscall
const (
diff --git a/src/syscall/zerrors_linux_arm64.go b/src/syscall/zerrors_linux_arm64.go
index f3d708be9c..35d9acefe5 100644
--- a/src/syscall/zerrors_linux_arm64.go
+++ b/src/syscall/zerrors_linux_arm64.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- _const.go
+// +build arm64,linux
+
package syscall
const (
diff --git a/src/syscall/zerrors_linux_ppc64.go b/src/syscall/zerrors_linux_ppc64.go
index 15e0770c18..1c769cdc77 100644
--- a/src/syscall/zerrors_linux_ppc64.go
+++ b/src/syscall/zerrors_linux_ppc64.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -m64 _const.go
+// +build ppc64,linux
+
package syscall
const (
@@ -366,9 +368,9 @@ const (
F_SETFD = 0x2
F_SETFL = 0x4
F_SETLEASE = 0x400
- F_SETLK = 0xd
+ F_SETLK = 0x6
F_SETLK64 = 0xd
- F_SETLKW = 0xe
+ F_SETLKW = 0x7
F_SETLKW64 = 0xe
F_SETOWN = 0x8
F_SETOWN_EX = 0xf
diff --git a/src/syscall/zerrors_linux_ppc64le.go b/src/syscall/zerrors_linux_ppc64le.go
index fdecdf24dd..73727a4197 100644
--- a/src/syscall/zerrors_linux_ppc64le.go
+++ b/src/syscall/zerrors_linux_ppc64le.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -m64 _const.go
+// +build ppc64le,linux
+
package syscall
const (
@@ -366,9 +368,9 @@ const (
F_SETFD = 0x2
F_SETFL = 0x4
F_SETLEASE = 0x400
- F_SETLK = 0xd
+ F_SETLK = 0x6
F_SETLK64 = 0xd
- F_SETLKW = 0xe
+ F_SETLKW = 0x7
F_SETLKW64 = 0xe
F_SETOWN = 0x8
F_SETOWN_EX = 0xf
diff --git a/src/syscall/zerrors_netbsd_386.go b/src/syscall/zerrors_netbsd_386.go
index 1e3dff7fac..6f32def661 100644
--- a/src/syscall/zerrors_netbsd_386.go
+++ b/src/syscall/zerrors_netbsd_386.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -m32 _const.go
+// +build 386,netbsd
+
package syscall
const (
diff --git a/src/syscall/zerrors_netbsd_amd64.go b/src/syscall/zerrors_netbsd_amd64.go
index 1469d00b78..a6d1701f8c 100644
--- a/src/syscall/zerrors_netbsd_amd64.go
+++ b/src/syscall/zerrors_netbsd_amd64.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -m64 _const.go
+// +build amd64,netbsd
+
package syscall
const (
diff --git a/src/syscall/zerrors_netbsd_arm.go b/src/syscall/zerrors_netbsd_arm.go
index 1a88c0d225..7f99279e55 100644
--- a/src/syscall/zerrors_netbsd_arm.go
+++ b/src/syscall/zerrors_netbsd_arm.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -marm _const.go
+// +build arm,netbsd
+
package syscall
const (
diff --git a/src/syscall/zerrors_openbsd_386.go b/src/syscall/zerrors_openbsd_386.go
index 0829834943..540d310e9f 100644
--- a/src/syscall/zerrors_openbsd_386.go
+++ b/src/syscall/zerrors_openbsd_386.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -m32 _const.go
+// +build 386,openbsd
+
package syscall
const (
diff --git a/src/syscall/zerrors_openbsd_amd64.go b/src/syscall/zerrors_openbsd_amd64.go
index e9fa37cdee..ae5b8c955a 100644
--- a/src/syscall/zerrors_openbsd_amd64.go
+++ b/src/syscall/zerrors_openbsd_amd64.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -m64 _const.go
+// +build amd64,openbsd
+
package syscall
const (
diff --git a/src/syscall/zerrors_openbsd_arm.go b/src/syscall/zerrors_openbsd_arm.go
index 5376fe6e85..c49ebcfd03 100644
--- a/src/syscall/zerrors_openbsd_arm.go
+++ b/src/syscall/zerrors_openbsd_arm.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- _const.go
+// +build arm,openbsd
+
package syscall
const (
diff --git a/src/syscall/zerrors_solaris_amd64.go b/src/syscall/zerrors_solaris_amd64.go
index 3f4cbfd984..62ec81be6b 100644
--- a/src/syscall/zerrors_solaris_amd64.go
+++ b/src/syscall/zerrors_solaris_amd64.go
@@ -4,6 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -m64 _const.go
+// +build amd64,solaris
+
package syscall
const (
diff --git a/src/syscall/zsyscall_darwin_386.go b/src/syscall/zsyscall_darwin_386.go
index 0ce383fb3b..23e7b5e420 100644
--- a/src/syscall/zsyscall_darwin_386.go
+++ b/src/syscall/zsyscall_darwin_386.go
@@ -1,6 +1,8 @@
// mksyscall.pl -l32 syscall_bsd.go syscall_darwin.go syscall_darwin_386.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build 386,darwin
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_darwin_amd64.go b/src/syscall/zsyscall_darwin_amd64.go
index 2370630f09..6e63d9a074 100644
--- a/src/syscall/zsyscall_darwin_amd64.go
+++ b/src/syscall/zsyscall_darwin_amd64.go
@@ -1,6 +1,8 @@
// mksyscall.pl syscall_bsd.go syscall_darwin.go syscall_darwin_amd64.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build amd64,darwin
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_darwin_arm.go b/src/syscall/zsyscall_darwin_arm.go
index c9426ee85a..f996a508f0 100644
--- a/src/syscall/zsyscall_darwin_arm.go
+++ b/src/syscall/zsyscall_darwin_arm.go
@@ -1,6 +1,8 @@
// mksyscall.pl -l32 syscall_bsd.go syscall_darwin.go syscall_darwin_arm.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build arm,darwin
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_darwin_arm64.go b/src/syscall/zsyscall_darwin_arm64.go
index 828b167fea..c260cc7acd 100644
--- a/src/syscall/zsyscall_darwin_arm64.go
+++ b/src/syscall/zsyscall_darwin_arm64.go
@@ -1,6 +1,8 @@
// mksyscall.pl syscall_bsd.go syscall_darwin.go syscall_darwin_arm64.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build arm64,darwin
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_dragonfly_amd64.go b/src/syscall/zsyscall_dragonfly_amd64.go
index 5eef1380ff..88e09d3a14 100644
--- a/src/syscall/zsyscall_dragonfly_amd64.go
+++ b/src/syscall/zsyscall_dragonfly_amd64.go
@@ -1,6 +1,8 @@
// mksyscall.pl -dragonfly syscall_bsd.go syscall_dragonfly.go syscall_dragonfly_amd64.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build amd64,dragonfly
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_freebsd_386.go b/src/syscall/zsyscall_freebsd_386.go
index 7cb21c9b39..30f29e52a9 100644
--- a/src/syscall/zsyscall_freebsd_386.go
+++ b/src/syscall/zsyscall_freebsd_386.go
@@ -1,6 +1,8 @@
// mksyscall.pl -l32 syscall_bsd.go syscall_freebsd.go syscall_freebsd_386.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build 386,freebsd
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_freebsd_amd64.go b/src/syscall/zsyscall_freebsd_amd64.go
index 3cb87c1a02..93059d1b5b 100644
--- a/src/syscall/zsyscall_freebsd_amd64.go
+++ b/src/syscall/zsyscall_freebsd_amd64.go
@@ -1,6 +1,8 @@
// mksyscall.pl syscall_bsd.go syscall_freebsd.go syscall_freebsd_amd64.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build amd64,freebsd
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_freebsd_arm.go b/src/syscall/zsyscall_freebsd_arm.go
index 340418345b..84096b07a5 100644
--- a/src/syscall/zsyscall_freebsd_arm.go
+++ b/src/syscall/zsyscall_freebsd_arm.go
@@ -1,6 +1,8 @@
// mksyscall.pl -l32 -arm syscall_bsd.go syscall_freebsd.go syscall_freebsd_arm.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build arm,freebsd
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_linux_386.go b/src/syscall/zsyscall_linux_386.go
index e7cf7452c2..620fba287a 100644
--- a/src/syscall/zsyscall_linux_386.go
+++ b/src/syscall/zsyscall_linux_386.go
@@ -1,6 +1,8 @@
// mksyscall.pl -l32 syscall_linux.go syscall_linux_386.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build 386,linux
+
package syscall
import "unsafe"
@@ -615,17 +617,6 @@ func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err e
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func InotifyInit() (fd int, err error) {
- r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0)
- fd = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func InotifyInit1(flags int) (fd int, err error) {
r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0)
fd = int(r0)
@@ -1298,6 +1289,17 @@ func Getuid() (uid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func InotifyInit() (fd int, err error) {
+ r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0)
+ fd = int(r0)
+ if e1 != 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Ioperm(from int, num int, on int) (err error) {
_, _, e1 := Syscall(SYS_IOPERM, uintptr(from), uintptr(num), uintptr(on))
if e1 != 0 {
diff --git a/src/syscall/zsyscall_linux_amd64.go b/src/syscall/zsyscall_linux_amd64.go
index b23573bc84..16cafbf06e 100644
--- a/src/syscall/zsyscall_linux_amd64.go
+++ b/src/syscall/zsyscall_linux_amd64.go
@@ -1,6 +1,8 @@
// mksyscall.pl syscall_linux.go syscall_linux_amd64.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build amd64,linux
+
package syscall
import "unsafe"
@@ -615,17 +617,6 @@ func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err e
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func InotifyInit() (fd int, err error) {
- r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0)
- fd = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func InotifyInit1(flags int) (fd int, err error) {
r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0)
fd = int(r0)
@@ -1298,6 +1289,17 @@ func Getuid() (uid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func InotifyInit() (fd int, err error) {
+ r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0)
+ fd = int(r0)
+ if e1 != 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Ioperm(from int, num int, on int) (err error) {
_, _, e1 := Syscall(SYS_IOPERM, uintptr(from), uintptr(num), uintptr(on))
if e1 != 0 {
diff --git a/src/syscall/zsyscall_linux_arm.go b/src/syscall/zsyscall_linux_arm.go
index 054cf4005d..9bc3a54f31 100644
--- a/src/syscall/zsyscall_linux_arm.go
+++ b/src/syscall/zsyscall_linux_arm.go
@@ -1,6 +1,8 @@
// mksyscall.pl -l32 -arm syscall_linux.go syscall_linux_arm.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build arm,linux
+
package syscall
import "unsafe"
@@ -615,17 +617,6 @@ func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err e
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func InotifyInit() (fd int, err error) {
- r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0)
- fd = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func InotifyInit1(flags int) (fd int, err error) {
r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0)
fd = int(r0)
@@ -1457,6 +1448,17 @@ func Getuid() (uid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func InotifyInit() (fd int, err error) {
+ r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0)
+ fd = int(r0)
+ if e1 != 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Lchown(path string, uid int, gid int) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
diff --git a/src/syscall/zsyscall_linux_arm64.go b/src/syscall/zsyscall_linux_arm64.go
index 26a14b7244..2ee58cfc8b 100644
--- a/src/syscall/zsyscall_linux_arm64.go
+++ b/src/syscall/zsyscall_linux_arm64.go
@@ -1,6 +1,8 @@
// mksyscall.pl syscall_linux.go syscall_linux_arm64.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build arm64,linux
+
package syscall
import "unsafe"
@@ -615,17 +617,6 @@ func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err e
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func InotifyInit() (fd int, err error) {
- r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0)
- fd = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func InotifyInit1(flags int) (fd int, err error) {
r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0)
fd = int(r0)
diff --git a/src/syscall/zsyscall_linux_ppc64.go b/src/syscall/zsyscall_linux_ppc64.go
index 326218031e..4f61114d5f 100644
--- a/src/syscall/zsyscall_linux_ppc64.go
+++ b/src/syscall/zsyscall_linux_ppc64.go
@@ -1,6 +1,8 @@
// mksyscall.pl syscall_linux.go syscall_linux_ppc64x.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build ppc64,linux
+
package syscall
import "unsafe"
@@ -615,17 +617,6 @@ func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err e
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func InotifyInit() (fd int, err error) {
- r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0)
- fd = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func InotifyInit1(flags int) (fd int, err error) {
r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0)
fd = int(r0)
@@ -1298,6 +1289,17 @@ func Getuid() (uid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func InotifyInit() (fd int, err error) {
+ r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0)
+ fd = int(r0)
+ if e1 != 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Ioperm(from int, num int, on int) (err error) {
_, _, e1 := Syscall(SYS_IOPERM, uintptr(from), uintptr(num), uintptr(on))
if e1 != 0 {
diff --git a/src/syscall/zsyscall_linux_ppc64le.go b/src/syscall/zsyscall_linux_ppc64le.go
index 326218031e..4073a0fa34 100644
--- a/src/syscall/zsyscall_linux_ppc64le.go
+++ b/src/syscall/zsyscall_linux_ppc64le.go
@@ -1,6 +1,8 @@
// mksyscall.pl syscall_linux.go syscall_linux_ppc64x.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build ppc64le,linux
+
package syscall
import "unsafe"
@@ -615,17 +617,6 @@ func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err e
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func InotifyInit() (fd int, err error) {
- r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0)
- fd = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func InotifyInit1(flags int) (fd int, err error) {
r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0)
fd = int(r0)
@@ -1298,6 +1289,17 @@ func Getuid() (uid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func InotifyInit() (fd int, err error) {
+ r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0)
+ fd = int(r0)
+ if e1 != 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Ioperm(from int, num int, on int) (err error) {
_, _, e1 := Syscall(SYS_IOPERM, uintptr(from), uintptr(num), uintptr(on))
if e1 != 0 {
diff --git a/src/syscall/zsyscall_nacl_386.go b/src/syscall/zsyscall_nacl_386.go
index d0ff66c819..bf3f9e3dcd 100644
--- a/src/syscall/zsyscall_nacl_386.go
+++ b/src/syscall/zsyscall_nacl_386.go
@@ -1,6 +1,8 @@
// mksyscall.pl -l32 -nacl syscall_nacl.go syscall_nacl_386.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build 386,nacl
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_nacl_amd64p32.go b/src/syscall/zsyscall_nacl_amd64p32.go
index 592c8d84a5..3f08da6e77 100644
--- a/src/syscall/zsyscall_nacl_amd64p32.go
+++ b/src/syscall/zsyscall_nacl_amd64p32.go
@@ -1,6 +1,8 @@
// mksyscall.pl -nacl syscall_nacl.go syscall_nacl_amd64p32.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build amd64p32,nacl
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_nacl_arm.go b/src/syscall/zsyscall_nacl_arm.go
index 10657ad080..77d46c3d81 100644
--- a/src/syscall/zsyscall_nacl_arm.go
+++ b/src/syscall/zsyscall_nacl_arm.go
@@ -1,6 +1,8 @@
// mksyscall.pl -l32 -nacl -arm syscall_nacl.go syscall_nacl_arm.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build arm,nacl
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_netbsd_386.go b/src/syscall/zsyscall_netbsd_386.go
index 5131c25696..e24c3b71cd 100644
--- a/src/syscall/zsyscall_netbsd_386.go
+++ b/src/syscall/zsyscall_netbsd_386.go
@@ -1,6 +1,8 @@
// mksyscall.pl -l32 -netbsd syscall_bsd.go syscall_netbsd.go syscall_netbsd_386.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build 386,netbsd
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_netbsd_amd64.go b/src/syscall/zsyscall_netbsd_amd64.go
index 0e26f6869d..7aa75ab12d 100644
--- a/src/syscall/zsyscall_netbsd_amd64.go
+++ b/src/syscall/zsyscall_netbsd_amd64.go
@@ -1,6 +1,8 @@
// mksyscall.pl -netbsd syscall_bsd.go syscall_netbsd.go syscall_netbsd_amd64.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build amd64,netbsd
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_netbsd_arm.go b/src/syscall/zsyscall_netbsd_arm.go
index 4aae1040ad..21f482b40f 100644
--- a/src/syscall/zsyscall_netbsd_arm.go
+++ b/src/syscall/zsyscall_netbsd_arm.go
@@ -1,6 +1,8 @@
// mksyscall.pl -l32 -arm syscall_bsd.go syscall_netbsd.go syscall_netbsd_arm.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build arm,netbsd
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_openbsd_386.go b/src/syscall/zsyscall_openbsd_386.go
index f6a19833ec..df7df1e7e4 100644
--- a/src/syscall/zsyscall_openbsd_386.go
+++ b/src/syscall/zsyscall_openbsd_386.go
@@ -1,6 +1,8 @@
// mksyscall.pl -l32 -openbsd syscall_bsd.go syscall_openbsd.go syscall_openbsd_386.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build 386,openbsd
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_openbsd_amd64.go b/src/syscall/zsyscall_openbsd_amd64.go
index 93f5fc0085..1d640700f7 100644
--- a/src/syscall/zsyscall_openbsd_amd64.go
+++ b/src/syscall/zsyscall_openbsd_amd64.go
@@ -1,6 +1,8 @@
// mksyscall.pl -openbsd syscall_bsd.go syscall_openbsd.go syscall_openbsd_amd64.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build amd64,openbsd
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_openbsd_arm.go b/src/syscall/zsyscall_openbsd_arm.go
index f59739c1e0..f40fb31c98 100644
--- a/src/syscall/zsyscall_openbsd_arm.go
+++ b/src/syscall/zsyscall_openbsd_arm.go
@@ -1,6 +1,8 @@
// mksyscall.pl -l32 -openbsd -arm syscall_bsd.go syscall_openbsd.go syscall_openbsd_arm.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build arm,openbsd
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_plan9_386.go b/src/syscall/zsyscall_plan9_386.go
index 06f1f04114..a424e789e3 100644
--- a/src/syscall/zsyscall_plan9_386.go
+++ b/src/syscall/zsyscall_plan9_386.go
@@ -1,6 +1,8 @@
// mksyscall.pl -l32 -plan9 syscall_plan9.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build 386,plan9
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_plan9_amd64.go b/src/syscall/zsyscall_plan9_amd64.go
index 06f1f04114..d58556b01a 100644
--- a/src/syscall/zsyscall_plan9_amd64.go
+++ b/src/syscall/zsyscall_plan9_amd64.go
@@ -1,6 +1,8 @@
// mksyscall.pl -l32 -plan9 syscall_plan9.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build amd64,plan9
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsyscall_solaris_amd64.go b/src/syscall/zsyscall_solaris_amd64.go
index be9bc28474..cabab7ece3 100644
--- a/src/syscall/zsyscall_solaris_amd64.go
+++ b/src/syscall/zsyscall_solaris_amd64.go
@@ -1,6 +1,8 @@
// mksyscall_solaris.pl syscall_solaris.go syscall_solaris_amd64.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// +build amd64,solaris
+
package syscall
import "unsafe"
diff --git a/src/syscall/zsysnum_darwin_386.go b/src/syscall/zsysnum_darwin_386.go
index abdef77436..c6f8342841 100644
--- a/src/syscall/zsysnum_darwin_386.go
+++ b/src/syscall/zsysnum_darwin_386.go
@@ -1,6 +1,8 @@
// mksysnum_darwin.pl /usr/include/sys/syscall.h
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build 386,darwin
+
package syscall
const (
diff --git a/src/syscall/zsysnum_darwin_amd64.go b/src/syscall/zsysnum_darwin_amd64.go
index abdef77436..7189abe3db 100644
--- a/src/syscall/zsysnum_darwin_amd64.go
+++ b/src/syscall/zsysnum_darwin_amd64.go
@@ -1,6 +1,8 @@
// mksysnum_darwin.pl /usr/include/sys/syscall.h
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build amd64,darwin
+
package syscall
const (
diff --git a/src/syscall/zsysnum_darwin_arm.go b/src/syscall/zsysnum_darwin_arm.go
index 1a53f13eff..1d76861204 100644
--- a/src/syscall/zsysnum_darwin_arm.go
+++ b/src/syscall/zsysnum_darwin_arm.go
@@ -1,6 +1,8 @@
// mksysnum_darwin.pl /usr/include/sys/syscall.h
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build arm,darwin
+
package syscall
const (
diff --git a/src/syscall/zsysnum_darwin_arm64.go b/src/syscall/zsysnum_darwin_arm64.go
index 723b1aa19e..ddf8e83c87 100644
--- a/src/syscall/zsysnum_darwin_arm64.go
+++ b/src/syscall/zsysnum_darwin_arm64.go
@@ -1,6 +1,8 @@
// mksysnum_darwin.pl /usr/include/sys/syscall.h
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build arm64,darwin
+
package syscall
const (
diff --git a/src/syscall/zsysnum_dragonfly_amd64.go b/src/syscall/zsysnum_dragonfly_amd64.go
index 4b086b9214..277478d208 100644
--- a/src/syscall/zsysnum_dragonfly_amd64.go
+++ b/src/syscall/zsysnum_dragonfly_amd64.go
@@ -1,6 +1,8 @@
// mksysnum_dragonfly.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build amd64,dragonfly
+
package syscall
const (
diff --git a/src/syscall/zsysnum_freebsd_386.go b/src/syscall/zsysnum_freebsd_386.go
index dfca558bb5..5e47217957 100644
--- a/src/syscall/zsysnum_freebsd_386.go
+++ b/src/syscall/zsysnum_freebsd_386.go
@@ -1,6 +1,8 @@
// mksysnum_freebsd.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build 386,freebsd
+
package syscall
const (
diff --git a/src/syscall/zsysnum_freebsd_amd64.go b/src/syscall/zsysnum_freebsd_amd64.go
index dfca558bb5..df8928cc68 100644
--- a/src/syscall/zsysnum_freebsd_amd64.go
+++ b/src/syscall/zsysnum_freebsd_amd64.go
@@ -1,6 +1,8 @@
// mksysnum_freebsd.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build amd64,freebsd
+
package syscall
const (
diff --git a/src/syscall/zsysnum_freebsd_arm.go b/src/syscall/zsysnum_freebsd_arm.go
index dfca558bb5..f670a59179 100644
--- a/src/syscall/zsysnum_freebsd_arm.go
+++ b/src/syscall/zsysnum_freebsd_arm.go
@@ -1,6 +1,8 @@
// mksysnum_freebsd.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build arm,freebsd
+
package syscall
const (
diff --git a/src/syscall/zsysnum_linux_386.go b/src/syscall/zsysnum_linux_386.go
index c40b5f1ace..c277ed9a25 100644
--- a/src/syscall/zsysnum_linux_386.go
+++ b/src/syscall/zsysnum_linux_386.go
@@ -1,6 +1,8 @@
// mksysnum_linux.pl /usr/include/asm/unistd_32.h
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build 386,linux
+
package syscall
const (
diff --git a/src/syscall/zsysnum_linux_amd64.go b/src/syscall/zsysnum_linux_amd64.go
index 7cf70a4d86..978a4d3c3d 100644
--- a/src/syscall/zsysnum_linux_amd64.go
+++ b/src/syscall/zsysnum_linux_amd64.go
@@ -1,6 +1,8 @@
// mksysnum_linux.pl /usr/include/asm/unistd_64.h
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build amd64,linux
+
package syscall
const (
diff --git a/src/syscall/zsysnum_linux_arm.go b/src/syscall/zsysnum_linux_arm.go
index 7068e4e210..5061cbab2b 100644
--- a/src/syscall/zsysnum_linux_arm.go
+++ b/src/syscall/zsysnum_linux_arm.go
@@ -1,6 +1,8 @@
// mksysnum_linux.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build arm,linux
+
package syscall
const (
diff --git a/src/syscall/zsysnum_linux_arm64.go b/src/syscall/zsysnum_linux_arm64.go
index 4bcf0573e7..d53712c681 100644
--- a/src/syscall/zsysnum_linux_arm64.go
+++ b/src/syscall/zsysnum_linux_arm64.go
@@ -1,6 +1,8 @@
// mksysnum_linux.pl /usr/include/asm-generic/unistd.h
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build arm64,linux
+
package syscall
const (
diff --git a/src/syscall/zsysnum_linux_ppc64.go b/src/syscall/zsysnum_linux_ppc64.go
index 0567fd0ea8..82d253a1ab 100644
--- a/src/syscall/zsysnum_linux_ppc64.go
+++ b/src/syscall/zsysnum_linux_ppc64.go
@@ -1,6 +1,8 @@
// mksysnum_linux.pl /usr/include/asm/unistd.h
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build ppc64,linux
+
package syscall
const (
diff --git a/src/syscall/zsysnum_linux_ppc64le.go b/src/syscall/zsysnum_linux_ppc64le.go
index 52c63e3d3e..3af4e83822 100644
--- a/src/syscall/zsysnum_linux_ppc64le.go
+++ b/src/syscall/zsysnum_linux_ppc64le.go
@@ -1,6 +1,8 @@
// mksysnum_linux.pl /usr/include/powerpc64le-linux-gnu/asm/unistd.h
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build ppc64le,linux
+
package syscall
const (
diff --git a/src/syscall/zsysnum_netbsd_386.go b/src/syscall/zsysnum_netbsd_386.go
index c570965237..c8af210082 100644
--- a/src/syscall/zsysnum_netbsd_386.go
+++ b/src/syscall/zsysnum_netbsd_386.go
@@ -1,6 +1,8 @@
// mksysnum_netbsd.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build 386,netbsd
+
package syscall
const (
diff --git a/src/syscall/zsysnum_netbsd_amd64.go b/src/syscall/zsysnum_netbsd_amd64.go
index c570965237..e342a3ce58 100644
--- a/src/syscall/zsysnum_netbsd_amd64.go
+++ b/src/syscall/zsysnum_netbsd_amd64.go
@@ -1,6 +1,8 @@
// mksysnum_netbsd.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build amd64,netbsd
+
package syscall
const (
diff --git a/src/syscall/zsysnum_netbsd_arm.go b/src/syscall/zsysnum_netbsd_arm.go
index c570965237..1f5b569b8f 100644
--- a/src/syscall/zsysnum_netbsd_arm.go
+++ b/src/syscall/zsysnum_netbsd_arm.go
@@ -1,6 +1,8 @@
// mksysnum_netbsd.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build arm,netbsd
+
package syscall
const (
diff --git a/src/syscall/zsysnum_openbsd_386.go b/src/syscall/zsysnum_openbsd_386.go
index 3b9ac4c941..c19f6de649 100644
--- a/src/syscall/zsysnum_openbsd_386.go
+++ b/src/syscall/zsysnum_openbsd_386.go
@@ -1,6 +1,8 @@
// mksysnum_openbsd.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build 386,openbsd
+
package syscall
const (
diff --git a/src/syscall/zsysnum_openbsd_amd64.go b/src/syscall/zsysnum_openbsd_amd64.go
index 3b9ac4c941..86e04cd47e 100644
--- a/src/syscall/zsysnum_openbsd_amd64.go
+++ b/src/syscall/zsysnum_openbsd_amd64.go
@@ -1,6 +1,8 @@
// mksysnum_openbsd.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build amd64,openbsd
+
package syscall
const (
diff --git a/src/syscall/zsysnum_openbsd_arm.go b/src/syscall/zsysnum_openbsd_arm.go
index 8457c14732..38b43caba6 100644
--- a/src/syscall/zsysnum_openbsd_arm.go
+++ b/src/syscall/zsysnum_openbsd_arm.go
@@ -1,6 +1,8 @@
// mksysnum_openbsd.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+// +build arm,openbsd
+
package syscall
const (
diff --git a/src/syscall/zsysnum_solaris_amd64.go b/src/syscall/zsysnum_solaris_amd64.go
index 43b3d8b40d..be198f899b 100644
--- a/src/syscall/zsysnum_solaris_amd64.go
+++ b/src/syscall/zsysnum_solaris_amd64.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build amd64,solaris
+
package syscall
// TODO(aram): remove these before Go 1.3.
diff --git a/src/syscall/ztypes_darwin_386.go b/src/syscall/ztypes_darwin_386.go
index 13724c3cc6..7298d0243d 100644
--- a/src/syscall/ztypes_darwin_386.go
+++ b/src/syscall/ztypes_darwin_386.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_darwin.go
+// +build 386,darwin
+
package syscall
const (
diff --git a/src/syscall/ztypes_darwin_amd64.go b/src/syscall/ztypes_darwin_amd64.go
index 65b02ae4f5..ec95d51f91 100644
--- a/src/syscall/ztypes_darwin_amd64.go
+++ b/src/syscall/ztypes_darwin_amd64.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_darwin.go
+// +build amd64,darwin
+
package syscall
const (
diff --git a/src/syscall/ztypes_darwin_arm.go b/src/syscall/ztypes_darwin_arm.go
index ec87f54fb2..91c4470e7c 100644
--- a/src/syscall/ztypes_darwin_arm.go
+++ b/src/syscall/ztypes_darwin_arm.go
@@ -2,6 +2,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_darwin.go
+// +build arm,darwin
+
package syscall
const (
diff --git a/src/syscall/ztypes_darwin_arm64.go b/src/syscall/ztypes_darwin_arm64.go
index 65b02ae4f5..1d65cfde9d 100644
--- a/src/syscall/ztypes_darwin_arm64.go
+++ b/src/syscall/ztypes_darwin_arm64.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_darwin.go
+// +build arm64,darwin
+
package syscall
const (
diff --git a/src/syscall/ztypes_dragonfly_amd64.go b/src/syscall/ztypes_dragonfly_amd64.go
index 954ffd7ab2..00120d0a27 100644
--- a/src/syscall/ztypes_dragonfly_amd64.go
+++ b/src/syscall/ztypes_dragonfly_amd64.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_dragonfly.go
+// +build amd64,dragonfly
+
package syscall
const (
diff --git a/src/syscall/ztypes_freebsd_386.go b/src/syscall/ztypes_freebsd_386.go
index b809eea37a..d972fb6bdf 100644
--- a/src/syscall/ztypes_freebsd_386.go
+++ b/src/syscall/ztypes_freebsd_386.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_freebsd.go
+// +build 386,freebsd
+
package syscall
const (
diff --git a/src/syscall/ztypes_freebsd_amd64.go b/src/syscall/ztypes_freebsd_amd64.go
index a05908aed1..0a5a10bf7d 100644
--- a/src/syscall/ztypes_freebsd_amd64.go
+++ b/src/syscall/ztypes_freebsd_amd64.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_freebsd.go
+// +build amd64,freebsd
+
package syscall
const (
diff --git a/src/syscall/ztypes_freebsd_arm.go b/src/syscall/ztypes_freebsd_arm.go
index 9303816f91..5d7acd547b 100644
--- a/src/syscall/ztypes_freebsd_arm.go
+++ b/src/syscall/ztypes_freebsd_arm.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -fsigned-char types_freebsd.go
+// +build arm,freebsd
+
package syscall
const (
diff --git a/src/syscall/ztypes_linux_386.go b/src/syscall/ztypes_linux_386.go
index a887f31427..dd198cb394 100644
--- a/src/syscall/ztypes_linux_386.go
+++ b/src/syscall/ztypes_linux_386.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_linux.go
+// +build 386,linux
+
package syscall
const (
diff --git a/src/syscall/ztypes_linux_amd64.go b/src/syscall/ztypes_linux_amd64.go
index adf95caee7..a39489e4e5 100644
--- a/src/syscall/ztypes_linux_amd64.go
+++ b/src/syscall/ztypes_linux_amd64.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_linux.go
+// +build amd64,linux
+
package syscall
const (
diff --git a/src/syscall/ztypes_linux_arm.go b/src/syscall/ztypes_linux_arm.go
index 1ae9718945..f446e41be0 100644
--- a/src/syscall/ztypes_linux_arm.go
+++ b/src/syscall/ztypes_linux_arm.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_linux.go
+// +build arm,linux
+
package syscall
const (
diff --git a/src/syscall/ztypes_linux_arm64.go b/src/syscall/ztypes_linux_arm64.go
index 1cb1d435d8..dcb1178dc4 100644
--- a/src/syscall/ztypes_linux_arm64.go
+++ b/src/syscall/ztypes_linux_arm64.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -fsigned-char types_linux.go
+// +build arm64,linux
+
package syscall
const (
diff --git a/src/syscall/ztypes_linux_ppc64.go b/src/syscall/ztypes_linux_ppc64.go
index fe438364d4..33d1b7f3e5 100644
--- a/src/syscall/ztypes_linux_ppc64.go
+++ b/src/syscall/ztypes_linux_ppc64.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_linux.go
+// +build ppc64,linux
+
package syscall
const (
@@ -549,7 +551,7 @@ type Sysinfo_t struct {
Totalhigh uint64
Freehigh uint64
Unit uint32
- X_f [0]byte
+ X_f [0]uint8
Pad_cgo_1 [4]byte
}
diff --git a/src/syscall/ztypes_linux_ppc64le.go b/src/syscall/ztypes_linux_ppc64le.go
index c6b6f1615d..27ca004834 100644
--- a/src/syscall/ztypes_linux_ppc64le.go
+++ b/src/syscall/ztypes_linux_ppc64le.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_linux.go
+// +build ppc64le,linux
+
package syscall
const (
@@ -549,7 +551,7 @@ type Sysinfo_t struct {
Totalhigh uint64
Freehigh uint64
Unit uint32
- X_f [0]byte
+ X_f [0]uint8
Pad_cgo_1 [4]byte
}
diff --git a/src/syscall/ztypes_netbsd_386.go b/src/syscall/ztypes_netbsd_386.go
index 6add325a37..1752c6c229 100644
--- a/src/syscall/ztypes_netbsd_386.go
+++ b/src/syscall/ztypes_netbsd_386.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_netbsd.go
+// +build 386,netbsd
+
package syscall
const (
diff --git a/src/syscall/ztypes_netbsd_amd64.go b/src/syscall/ztypes_netbsd_amd64.go
index 4451fc1f02..b8d4b0b02a 100644
--- a/src/syscall/ztypes_netbsd_amd64.go
+++ b/src/syscall/ztypes_netbsd_amd64.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_netbsd.go
+// +build amd64,netbsd
+
package syscall
const (
diff --git a/src/syscall/ztypes_netbsd_arm.go b/src/syscall/ztypes_netbsd_arm.go
index 4e853eaa23..c21d875a93 100644
--- a/src/syscall/ztypes_netbsd_arm.go
+++ b/src/syscall/ztypes_netbsd_arm.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_netbsd.go
+// +build arm,netbsd
+
package syscall
const (
diff --git a/src/syscall/ztypes_openbsd_386.go b/src/syscall/ztypes_openbsd_386.go
index 2e4d9dd174..0376d3acab 100644
--- a/src/syscall/ztypes_openbsd_386.go
+++ b/src/syscall/ztypes_openbsd_386.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_openbsd.go
+// +build 386,openbsd
+
package syscall
const (
diff --git a/src/syscall/ztypes_openbsd_amd64.go b/src/syscall/ztypes_openbsd_amd64.go
index f07bc714e9..bf23626ff2 100644
--- a/src/syscall/ztypes_openbsd_amd64.go
+++ b/src/syscall/ztypes_openbsd_amd64.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_openbsd.go
+// +build amd64,openbsd
+
package syscall
const (
diff --git a/src/syscall/ztypes_openbsd_arm.go b/src/syscall/ztypes_openbsd_arm.go
index 5f8fb1262f..e1d8938f0e 100644
--- a/src/syscall/ztypes_openbsd_arm.go
+++ b/src/syscall/ztypes_openbsd_arm.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_openbsd.go
+// +build arm,openbsd
+
package syscall
const (
diff --git a/src/syscall/ztypes_solaris_amd64.go b/src/syscall/ztypes_solaris_amd64.go
index 77275a54e3..2471519ade 100644
--- a/src/syscall/ztypes_solaris_amd64.go
+++ b/src/syscall/ztypes_solaris_amd64.go
@@ -1,6 +1,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_solaris.go
+// +build amd64,solaris
+
package syscall
const (
diff --git a/src/testing/example.go b/src/testing/example.go
index 61339a6465..30baf27030 100644
--- a/src/testing/example.go
+++ b/src/testing/example.go
@@ -43,7 +43,7 @@ func RunExamples(matchString func(pat, str string) (bool, error), examples []Int
func runExample(eg InternalExample) (ok bool) {
if *chatty {
- fmt.Printf("=== RUN: %s\n", eg.Name)
+ fmt.Printf("=== RUN %s\n", eg.Name)
}
// Capture stdout.
diff --git a/src/testing/testing.go b/src/testing/testing.go
index 280d76a1aa..2a1c45f768 100644
--- a/src/testing/testing.go
+++ b/src/testing/testing.go
@@ -44,7 +44,7 @@
// }
//
// The benchmark function must run the target code b.N times.
-// During benchark execution, b.N is adjusted until the benchmark function lasts
+// During benchmark execution, b.N is adjusted until the benchmark function lasts
// long enough to be timed reliably. The output
// BenchmarkHello 10000000 282 ns/op
// means that the loop ran 10000000 times at a speed of 282 ns per loop.
@@ -557,7 +557,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT
}
t.self = t
if *chatty {
- fmt.Printf("=== RUN %s\n", t.name)
+ fmt.Printf("=== RUN %s\n", t.name)
}
go tRunner(t, &tests[i])
out := (<-t.signal).(*T)
diff --git a/src/text/scanner/scanner.go b/src/text/scanner/scanner.go
index d3eadfd7e1..eacc0a2245 100644
--- a/src/text/scanner/scanner.go
+++ b/src/text/scanner/scanner.go
@@ -314,7 +314,9 @@ func (s *Scanner) Next() rune {
s.tokPos = -1 // don't collect token text
s.Line = 0 // invalidate token position
ch := s.Peek()
- s.ch = s.next()
+ if ch != EOF {
+ s.ch = s.next()
+ }
return ch
}
@@ -597,6 +599,8 @@ redo:
}
default:
switch ch {
+ case EOF:
+ break
case '"':
if s.Mode&ScanStrings != 0 {
s.scanString('"')
diff --git a/src/text/scanner/scanner_test.go b/src/text/scanner/scanner_test.go
index aca17b1b27..798bed7e92 100644
--- a/src/text/scanner/scanner_test.go
+++ b/src/text/scanner/scanner_test.go
@@ -619,13 +619,12 @@ func TestPos(t *testing.T) {
type countReader int
-func (c *countReader) Read([]byte) (int, error) {
- *c++
-
+func (r *countReader) Read([]byte) (int, error) {
+ *r++
return 0, io.EOF
}
-func TestPeekEOFHandling(t *testing.T) {
+func TestNextEOFHandling(t *testing.T) {
var r countReader
// corner case: empty source
@@ -633,15 +632,36 @@ func TestPeekEOFHandling(t *testing.T) {
tok := s.Next()
if tok != EOF {
- t.Errorf("EOF not reported")
+ t.Error("1) EOF not reported")
}
tok = s.Peek()
if tok != EOF {
- t.Errorf("EOF not reported")
+ t.Error("2) EOF not reported")
}
- if r != 2 {
- t.Errorf("scanner called Read %d times, not twice", r)
+ if r != 1 {
+ t.Errorf("scanner called Read %d times, not once", r)
+ }
+}
+
+func TestScanEOFHandling(t *testing.T) {
+ var r countReader
+
+ // corner case: empty source
+ s := new(Scanner).Init(&r)
+
+ tok := s.Scan()
+ if tok != EOF {
+ t.Error("1) EOF not reported")
+ }
+
+ tok = s.Peek()
+ if tok != EOF {
+ t.Error("2) EOF not reported")
+ }
+
+ if r != 1 {
+ t.Errorf("scanner called Read %d times, not once", r)
}
}
diff --git a/src/text/template/exec.go b/src/text/template/exec.go
index e6e1287993..ebafb4b5dc 100644
--- a/src/text/template/exec.go
+++ b/src/text/template/exec.go
@@ -660,7 +660,7 @@ func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) refle
case *parse.PipeNode:
return s.validateType(s.evalPipeline(dot, arg), typ)
case *parse.IdentifierNode:
- return s.evalFunction(dot, arg, arg, nil, zero)
+ return s.validateType(s.evalFunction(dot, arg, arg, nil, zero), typ)
case *parse.ChainNode:
return s.validateType(s.evalChainNode(dot, arg, nil, zero), typ)
}
diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go
index abce27ff3d..0f1ad62380 100644
--- a/src/text/template/exec_test.go
+++ b/src/text/template/exec_test.go
@@ -531,6 +531,8 @@ var execTests = []execTest{
{"bug14a", "{{(nil).True}}", "", tVal, false},
{"bug14b", "{{$x := nil}}{{$x.anything}}", "", tVal, false},
{"bug14c", `{{$x := (1.0)}}{{$y := ("hello")}}{{$x.anything}}{{$y.true}}`, "", tVal, false},
+ // Didn't call validateType on function results. Issue 10800.
+ {"bug15", "{{valueString returnInt}}", "", tVal, false},
}
func zeroArgs() string {
@@ -570,6 +572,11 @@ func valueString(v string) string {
return "value is ignored"
}
+// returnInt returns an int
+func returnInt() int {
+ return 7
+}
+
func add(args ...int) int {
sum := 0
for _, x := range args {
@@ -611,6 +618,7 @@ func testExecute(execTests []execTest, template *Template, t *testing.T) {
"makemap": makemap,
"mapOfThree": mapOfThree,
"oneArg": oneArg,
+ "returnInt": returnInt,
"stringer": stringer,
"typeOf": typeOf,
"valueString": valueString,
diff --git a/src/text/template/funcs.go b/src/text/template/funcs.go
index cdd187bda2..ccd0dfc80d 100644
--- a/src/text/template/funcs.go
+++ b/src/text/template/funcs.go
@@ -92,6 +92,8 @@ func goodFunc(typ reflect.Type) bool {
// findFunction looks for a function in the template, and global map.
func findFunction(name string, tmpl *Template) (reflect.Value, bool) {
if tmpl != nil && tmpl.common != nil {
+ tmpl.muFuncs.RLock()
+ defer tmpl.muFuncs.RUnlock()
if fn := tmpl.execFuncs[name]; fn.IsValid() {
return fn, true
}
diff --git a/src/text/template/template.go b/src/text/template/template.go
index 8611faad9f..a7c5c8cd2c 100644
--- a/src/text/template/template.go
+++ b/src/text/template/template.go
@@ -7,18 +7,20 @@ package template
import (
"fmt"
"reflect"
+ "sync"
"text/template/parse"
)
// common holds the information shared by related templates.
type common struct {
- tmpl map[string]*Template
+ tmpl map[string]*Template
+ option option
// We use two maps, one for parsing and one for execution.
// This separation makes the API cleaner since it doesn't
// expose reflection to the client.
+ muFuncs sync.RWMutex // protects parseFuncs and execFuncs
parseFuncs FuncMap
execFuncs map[string]reflect.Value
- option option
}
// Template is the representation of a parsed template. The *parse.Tree
@@ -84,6 +86,8 @@ func (t *Template) Clone() (*Template, error) {
tmpl := v.copy(nt.common)
nt.tmpl[k] = tmpl
}
+ t.muFuncs.RLock()
+ defer t.muFuncs.RUnlock()
for k, v := range t.parseFuncs {
nt.parseFuncs[k] = v
}
@@ -146,6 +150,8 @@ func (t *Template) Delims(left, right string) *Template {
// value is the template, so calls can be chained.
func (t *Template) Funcs(funcMap FuncMap) *Template {
t.init()
+ t.muFuncs.Lock()
+ defer t.muFuncs.Unlock()
addValueFuncs(t.execFuncs, funcMap)
addFuncs(t.parseFuncs, funcMap)
return t
@@ -169,7 +175,9 @@ func (t *Template) Lookup(name string) *Template {
// can contain text other than space, comments, and template definitions.)
func (t *Template) Parse(text string) (*Template, error) {
t.init()
+ t.muFuncs.RLock()
trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins)
+ t.muFuncs.RUnlock()
if err != nil {
return nil, err
}
diff --git a/src/time/export_windows_test.go b/src/time/export_windows_test.go
index 7e689b829f..6fd4509137 100644
--- a/src/time/export_windows_test.go
+++ b/src/time/export_windows_test.go
@@ -8,3 +8,7 @@ func ForceAusForTesting() {
ResetLocalOnceForTest()
localOnce.Do(initAusTestingZone)
}
+
+func ToEnglishName(stdname, dstname string) (string, error) {
+ return toEnglishName(stdname, dstname)
+}
diff --git a/src/time/time.go b/src/time/time.go
index 0300e846a4..fbf3f8d3c8 100644
--- a/src/time/time.go
+++ b/src/time/time.go
@@ -966,6 +966,8 @@ func (t *Time) UnmarshalText(data []byte) (err error) {
// Unix returns the local Time corresponding to the given Unix time,
// sec seconds and nsec nanoseconds since January 1, 1970 UTC.
// It is valid to pass nsec outside the range [0, 999999999].
+// Not all sec values have a corresponding time value. Notable such
+// values are -1<<63 and 1<<63-1.
func Unix(sec int64, nsec int64) Time {
if nsec < 0 || nsec >= 1e9 {
n := nsec / 1e9
diff --git a/src/time/zoneinfo_windows.go b/src/time/zoneinfo_windows.go
index 9f987ab302..d04ebec614 100644
--- a/src/time/zoneinfo_windows.go
+++ b/src/time/zoneinfo_windows.go
@@ -49,7 +49,7 @@ func matchZoneKey(zones registry.Key, kname string, stdname, dstname string) (ma
// toEnglishName searches the registry for an English name of a time zone
// whose zone names are stdname and dstname and returns the English name.
func toEnglishName(stdname, dstname string) (string, error) {
- k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`, registry.ENUMERATE_SUB_KEYS)
+ k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`, registry.ENUMERATE_SUB_KEYS|registry.QUERY_VALUE)
if err != nil {
return "", err
}
diff --git a/src/time/zoneinfo_windows_test.go b/src/time/zoneinfo_windows_test.go
index 9db81b7cfd..5f1141d3ca 100644
--- a/src/time/zoneinfo_windows_test.go
+++ b/src/time/zoneinfo_windows_test.go
@@ -5,6 +5,7 @@
package time_test
import (
+ "internal/syscall/windows/registry"
"testing"
. "time"
)
@@ -33,3 +34,27 @@ func TestAusZoneAbbr(t *testing.T) {
defer ForceUSPacificForTesting()
testZoneAbbr(t)
}
+
+func TestToEnglishName(t *testing.T) {
+ const want = "Central Europe Standard Time"
+ k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\`+want, registry.READ)
+ if err != nil {
+ t.Fatalf("cannot open CEST time zone information from registry: %s", err)
+ }
+ defer k.Close()
+ std, _, err := k.GetStringValue("Std")
+ if err != nil {
+ t.Fatalf("cannot read CEST Std registry key: %s", err)
+ }
+ dlt, _, err := k.GetStringValue("Dlt")
+ if err != nil {
+ t.Fatalf("cannot read CEST Dlt registry key: %s", err)
+ }
+ name, err := ToEnglishName(std, dlt)
+ if err != nil {
+ t.Fatalf("toEnglishName failed: %s", err)
+ }
+ if name != want {
+ t.Fatalf("english name: %q, want: %q", name, want)
+ }
+}
diff --git a/test/bench/shootout/timing.sh b/test/bench/shootout/timing.sh
index a06c326c3e..d8b1486480 100755
--- a/test/bench/shootout/timing.sh
+++ b/test/bench/shootout/timing.sh
@@ -6,9 +6,8 @@
set -e
eval $(go tool dist env)
-O=$GOCHAR
-GC="go tool ${O}g"
-LD="go tool ${O}l"
+GC="go tool compile"
+LD="go tool link"
gccm=""
case "$O" in
@@ -61,11 +60,11 @@ X-test)
esac
gc() {
- $GC $1.go; $LD -o $O.$EXE $1.$O
+ $GC $1.go; $LD -o a.$EXE $1.o
}
gc_B() {
- $GC -B $1.go; $LD -o $O.$EXE $1.$O
+ $GC -B $1.go; $LD -o a.$EXE $1.o
}
runonly() {
@@ -115,8 +114,8 @@ fasta() {
runonly echo 'fasta -n 25000000'
run "gcc $gccm -O2 fasta.c" a.$EXE 25000000
run 'gccgo -O2 fasta.go' a.$EXE -n 25000000 #commented out until WriteString is in bufio
- run 'gc fasta' $O.$EXE -n 25000000
- run 'gc_B fasta' $O.$EXE -n 25000000
+ run 'gc fasta' a.$EXE -n 25000000
+ run 'gc_B fasta' a.$EXE -n 25000000
}
revcomp() {
@@ -125,8 +124,8 @@ revcomp() {
runonly echo 'reverse-complement < output-of-fasta-25000000'
run "gcc $gccm -O2 reverse-complement.c" a.$EXE < x
run 'gccgo -O2 reverse-complement.go' a.$EXE < x
- run 'gc reverse-complement' $O.$EXE < x
- run 'gc_B reverse-complement' $O.$EXE < x
+ run 'gc reverse-complement' a.$EXE < x
+ run 'gc_B reverse-complement' a.$EXE < x
rm x
}
@@ -134,8 +133,8 @@ nbody() {
runonly echo 'nbody -n 50000000'
run "gcc $gccm -O2 nbody.c -lm" a.$EXE 50000000
run 'gccgo -O2 nbody.go' a.$EXE -n 50000000
- run 'gc nbody' $O.$EXE -n 50000000
- run 'gc_B nbody' $O.$EXE -n 50000000
+ run 'gc nbody' a.$EXE -n 50000000
+ run 'gc_B nbody' a.$EXE -n 50000000
}
binarytree() {
@@ -143,8 +142,8 @@ binarytree() {
run "gcc $gccm -O2 binary-tree.c -lm" a.$EXE 15
run 'gccgo -O2 binary-tree.go' a.$EXE -n 15
run 'gccgo -O2 binary-tree-freelist.go' a.$EXE -n 15
- run 'gc binary-tree' $O.$EXE -n 15
- run 'gc binary-tree-freelist' $O.$EXE -n 15
+ run 'gc binary-tree' a.$EXE -n 15
+ run 'gc binary-tree-freelist' a.$EXE -n 15
}
fannkuch() {
@@ -152,9 +151,9 @@ fannkuch() {
run "gcc $gccm -O2 fannkuch.c" a.$EXE 12
run 'gccgo -O2 fannkuch.go' a.$EXE -n 12
run 'gccgo -O2 fannkuch-parallel.go' a.$EXE -n 12
- run 'gc fannkuch' $O.$EXE -n 12
- run 'gc fannkuch-parallel' $O.$EXE -n 12
- run 'gc_B fannkuch' $O.$EXE -n 12
+ run 'gc fannkuch' a.$EXE -n 12
+ run 'gc fannkuch-parallel' a.$EXE -n 12
+ run 'gc_B fannkuch' a.$EXE -n 12
}
regexdna() {
@@ -166,9 +165,9 @@ regexdna() {
fi
run 'gccgo -O2 regex-dna.go' a.$EXE 0 {
@@ -192,11 +180,11 @@ func goFiles(dir string) []string {
type runCmd func(...string) ([]byte, error)
func compileFile(runcmd runCmd, longname string) (out []byte, err error) {
- return runcmd("go", "tool", gc, "-e", longname)
+ return runcmd("go", "tool", "compile", "-e", longname)
}
func compileInDir(runcmd runCmd, dir string, names ...string) (out []byte, err error) {
- cmd := []string{"go", "tool", gc, "-e", "-D", ".", "-I", "."}
+ cmd := []string{"go", "tool", "compile", "-e", "-D", ".", "-I", "."}
for _, name := range names {
cmd = append(cmd, filepath.Join(dir, name))
}
@@ -204,8 +192,8 @@ func compileInDir(runcmd runCmd, dir string, names ...string) (out []byte, err e
}
func linkFile(runcmd runCmd, goname string) (err error) {
- pfile := strings.Replace(goname, ".go", "."+letter, -1)
- _, err = runcmd("go", "tool", ld, "-w", "-o", "a.exe", "-L", ".", pfile)
+ pfile := strings.Replace(goname, ".go", ".o", -1)
+ _, err = runcmd("go", "tool", "link", "-w", "-o", "a.exe", "-L", ".", pfile)
return
}
@@ -506,7 +494,7 @@ func (t *test) run() {
t.err = fmt.Errorf("unimplemented action %q", action)
case "errorcheck":
- cmdline := []string{"go", "tool", gc, "-e", "-o", "a." + letter}
+ cmdline := []string{"go", "tool", "compile", "-e", "-o", "a.o"}
cmdline = append(cmdline, flags...)
cmdline = append(cmdline, long)
out, err := runcmd(cmdline...)
@@ -669,7 +657,7 @@ func (t *test) run() {
t.err = fmt.Errorf("write tempfile:%s", err)
return
}
- cmdline := []string{"go", "tool", gc, "-e", "-o", "a." + letter}
+ cmdline := []string{"go", "tool", "compile", "-e", "-o", "a.o"}
cmdline = append(cmdline, flags...)
cmdline = append(cmdline, tfile)
out, err = runcmd(cmdline...)
diff --git a/test/sinit_run.go b/test/sinit_run.go
index b0a91ce5b1..c9afd3b777 100644
--- a/test/sinit_run.go
+++ b/test/sinit_run.go
@@ -12,26 +12,19 @@ package main
import (
"bytes"
"fmt"
- "go/build"
"os"
"os/exec"
)
func main() {
- letter, err := build.ArchChar(build.Default.GOARCH)
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
-
- cmd := exec.Command("go", "tool", letter+"g", "-S", "sinit.go")
+ cmd := exec.Command("go", "tool", "compile", "-S", "sinit.go")
out, err := cmd.CombinedOutput()
if err != nil {
fmt.Println(string(out))
fmt.Println(err)
os.Exit(1)
}
- os.Remove("sinit." + letter)
+ os.Remove("sinit.o")
if bytes.Contains(out, []byte("initdone")) {
fmt.Println("sinit generated an init function")
diff --git a/test/sliceopt.go b/test/sliceopt.go
new file mode 100644
index 0000000000..c9d089f7d2
--- /dev/null
+++ b/test/sliceopt.go
@@ -0,0 +1,59 @@
+// errorcheck -0 -d=append,slice
+
+// 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.
+
+// Check optimization results for append and slicing.
+
+package main
+
+func a1(x []int, y int) []int {
+ x = append(x, y) // ERROR "append: len-only update"
+ return x
+}
+
+func a2(x []int, y int) []int {
+ return append(x, y) // ERROR "append: full update"
+}
+
+func a3(x *[]int, y int) {
+ *x = append(*x, y) // ERROR "append: len-only update"
+}
+
+func s1(x **[]int, xs **string, i, j int) {
+ var z []int
+ z = (**x)[2:] // ERROR "slice: omit check for 2nd index"
+ z = (**x)[2:len(**x)] // not yet: "slice: reuse len" "slice: omit check for 2nd index"
+ z = (**x)[2:cap(**x)] // not yet: "slice: reuse cap" "slice: omit check for 2nd index"
+ z = (**x)[i:i] // ERROR "slice: reuse 1st index" "slice: omit check for 1st index" "slice: result len == 0"
+ z = (**x)[1:i:i] // ERROR "slice: reuse 2nd index" "slice: omit check for 2nd index" "slice: result cap == result len"
+ z = (**x)[i:j:0] // ERROR "slice: omit check for 3rd index"
+ z = (**x)[i:0:j] // ERROR "slice: omit check for 2nd index"
+ z = (**x)[0:i:j] // ERROR "slice: omit check for 1st index" "slice: skip base adjustment for 1st index 0"
+ z = (**x)[0:] // ERROR "slice: omit slice operation"
+ z = (**x)[2:8] // ERROR "slice: omit check for 1st index" "slice: result len == 6"
+ z = (**x)[2:2] // ERROR "slice: omit check for 1st index" "slice: result len == 0"
+ z = (**x)[0:i] // ERROR "slice: omit check for 1st index" "slice: skip base adjustment for 1st index 0"
+ z = (**x)[2:i:8] // ERROR "slice: result cap == 6"
+ z = (**x)[i:2:i] // ERROR "slice: reuse 1st index" "slice: result cap == 0" "slice: skip base adjustment for cap == 0"
+
+ z = z[0:i] // ERROR "slice: omit check for 1st index" "slice: result cap not computed" "slice: skip base adjustment for 1st index 0" "slice: len-only update"
+ z = z[0:i : i+1] // ERROR "slice: omit check for 1st index" "slice: skip base adjustment for 1st index 0" "slice: len/cap-only update"
+ z = z[i : i+1]
+
+ println(z)
+
+ var zs string
+ zs = (**xs)[2:] // ERROR "slice: omit check for 2nd index"
+ zs = (**xs)[2:len(**xs)] // not yet: "slice: reuse len" "slice: omit check for 2nd index"
+ zs = (**xs)[i:i] // ERROR "slice: reuse 1st index" "slice: omit check for 1st index" "slice: result len == 0" "slice: skip base adjustment for string len == 0"
+ zs = (**xs)[0:] // ERROR "slice: omit slice operation"
+ zs = (**xs)[2:8] // ERROR "slice: omit check for 1st index" "slice: result len == 6"
+ zs = (**xs)[2:2] // ERROR "slice: omit check for 1st index" "slice: result len == 0" "slice: skip base adjustment for string len == 0"
+ zs = (**xs)[0:i] // ERROR "slice: omit check for 1st index" "slice: skip base adjustment for 1st index 0"
+
+ zs = zs[0:i] // ERROR "slice: omit check for 1st index" "slice: skip base adjustment for 1st index 0" "slice: len-only update"
+ zs = zs[i : i+1]
+ println(zs)
+}
diff --git a/test/writebarrier.go b/test/writebarrier.go
index 1f25d91ea4..9b741a60df 100644
--- a/test/writebarrier.go
+++ b/test/writebarrier.go
@@ -28,7 +28,7 @@ func f1a(x *[]byte, y *[]byte) {
*x = *y // ERROR "write barrier"
z := *y // no barrier
- *x = z // ERROR "write barrier"
+ *x = z // ERROR "write barrier"
}
func f2(x *interface{}, y interface{}) {
@@ -56,7 +56,7 @@ func f3a(x *string, y *string) {
*x = *y // ERROR "write barrier"
z := *y // no barrier
- *x = z // ERROR "write barrier"
+ *x = z // ERROR "write barrier"
}
func f4(x *[2]string, y [2]string) {
@@ -70,7 +70,7 @@ func f4a(x *[2]string, y *[2]string) {
*x = *y // ERROR "write barrier"
z := *y // no barrier
- *x = z // ERROR "write barrier"
+ *x = z // ERROR "write barrier"
}
type T struct {
@@ -108,3 +108,39 @@ func f10(x *byte, f func(interface{})) {
func f11(x *unsafe.Pointer, y unsafe.Pointer) {
*x = unsafe.Pointer(uintptr(y) + 1) // ERROR "write barrier"
}
+
+func f12(x []*int, y *int) []*int {
+ // write barrier for storing y in x's underlying array
+ x = append(x, y) // ERROR "write barrier"
+ return x
+}
+
+func f12a(x []int, y int) []int {
+ // y not a pointer, so no write barriers in this function
+ x = append(x, y)
+ return x
+}
+
+func f13(x []int, y *[]int) {
+ *y = append(x, 1) // ERROR "write barrier"
+}
+
+func f14(y *[]int) {
+ *y = append(*y, 1) // ERROR "write barrier"
+}
+
+type T1 struct {
+ X *int
+}
+
+func f15(x []T1, y T1) []T1 {
+ return append(x, y) // ERROR "write barrier"
+}
+
+type T8 struct {
+ X [8]*int
+}
+
+func f16(x []T8, y T8) []T8 {
+ return append(x, y) // ERROR "write barrier"
+}