[dev.ssa] Merge remote-tracking branch 'origin/master' into mergebranch
Semi-regular merge of tip to dev.ssa.
Complicated a bit by the move of cmd/internal/* to cmd/compile/internal/*.
Change-Id: I1c66d3c29bb95cce4a53c5a3476373aa5245303d
diff --git a/api/next.txt b/api/next.txt
index cebbe87..b8e09df 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 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/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 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) 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, 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, 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, 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, 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 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) 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 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 d6be379..8a827d0 100644
--- a/doc/effective_go.html
+++ b/doc/effective_go.html
@@ -1382,7 +1382,7 @@
<code>os</code>:
</p>
<pre>
-func (file *File) Read(buf []byte) (n int, err error)
+func (f *File) Read(buf []byte) (n int, err error)
</pre>
<p>
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 b0602f9..f2ceb1d 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/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 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 @@
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 d02697b..b5f18f3 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,24 +1,9 @@
<!--{
"Title": "The Go Programming Language Specification",
- "Subtitle": "Version of March 20, 2015",
+ "Subtitle": "Version of May 26, 2015",
"Path": "/ref/spec"
}-->
-<!--
-TODO
-[ ] need language about function/method calls and parameter passing rules
-[ ] last paragraph of #Assignments (constant promotion) should be elsewhere
- and mention assignment to empty interface.
-[ ] need to say something about "scope" of selectors?
-[ ] clarify what a field name is in struct declarations
- (struct{T} vs struct {T T} vs struct {t T})
-[ ] need explicit language about the result type of operations
-[ ] should probably write something about evaluation order of statements even
- though obvious
-[ ] in Selectors section, clarify what receiver value is passed in method invocations
--->
-
-
<h2 id="Introduction">Introduction</h2>
<p>
@@ -2605,7 +2590,7 @@
<pre>
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 @@
</p>
<pre class="ebnf">
-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 0000000..dafb162
--- /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 6e1d106..3cc2af5 100644
--- a/misc/cgo/test/cgo_linux_test.go
+++ b/misc/cgo/test/cgo_linux_test.go
@@ -6,7 +6,8 @@
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 0000000..518c533
--- /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 <signal.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+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 0000000..7d343e92
--- /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 9862a37..492d25e 100755
--- a/misc/cgo/testcshared/test.bash
+++ b/misc/cgo/testcshared/test.bash
@@ -15,6 +15,14 @@
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 @@
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 @@
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 @@
# 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 0000000..f7a99af
--- /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 fb112cd..d3bed3f 100644
--- a/misc/cgo/testshared/src/dep/dep.go
+++ b/misc/cgo/testshared/src/dep/dep.go
@@ -2,6 +2,12 @@
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 0000000..bac1086
--- /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 0000000..acdb4dd
--- /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 21004ad..0000000
--- 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 1c013c1..8e53726 100644
--- a/misc/nacl/testzip.proto
+++ b/misc/nacl/testzip.proto
@@ -35,7 +35,7 @@
gofmt_test.go
testdata
+
- link
+ newlink
testdata
+
archive
diff --git a/misc/trace/README.md b/misc/trace/README.md
index b9364de..775fdb8 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 aad1f7e..39e73c3 100755
--- a/src/androidtest.bash
+++ b/src/androidtest.bash
@@ -24,10 +24,11 @@
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}/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 abd8f14..cd23fb5 100644
--- a/src/archive/tar/reader.go
+++ b/src/archive/tar/reader.go
@@ -463,6 +463,10 @@
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 9601ffe..ab1e844 100644
--- a/src/archive/tar/reader_test.go
+++ b/src/archive/tar/reader_test.go
@@ -741,3 +741,19 @@
}
}
+
+// 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 0000000..5deea3d
--- /dev/null
+++ b/src/archive/tar/testdata/neg-size.tar
Binary files differ
diff --git a/src/archive/zip/reader.go b/src/archive/zip/reader.go
index 8136b84..f68ab09 100644
--- a/src/archive/zip/reader.go
+++ b/src/archive/zip/reader.go
@@ -8,6 +8,7 @@
"bufio"
"encoding/binary"
"errors"
+ "fmt"
"hash"
"hash/crc32"
"io"
@@ -77,6 +78,9 @@
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 @@
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 @@
}
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 29d0652..4806b89 100644
--- a/src/archive/zip/reader_test.go
+++ b/src/archive/zip/reader_test.go
@@ -531,3 +531,54 @@
}
}
}
+
+// 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 a07529e..ba23d31 100755
--- a/src/buildall.bash
+++ b/src/buildall.bash
@@ -36,7 +36,7 @@
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 06ab7d0..0000000
--- 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 725c635..d5d2772 100644
--- a/src/cmd/asm/internal/asm/asm.go
+++ b/src/cmd/asm/internal/asm/asm.go
@@ -493,7 +493,10 @@
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 c74f269..bf5cb1e 100644
--- a/src/cmd/asm/internal/flags/flags.go
+++ b/src/cmd/asm/internal/flags/flags.go
@@ -51,7 +51,7 @@
os.Exit(2)
}
-func Parse(theChar int) {
+func Parse() {
flag.Usage = Usage
flag.Parse()
if flag.NArg() != 1 {
@@ -64,6 +64,6 @@
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 730042b..7e495b8 100644
--- a/src/cmd/asm/internal/lex/input.go
+++ b/src/cmd/asm/internal/lex/input.go
@@ -13,7 +13,6 @@
"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 @@
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 28a4b85..6a4d954 100644
--- a/src/cmd/asm/internal/lex/tokenizer.go
+++ b/src/cmd/asm/internal/lex/tokenizer.go
@@ -10,8 +10,6 @@
"strings"
"text/scanner"
"unicode"
-
- "cmd/internal/obj"
)
// A Tokenizer is a simple wrapping of text/scanner.Scanner, configured
@@ -40,7 +38,7 @@
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 @@
if t.file != nil {
t.file.Close()
// It's an open file, so pop the line history.
- obj.Linklinehist(linkCtxt, histLine, "<pop>", 0)
+ linkCtxt.LineHist.Pop(histLine)
}
}
diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go
index 32bdee6..db0e28e 100644
--- a/src/cmd/asm/main.go
+++ b/src/cmd/asm/main.go
@@ -29,7 +29,7 @@
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 30f828c..87f21ed 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -846,6 +846,8 @@
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 @@
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 @@
#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 23e2d1b..71f8f88 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 96%
rename from src/cmd/6g/galign.go
rename to src/cmd/compile/internal/amd64/galign.go
index 0ca8753..79bf94a 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 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 @@
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 6e5e6bc..6425633 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 @@
// 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 @@
check := 0
if gc.Issigned[t.Etype] {
check = 1
- if gc.Isconst(nl, gc.CTINT) && gc.Mpgetfix(nl.Val.U.Xval) != -(1<<uint64(t.Width*8-1)) {
+ if gc.Isconst(nl, gc.CTINT) && nl.Int() != -(1<<uint64(t.Width*8-1)) {
check = 0
- } else if gc.Isconst(nr, gc.CTINT) && gc.Mpgetfix(nr.Val.U.Xval) != -1 {
+ } else if gc.Isconst(nr, gc.CTINT) && nr.Int() != -1 {
check = 0
}
}
@@ -306,7 +306,7 @@
* known to be dead.
*/
func savex(dr int, x *gc.Node, oldx *gc.Node, res *gc.Node, t *gc.Type) {
- r := int(reg[dr])
+ r := reg[dr]
// save current ax and dx if they are live
// and not the destination
@@ -318,7 +318,7 @@
x.Type = gc.Types[gc.TINT64]
gmove(x, oldx)
x.Type = t
- oldx.Ostk = int32(r) // squirrel away old r value
+ oldx.Etype = r // squirrel away old r value
reg[dr] = 1
}
}
@@ -326,7 +326,7 @@
func restx(x *gc.Node, oldx *gc.Node) {
if oldx.Op != 0 {
x.Type = gc.Types[gc.TINT64]
- reg[x.Reg] = uint8(oldx.Ostk)
+ reg[x.Reg] = oldx.Etype
gmove(oldx, x)
gc.Regfree(oldx)
}
@@ -381,7 +381,7 @@
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 >= 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 94%
rename from src/cmd/6g/gsubr.go
rename to src/cmd/compile/internal/amd64/gsubr.go
index 53d0f03..a8e4170 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 @@
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 @@
// 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 @@
// 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 @@
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 cd07199..19db68e 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 5f60474..00918c8 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 cab07b5..7d4f406 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 2e92239..8ea6c5f 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 @@
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 98%
rename from src/cmd/5g/cgen64.go
rename to src/cmd/compile/internal/arm/cgen64.go
index 699e555..6c88b76 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 @@
// 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 @@
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 @@
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 3c8ba519..60a39d3 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 @@
gc.Widthreg = 4
}
-func main() {
+func Main() {
gc.Thearch.Thechar = thechar
gc.Thearch.Thestring = thestring
gc.Thearch.Thelinkarch = thelinkarch
@@ -65,6 +65,7 @@
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 0cf0d92..6633351 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 @@
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 @@
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 @@
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 @@
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 57d511e..5263f15 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 b76719d..66eba41 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 c472cdf..cdf9d29 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 2afdf12..b72ccc9 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 6f268b4..30326d7 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 95%
rename from src/cmd/7g/galign.go
rename to src/cmd/compile/internal/arm64/galign.go
index 34b4ab6..38def8f 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 @@
gc.Widthreg = 8
}
-func main() {
+func Main() {
gc.Thearch.Thechar = thechar
gc.Thearch.Thestring = thestring
gc.Thearch.Thelinkarch = thelinkarch
@@ -65,6 +65,7 @@
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 b824a3a..851ca4e 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 @@
// 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 @@
check := 0
if gc.Issigned[t.Etype] {
check = 1
- if gc.Isconst(nl, gc.CTINT) && gc.Mpgetfix(nl.Val.U.Xval) != -(1<<uint64(t.Width*8-1)) {
+ if gc.Isconst(nl, gc.CTINT) && nl.Int() != -(1<<uint64(t.Width*8-1)) {
check = 0
- } else if gc.Isconst(nr, gc.CTINT) && gc.Mpgetfix(nr.Val.U.Xval) != -1 {
+ } else if gc.Isconst(nr, gc.CTINT) && nr.Int() != -1 {
check = 0
}
}
@@ -312,7 +312,7 @@
var n1 gc.Node
gc.Regalloc(&n1, nl.Type, res)
gc.Cgen(nl, &n1)
- sc := uint64(uint64(gc.Mpgetfix(nr.Val.U.Xval)))
+ sc := uint64(nr.Int())
if sc >= 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 94%
rename from src/cmd/7g/gsubr.go
rename to src/cmd/compile/internal/arm64/gsubr.go
index a34a4306..0a14654 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 @@
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 @@
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 @@
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 @@
}
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 98%
rename from src/cmd/7g/peep.go
rename to src/cmd/compile/internal/arm64/peep.go
index 49bc69b..3dbccb7 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 @@
// 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 023f302..1106e78 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 0e5ac73..7bc756b 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 98%
rename from src/cmd/internal/gc/big/arith.go
rename to src/cmd/compile/internal/big/arith.go
index 328c85c..1ff6349 100644
--- a/src/cmd/internal/gc/big/arith.go
+++ b/src/cmd/compile/internal/big/arith.go
@@ -196,7 +196,6 @@
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 cd92dd7..f46a494 100644
--- a/src/cmd/internal/gc/big/arith_test.go
+++ b/src/cmd/compile/internal/big/arith_test.go
@@ -155,6 +155,7 @@
{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 078be47..cb91bc2 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 ed55e8e..dcb72c5 100644
--- a/src/cmd/internal/gc/big/float.go
+++ b/src/cmd/compile/internal/big/float.go
@@ -65,12 +65,16 @@
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 @@
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 @@
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<<mbits - 1) // cut off msb (implicit 1 bit)
-
// Rounding may have caused a denormal number to
// become normal. Check again.
- c := float32(1.0)
- if r.exp < emin+1 {
+ if e < emin {
// denormal number
- r.exp += mbits
- c = 1.0 / (1 << mbits) // 2**-mbits
+ if e < dmin {
+ // underflow to ±0
+ if x.neg {
+ var z float32
+ return -z, Above
+ }
+ return 0.0, Below
+ }
+ // bexp = 0
+ mant = high32(r.mant) >> (fbits - r.prec)
+ } else {
+ // normal number: emin <= e <= emax
+ bexp = uint32(e+bias) << mbits
+ mant = high32(r.mant) >> ebits & (1<<mbits - 1) // cut off msb (implicit 1 bit)
}
- // emin+1 <= r.exp <= emax+1
- e := uint32(r.exp-emin) << mbits
- return c * math.Float32frombits(s|e|m), r.acc
+ return math.Float32frombits(sign | bexp | mant), r.acc
case zero:
if x.neg {
@@ -976,64 +983,70 @@
emax = bias // 1023 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 float64
- 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 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<<mbits - 1) // cut off msb (implicit 1 bit)
-
// Rounding may have caused a denormal number to
// become normal. Check again.
- c := 1.0
- if r.exp < emin+1 {
+ if e < emin {
// denormal number
- r.exp += mbits
- c = 1.0 / (1 << mbits) // 2**-mbits
+ if e < dmin {
+ // underflow to ±0
+ if x.neg {
+ var z float64
+ return -z, Above
+ }
+ return 0.0, Below
+ }
+ // bexp = 0
+ mant = high64(r.mant) >> (fbits - r.prec)
+ } else {
+ // normal number: emin <= e <= emax
+ bexp = uint64(e+bias) << mbits
+ mant = high64(r.mant) >> ebits & (1<<mbits - 1) // cut off msb (implicit 1 bit)
}
- // emin+1 <= r.exp <= emax+1
- e := uint64(r.exp-emin) << mbits
- return c * math.Float64frombits(s|e|m), r.acc
+ return math.Float64frombits(sign | bexp | mant), r.acc
case zero:
if x.neg {
diff --git a/src/cmd/internal/gc/big/float_test.go b/src/cmd/compile/internal/big/float_test.go
similarity index 90%
rename from src/cmd/internal/gc/big/float_test.go
rename to src/cmd/compile/internal/big/float_test.go
index 2a48ec4..8bd3a9c 100644
--- a/src/cmd/internal/gc/big/float_test.go
+++ b/src/cmd/compile/internal/big/float_test.go
@@ -12,6 +12,9 @@
"testing"
)
+// Verify that ErrNaN implements the error interface.
+var _ error = ErrNaN{}
+
func (x *Float) uint64() uint64 {
u, acc := x.Uint64()
if acc != Exact {
@@ -200,6 +203,18 @@
return x.Cmp(y) == 0 && x.Signbit() == y.Signbit()
}
+func alike32(x, y float32) bool {
+ // we can ignore NaNs
+ return x == y && math.Signbit(float64(x)) == math.Signbit(float64(y))
+
+}
+
+func alike64(x, y float64) bool {
+ // we can ignore NaNs
+ return x == y && math.Signbit(x) == math.Signbit(y)
+
+}
+
func TestFloatMantExp(t *testing.T) {
for _, test := range []struct {
x string
@@ -825,52 +840,69 @@
out float32
acc Accuracy
}{
- {"-Inf", float32(math.Inf(-1)), Exact},
- {"-0x1.ffffff0p2147483646", float32(-math.Inf(+1)), Below}, // overflow in rounding
- {"-1e10000", float32(math.Inf(-1)), Below}, // overflow
- {"-0x1p128", float32(math.Inf(-1)), Below}, // overflow
- {"-0x1.ffffff0p127", float32(-math.Inf(+1)), Below}, // overflow
- {"-0x1.fffffe8p127", -math.MaxFloat32, Above},
- {"-0x1.fffffe0p127", -math.MaxFloat32, Exact},
- {"-12345.000000000000000000001", -12345, Above},
- {"-12345.0", -12345, Exact},
- {"-1.000000000000000000001", -1, Above},
- {"-1", -1, Exact},
- {"-0x0.000002p-126", -math.SmallestNonzeroFloat32, Exact},
- {"-0x0.000002p-127", -0, Above}, // underflow
- {"-1e-1000", -0, Above}, // underflow
{"0", 0, Exact},
- {"1e-1000", 0, Below}, // underflow
- {"0x0.000002p-127", 0, Below}, // underflow
- {"0x0.000002p-126", math.SmallestNonzeroFloat32, Exact},
+
+ // underflow
+ {"1e-1000", 0, Below},
+ {"0x0.000002p-127", 0, Below},
+ {"0x.0000010p-126", 0, Below},
+
+ // denormals
+ {"1.401298464e-45", math.SmallestNonzeroFloat32, Above}, // rounded up to smallest denormal
+ {"0x.ffffff8p-149", math.SmallestNonzeroFloat32, Above}, // rounded up to smallest denormal
+ {"0x.0000018p-126", math.SmallestNonzeroFloat32, Above}, // rounded up to smallest denormal
+ {"0x.0000020p-126", math.SmallestNonzeroFloat32, Exact},
+ {"0x.8p-148", math.SmallestNonzeroFloat32, Exact},
+ {"1p-149", math.SmallestNonzeroFloat32, Exact},
+ {"0x.fffffep-126", math.Float32frombits(0x7fffff), Exact}, // largest denormal
+
+ // normals
+ {"0x.ffffffp-126", math.Float32frombits(0x00800000), Above}, // rounded up to smallest normal
+ {"1p-126", math.Float32frombits(0x00800000), Exact}, // smallest normal
+ {"0x1.fffffep-126", math.Float32frombits(0x00ffffff), Exact},
+ {"0x1.ffffffp-126", math.Float32frombits(0x01000000), Above}, // rounded up
{"1", 1, Exact},
{"1.000000000000000000001", 1, Below},
{"12345.0", 12345, Exact},
{"12345.000000000000000000001", 12345, Below},
{"0x1.fffffe0p127", math.MaxFloat32, Exact},
{"0x1.fffffe8p127", math.MaxFloat32, Below},
- {"0x1.ffffff0p127", float32(math.Inf(+1)), Above}, // overflow
- {"0x1p128", float32(math.Inf(+1)), Above}, // overflow
- {"1e10000", float32(math.Inf(+1)), Above}, // overflow
+
+ // overflow
+ {"0x1.ffffff0p127", float32(math.Inf(+1)), Above},
+ {"0x1p128", float32(math.Inf(+1)), Above},
+ {"1e10000", float32(math.Inf(+1)), Above},
{"0x1.ffffff0p2147483646", float32(math.Inf(+1)), Above}, // overflow in rounding
- {"+Inf", float32(math.Inf(+1)), Exact},
+
+ // inf
+ {"Inf", float32(math.Inf(+1)), Exact},
} {
- // conversion should match strconv where syntax is agreeable
- if f, err := strconv.ParseFloat(test.x, 32); err == nil && float32(f) != test.out {
- t.Errorf("%s: got %g; want %g (incorrect test data)", test.x, f, test.out)
- }
+ for i := 0; i < 2; i++ {
+ // test both signs
+ tx, tout, tacc := test.x, test.out, test.acc
+ if i != 0 {
+ tx = "-" + tx
+ tout = -tout
+ tacc = -tacc
+ }
- x := makeFloat(test.x)
- out, acc := x.Float32()
- if out != test.out || acc != test.acc {
- t.Errorf("%s: got %g (%#x, %s); want %g (%#x, %s)", test.x, out, math.Float32bits(out), acc, test.out, math.Float32bits(test.out), test.acc)
- }
+ // conversion should match strconv where syntax is agreeable
+ if f, err := strconv.ParseFloat(tx, 32); err == nil && !alike32(float32(f), tout) {
+ t.Errorf("%s: got %g; want %g (incorrect test data)", tx, f, tout)
+ }
- // test that x.SetFloat64(float64(f)).Float32() == f
- var x2 Float
- out2, acc2 := x2.SetFloat64(float64(out)).Float32()
- if out2 != out || acc2 != Exact {
- t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out)
+ x := makeFloat(tx)
+ out, acc := x.Float32()
+ if !alike32(out, tout) || acc != tacc {
+ t.Errorf("%s: got %g (%#x, %s); want %g (%#x, %s)", tx, out, math.Float32bits(out), acc, test.out, math.Float32bits(test.out), tacc)
+ }
+
+ // test that x.SetFloat64(float64(f)).Float32() == f
+ var x2 Float
+ out2, acc2 := x2.SetFloat64(float64(out)).Float32()
+ if !alike32(out2, out) || acc2 != Exact {
+ t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out)
+ }
}
}
}
@@ -882,35 +914,36 @@
out float64
acc Accuracy
}{
- {"-Inf", math.Inf(-1), Exact},
- {"-0x1.fffffffffffff8p2147483646", -math.Inf(+1), Below}, // overflow in rounding
- {"-1e10000", math.Inf(-1), Below}, // overflow
- {"-0x1p1024", math.Inf(-1), Below}, // overflow
- {"-0x1.fffffffffffff8p1023", -math.Inf(+1), Below}, // overflow
- {"-0x1.fffffffffffff4p1023", -math.MaxFloat64, Above},
- {"-0x1.fffffffffffff0p1023", -math.MaxFloat64, Exact},
- {"-12345.000000000000000000001", -12345, Above},
- {"-12345.0", -12345, Exact},
- {"-1.000000000000000000001", -1, Above},
- {"-1", -1, Exact},
- {"-0x0.0000000000001p-1022", -math.SmallestNonzeroFloat64, Exact},
- {"-0x0.0000000000001p-1023", -0, Above}, // underflow
- {"-1e-1000", -0, Above}, // underflow
{"0", 0, Exact},
- {"1e-1000", 0, Below}, // underflow
- {"0x0.0000000000001p-1023", 0, Below}, // underflow
- {"0x0.0000000000001p-1022", math.SmallestNonzeroFloat64, Exact},
+
+ // underflow
+ {"1e-1000", 0, Below},
+ {"0x0.0000000000001p-1023", 0, Below},
+ {"0x0.00000000000008p-1022", 0, Below},
+
+ // denormals
+ {"0x0.0000000000000cp-1022", math.SmallestNonzeroFloat64, Above}, // rounded up to smallest denormal
+ {"0x0.0000000000001p-1022", math.SmallestNonzeroFloat64, Exact}, // smallest denormal
+ {"0x.8p-1073", math.SmallestNonzeroFloat64, Exact},
+ {"1p-1074", math.SmallestNonzeroFloat64, Exact},
+ {"0x.fffffffffffffp-1022", math.Float64frombits(0x000fffffffffffff), Exact}, // largest denormal
+
+ // normals
+ {"0x.fffffffffffff8p-1022", math.Float64frombits(0x0010000000000000), Above}, // rounded up to smallest normal
+ {"1p-1022", math.Float64frombits(0x0010000000000000), Exact}, // smallest normal
{"1", 1, Exact},
{"1.000000000000000000001", 1, Below},
{"12345.0", 12345, Exact},
{"12345.000000000000000000001", 12345, Below},
{"0x1.fffffffffffff0p1023", math.MaxFloat64, Exact},
{"0x1.fffffffffffff4p1023", math.MaxFloat64, Below},
- {"0x1.fffffffffffff8p1023", math.Inf(+1), Above}, // overflow
- {"0x1p1024", math.Inf(+1), Above}, // overflow
- {"1e10000", math.Inf(+1), Above}, // overflow
+
+ // overflow
+ {"0x1.fffffffffffff8p1023", math.Inf(+1), Above},
+ {"0x1p1024", math.Inf(+1), Above},
+ {"1e10000", math.Inf(+1), Above},
{"0x1.fffffffffffff8p2147483646", math.Inf(+1), Above}, // overflow in rounding
- {"+Inf", math.Inf(+1), Exact},
+ {"Inf", math.Inf(+1), Exact},
// selected denormalized values that were handled incorrectly in the past
{"0x.fffffffffffffp-1022", smallestNormalFloat64 - math.SmallestNonzeroFloat64, Exact},
@@ -921,22 +954,32 @@
// http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
{"2.2250738585072012e-308", 2.2250738585072014e-308, Above},
} {
- // conversion should match strconv where syntax is agreeable
- if f, err := strconv.ParseFloat(test.x, 64); err == nil && f != test.out {
- t.Errorf("%s: got %g; want %g (incorrect test data)", test.x, f, test.out)
- }
+ for i := 0; i < 2; i++ {
+ // test both signs
+ tx, tout, tacc := test.x, test.out, test.acc
+ if i != 0 {
+ tx = "-" + tx
+ tout = -tout
+ tacc = -tacc
+ }
- x := makeFloat(test.x)
- out, acc := x.Float64()
- if out != test.out || acc != test.acc {
- t.Errorf("%s: got %g (%#x, %s); want %g (%#x, %s)", test.x, out, math.Float64bits(out), acc, test.out, math.Float64bits(test.out), test.acc)
- }
+ // conversion should match strconv where syntax is agreeable
+ if f, err := strconv.ParseFloat(tx, 64); err == nil && !alike64(f, tout) {
+ t.Errorf("%s: got %g; want %g (incorrect test data)", tx, f, tout)
+ }
- // test that x.SetFloat64(f).Float64() == f
- var x2 Float
- out2, acc2 := x2.SetFloat64(out).Float64()
- if out2 != out || acc2 != Exact {
- t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out)
+ x := makeFloat(tx)
+ out, acc := x.Float64()
+ if !alike64(out, tout) || acc != tacc {
+ t.Errorf("%s: got %g (%#x, %s); want %g (%#x, %s)", tx, out, math.Float64bits(out), acc, test.out, math.Float64bits(test.out), tacc)
+ }
+
+ // test that x.SetFloat64(f).Float64() == f
+ var x2 Float
+ out2, acc2 := x2.SetFloat64(out).Float64()
+ if !alike64(out2, out) || acc2 != Exact {
+ t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out)
+ }
}
}
}
@@ -1656,7 +1699,7 @@
want = +1
}
if got != want {
- t.Errorf("(%g).Cmp(%g) = %s; want %s", x, y, got, want)
+ t.Errorf("(%g).Cmp(%g) = %v; want %v", x, y, got, want)
}
}
}
diff --git a/src/cmd/internal/gc/big/floatconv.go b/src/cmd/compile/internal/big/floatconv.go
similarity index 100%
rename from src/cmd/internal/gc/big/floatconv.go
rename to src/cmd/compile/internal/big/floatconv.go
diff --git a/src/cmd/internal/gc/big/floatconv_test.go b/src/cmd/compile/internal/big/floatconv_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/floatconv_test.go
rename to src/cmd/compile/internal/big/floatconv_test.go
diff --git a/src/cmd/internal/gc/big/floatexample_test.go b/src/cmd/compile/internal/big/floatexample_test.go
similarity index 98%
rename from src/cmd/internal/gc/big/floatexample_test.go
rename to src/cmd/compile/internal/big/floatexample_test.go
index 7db1023..0ac9617 100644
--- a/src/cmd/internal/gc/big/floatexample_test.go
+++ b/src/cmd/compile/internal/big/floatexample_test.go
@@ -5,9 +5,9 @@
package big_test
import (
+ "cmd/compile/internal/big"
"fmt"
"math"
- "math/big"
)
func ExampleFloat_Add() {
diff --git a/src/cmd/internal/gc/big/ftoa.go b/src/cmd/compile/internal/big/ftoa.go
similarity index 100%
rename from src/cmd/internal/gc/big/ftoa.go
rename to src/cmd/compile/internal/big/ftoa.go
diff --git a/src/cmd/internal/gc/big/gcd_test.go b/src/cmd/compile/internal/big/gcd_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/gcd_test.go
rename to src/cmd/compile/internal/big/gcd_test.go
diff --git a/src/cmd/internal/gc/big/hilbert_test.go b/src/cmd/compile/internal/big/hilbert_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/hilbert_test.go
rename to src/cmd/compile/internal/big/hilbert_test.go
diff --git a/src/cmd/internal/gc/big/int.go b/src/cmd/compile/internal/big/int.go
similarity index 88%
rename from src/cmd/internal/gc/big/int.go
rename to src/cmd/compile/internal/big/int.go
index 7b419bf..5e31253 100644
--- a/src/cmd/internal/gc/big/int.go
+++ b/src/cmd/compile/internal/big/int.go
@@ -583,6 +583,124 @@
return z
}
+// Jacobi returns the Jacobi symbol (x/y), either +1, -1, or 0.
+// The y argument must be an odd integer.
+func Jacobi(x, y *Int) int {
+ if len(y.abs) == 0 || y.abs[0]&1 == 0 {
+ panic(fmt.Sprintf("big: invalid 2nd argument to Int.Jacobi: need odd integer but got %s", y))
+ }
+
+ // We use the formulation described in chapter 2, section 2.4,
+ // "The Yacas Book of Algorithms":
+ // http://yacas.sourceforge.net/Algo.book.pdf
+
+ var a, b, c Int
+ a.Set(x)
+ b.Set(y)
+ j := 1
+
+ if b.neg {
+ if a.neg {
+ j = -1
+ }
+ b.neg = false
+ }
+
+ for {
+ if b.Cmp(intOne) == 0 {
+ return j
+ }
+ if len(a.abs) == 0 {
+ return 0
+ }
+ a.Mod(&a, &b)
+ if len(a.abs) == 0 {
+ return 0
+ }
+ // a > 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 89%
rename from src/cmd/internal/gc/big/int_test.go
rename to src/cmd/compile/internal/big/int_test.go
index a972a72..c19e88a 100644
--- a/src/cmd/internal/gc/big/int_test.go
+++ b/src/cmd/compile/internal/big/int_test.go
@@ -525,6 +525,7 @@
{"1234", "-1", "1", "0"},
// misc
+ {"5", "1", "3", "2"},
{"5", "-7", "", "1"},
{"-5", "-7", "", "1"},
{"5", "0", "", "1"},
@@ -703,6 +704,13 @@
"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 @@
}
}
+// 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 2a279d1..c3eef76 100644
--- a/src/cmd/internal/gc/big/nat.go
+++ b/src/cmd/compile/internal/big/nat.go
@@ -216,6 +216,34 @@
}
}
+// 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 @@
}
// 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 @@
// 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 @@
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<<n; i++ {
+ powers[i] = powers[i].montgomery(powers[i-1], powers[1], m, k0, numWords)
+ }
+
+ // initialize z = 1 (Montgomery 1)
+ z = z.make(numWords)
+ copy(z, powers[0])
+
+ zz = zz.make(numWords)
+
+ // same windowed exponent, but with Montgomery multiplications
+ for i := len(y) - 1; i >= 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 b25a89f..a15a2bc 100644
--- a/src/cmd/internal/gc/big/nat_test.go
+++ b/src/cmd/compile/internal/big/nat_test.go
@@ -332,6 +332,67 @@
}
}
+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 84aa750..1b191cc 100755
--- a/src/cmd/internal/gc/big/vendor.bash
+++ b/src/cmd/compile/internal/big/vendor.bash
@@ -15,9 +15,15 @@
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 789e59b..892595a 100644
--- a/src/cmd/internal/gc/align.go
+++ b/src/cmd/compile/internal/gc/align.go
@@ -71,8 +71,8 @@
// 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 501cdcb..ca58b1c 100644
--- a/src/cmd/internal/gc/cgen.go
+++ b/src/cmd/compile/internal/gc/cgen.go
@@ -43,14 +43,7 @@
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 @@
case ODOTTYPE:
cgen_dottype(n, res, nil, wb)
return
+
+ case OAPPEND:
+ cgen_append(n, res)
+ return
}
if n.Ullman >= UINF {
@@ -545,7 +542,7 @@
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 @@
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 @@
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 @@
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 @@
}
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_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 @@
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 @@
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 @@
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 @@
} 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
}
}
-/*
- * 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 @@
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 @@
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 @@
// 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 @@
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 @@
}
// call direct
- n.Left.Method = true
+ n.Left.Name.Method = true
Ginscall(n.Left, proc)
}
@@ -2701,7 +2661,7 @@
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 @@
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 @@
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 8d5fd5a..64cd972 100644
--- a/src/cmd/internal/gc/closure.go
+++ b/src/cmd/compile/internal/gc/closure.go
@@ -17,7 +17,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
// 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 @@
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 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 @@
}
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 @@
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 @@
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 @@
// 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 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 @@
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 @@
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 @@
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 @@
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 ad29158..b3605ab 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 @@
var v Val
v.Ctype = CTFLT
- v.U.Fval = oldv
+ v.U = oldv
overflow(v, t)
fv := newMpflt()
@@ -190,8 +227,8 @@
// 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 @@
}
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
case CTNIL:
v = Val{}
v.Ctype = CTSTR
- v.U.Sval = ""
+ v.U = ""
}
return v
@@ -519,7 +562,7 @@
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 @@
*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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 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 @@
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 @@
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 @@
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 @@
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 @@
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 cf48c92..56a4892 100644
--- a/src/cmd/internal/gc/cplx.go
+++ b/src/cmd/compile/internal/gc/cplx.go
@@ -89,8 +89,8 @@
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 @@
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 08d2469..4a9cb29 100644
--- a/src/cmd/internal/gc/dcl.go
+++ b/src/cmd/compile/internal/gc/dcl.go
@@ -183,7 +183,7 @@
}
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 @@
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 @@
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 @@
* 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 @@
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 @@
// 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 @@
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 @@
dclcontext = PPARAM
markdcl()
Funcdepth++
- n.Outer = Curfn
+ n.Param.Outer = Curfn
Curfn = n
funcargs(n.Right)
@@ -583,13 +584,13 @@
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 @@
}
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 @@
}
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 @@
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 @@
}
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 @@
* 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 @@
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 92%
rename from src/cmd/internal/gc/esc.go
rename to src/cmd/compile/internal/gc/esc.go
index c816fea..2c13493 100644
--- a/src/cmd/internal/gc/esc.go
+++ b/src/cmd/compile/internal/gc/esc.go
@@ -154,7 +154,7 @@
}
if n.Op == OCLOSURE {
- m := v.visit(n.Closure)
+ m := v.visit(n.Param.Closure)
if m < min {
min = m
}
@@ -379,7 +379,7 @@
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 @@
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 @@
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 @@
}
}
+ // 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 @@
}
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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
// 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 @@
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 @@
case OCALLINTER:
fntype = n.Left.Type
+ indirect = true
}
ll := n.List
@@ -1307,8 +1340,30 @@
}
}
+ 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 @@
}
// 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 @@
}
// 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 @@
}
}
- 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 @@
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 @@
}
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 @@
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 @@
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 @@
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 98%
rename from src/cmd/internal/gc/export.go
rename to src/cmd/compile/internal/gc/export.go
index 1efc815..5117490 100644
--- a/src/cmd/internal/gc/export.go
+++ b/src/cmd/compile/internal/gc/export.go
@@ -64,7 +64,7 @@
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 1a991a0..4b93363 100644
--- a/src/cmd/internal/gc/fmt.go
+++ b/src/cmd/compile/internal/gc/fmt.go
@@ -302,12 +302,12 @@
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 @@
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 @@
// 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 @@
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 @@
} 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 @@
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 e6af897..c0dd996 100644
--- a/src/cmd/internal/gc/gen.go
+++ b/src/cmd/compile/internal/gc/gen.go
@@ -57,14 +57,14 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
}
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)
@@ -556,122 +553,6 @@
}
/*
- * 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
* <0 is pointer to next field (+1)
@@ -1085,7 +966,7 @@
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 @@
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 @@
// 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 71bce0b..dc33f62 100644
--- a/src/cmd/internal/gc/go.go
+++ b/src/cmd/compile/internal/gc/go.go
@@ -6,7 +6,7 @@
import (
"bytes"
- "cmd/internal/gc/big"
+ "cmd/compile/internal/big"
"cmd/internal/obj"
)
@@ -83,13 +83,13 @@
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 nsyntaxerrors int
-var decldepth int
+var decldepth int32
var safemode int
@@ -778,13 +778,27 @@
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 f1904b0..ae2e761 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 @@
%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 @@
{
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 @@
{
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 @@
$$ = 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 @@
$$.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 @@
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 @@
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 @@
$$ = 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 @@
{
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 53b3f6c..5ec4587 100644
--- a/src/cmd/internal/gc/gsubr.go
+++ b/src/cmd/compile/internal/gc/gsubr.go
@@ -90,6 +90,10 @@
p.From.Offset = int64(obj.Bool2int(likely > 0))
}
+ if Debug['g'] != 0 {
+ fmt.Printf("%v\n", p)
+ }
+
return p
}
@@ -210,7 +214,7 @@
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 @@
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 @@
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 @@
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 b5d1e50..92bfeec 100644
--- a/src/cmd/internal/gc/init.go
+++ b/src/cmd/compile/internal/gc/init.go
@@ -116,7 +116,7 @@
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 97%
rename from src/cmd/internal/gc/inl.go
rename to src/cmd/compile/internal/gc/inl.go
index dd2087d..22a5d3d 100644
--- a/src/cmd/internal/gc/inl.go
+++ b/src/cmd/compile/internal/gc/inl.go
@@ -511,10 +511,10 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 4bbda95..cf41c40 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 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 @@
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 @@
lexlineno = 1
for _, infile = range flag.Args() {
- linehist(infile, 0, 0)
+ linehistpush(infile)
curio.infile = infile
var err error
@@ -339,7 +345,7 @@
errorexit()
}
- linehist("<pop>", 0, 0)
+ linehistpop()
if curio.bin != nil {
obj.Bterm(curio.bin)
}
@@ -394,7 +400,7 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 fakeimport() {
importpkg = mkpkg("fake")
- cannedimports("fake.6", "$$\n")
+ cannedimports("fake.o", "$$\n")
}
func importfile(f *Val, line int) {
@@ -641,13 +645,13 @@
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 @@
// 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 @@
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 @@
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 @@
// 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 @@
return
}
- Yyerror("no import in %q", f.U.Sval)
+ Yyerror("no import in %q", f.U.(string))
unimportfile()
}
@@ -1060,8 +1064,8 @@
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 @@
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 @@
}
caseep:
+ if importpkg == nil && (c == 'p' || c == 'P') {
+ // <mantissa>p<base-2-exponent> 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 @@
}
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 @@
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 @@
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 @@
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 @@
}
name = text[:linep-1]
- linehist(name, int32(n), 0)
+ linehistupdate(name, n)
return c
out:
@@ -2590,6 +2599,6 @@
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 82%
rename from src/cmd/internal/gc/mkbuiltin.go
rename to src/cmd/compile/internal/gc/mkbuiltin.go
index b2362a6..f4569b4 100644
--- a/src/cmd/internal/gc/mkbuiltin.go
+++ b/src/cmd/compile/internal/gc/mkbuiltin.go
@@ -13,21 +13,14 @@
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 @@
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 @@
}
// 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 @@
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 de96e97..2c7e517 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 @@
/// 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 2700b64..0e0b626 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 @@
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 @@
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 05c5b1a..9bb334c 100644
--- a/src/cmd/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -102,8 +102,7 @@
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 @@
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 f08f5f2..ee0ec52 100644
--- a/src/cmd/internal/gc/order.go
+++ b/src/cmd/compile/internal/gc/order.go
@@ -104,9 +104,23 @@
// 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 @@
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 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 @@
}
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 @@
// 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 @@
// 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 @@
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 @@
n.Right.Ninit = concat(l, n.Right.Ninit)
orderexprinplace(&n.Right, order)
- case OAPPEND,
- OCALLFUNC,
+ case OCALLFUNC,
OCALLINTER,
OCALLMETH,
OCAP,
@@ -1064,7 +1084,37 @@
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 @@
}
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 @@
}
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 @@
}
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 2c225c8..c170060 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 @@
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 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 040a778..b4d0699 100644
--- a/src/cmd/internal/gc/plive.go
+++ b/src/cmd/compile/internal/gc/plive.go
@@ -944,7 +944,7 @@
*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 @@
}
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 e7f3500..05a902e 100644
--- a/src/cmd/internal/gc/racewalk.go
+++ b/src/cmd/compile/internal/gc/racewalk.go
@@ -186,31 +186,6 @@
// 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 @@
}
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 @@
*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 9979fe8..6c0962f 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 @@
// 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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
if isdirectiface(t) {
i |= obj.KindDirectIface
}
- if gcprog {
+ if useGCProg {
i |= obj.KindGCProg
}
ot = duint8(s, ot, uint8(i)) // kind
@@ -832,48 +834,7 @@
} 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 @@
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 a9af945..b5427a3 100644
--- a/src/cmd/internal/gc/sinit.go
+++ b/src/cmd/compile/internal/gc/sinit.go
@@ -221,7 +221,7 @@
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 @@
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 @@
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 @@
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 @@
// 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 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 @@
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 @@
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 @@
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 bb4d278..7f78fce 100644
--- a/src/cmd/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -7,9 +7,9 @@
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 @@
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 96%
rename from src/cmd/internal/gc/subr.go
rename to src/cmd/compile/internal/gc/subr.go
index 06ceff5..ed5001a 100644
--- a/src/cmd/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -125,13 +125,6 @@
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 @@
}
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 @@
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 @@
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 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 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 @@
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 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 @@
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 @@
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 @@
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 @@
*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 @@
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 @@
lineno = lexlineno
if genwrapper_linehistdone == 0 {
// All the wrappers can share the same linehist entry.
- linehist("<autogenerated>", 0, 0)
+ linehistpush("<autogenerated>")
genwrapper_linehistdone = 1
}
@@ -2368,7 +2375,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 7cb632c..221b1f4 100644
--- a/src/cmd/internal/gc/swt.go
+++ b/src/cmd/compile/internal/gc/swt.go
@@ -169,10 +169,10 @@
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 @@
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 @@
// 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 7c9fb8d..be43076 100644
--- a/src/cmd/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -23,14 +23,67 @@
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 @@
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 6f7830d..cf1589e 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 6daf842..2900da8 100644
--- a/src/cmd/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -311,8 +311,8 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
}
// 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 @@
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)
+ }
}
- args.N = assignconv(args.N, t.Type, "append")
+ } else {
+ for args = args.Next; args != nil; args = args.Next {
+ if args.N.Type == nil {
+ continue
+ }
+ args.N = assignconv(args.N, t.Type, "append")
+ }
}
break OpSwitch
@@ -1852,7 +1862,7 @@
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 @@
}
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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
}
}
- 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 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 @@
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 @@
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 @@
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 @@
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 97%
rename from src/cmd/internal/gc/unsafe.go
rename to src/cmd/compile/internal/gc/unsafe.go
index aa90a19..824ecd0 100644
--- a/src/cmd/internal/gc/unsafe.go
+++ b/src/cmd/compile/internal/gc/unsafe.go
@@ -140,8 +140,8 @@
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 5dc6561..c59af06 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 @@
)
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 88%
rename from src/cmd/internal/gc/walk.go
rename to src/cmd/compile/internal/gc/walk.go
index c32a813..d5eb44c 100644
--- a/src/cmd/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -352,6 +352,20 @@
*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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
*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 @@
}
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 @@
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 @@
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 Debug_wb > 1 {
+ Warnl(int(n.Lineno), "marking %v for barrier", Nconv(n.Left, 0))
}
- 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)
- }
+ n.Op = OASWB
+ return n
}
return n
}
@@ -2728,10 +2685,10 @@
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 @@
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 @@
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 @@
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 @@
// ...
// }
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 91%
rename from src/cmd/internal/gc/y.go
rename to src/cmd/compile/internal/gc/y.go
index f2c8b96..56b9d04 100644
--- a/src/cmd/internal/gc/y.go
+++ b/src/cmd/compile/internal/gc/y.go
@@ -5,10 +5,11 @@
//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 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 @@
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 yyParserImpl struct {
lookahead func() int
- state func() int
}
func (p *yyParserImpl) Lookahead() int {
@@ -887,7 +915,6 @@
func yyNewParser() yyParser {
p := &yyParserImpl{
lookahead: func() int { return -1 },
- state: func() int { return -1 },
}
return p
}
@@ -918,6 +945,13 @@
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 @@
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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
case 55:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:475
+ //line go.y:537
{
var n, nn *Node
@@ -1594,7 +1627,7 @@
}
case 56:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:498
+ //line go.y:560
{
var n *Node
@@ -1614,7 +1647,7 @@
}
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 @@
}
case 58:
yyDollar = yyS[yypt-2 : yypt+1]
- //line go.y:525
+ //line go.y:587
{
var n, nn *Node
@@ -1646,13 +1679,13 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
case 205:
yyDollar = yyS[yypt-5 : yypt+1]
- //line go.y:1338
+ //line go.y:1400
{
var t *Node
@@ -2527,14 +2560,14 @@
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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
case 236:
yyDollar = yyS[yypt-1 : yypt+1]
- //line go.y:1623
+ //line go.y:1685
{
var n *Node
@@ -2819,7 +2852,7 @@
}
case 237:
yyDollar = yyS[yypt-3 : yypt+1]
- //line go.y:1633
+ //line go.y:1695
{
var pkg *Pkg
@@ -2834,33 +2867,33 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
case 331:
yyDollar = yyS[yypt-4 : yypt+1]
- //line go.y:2095
+ //line go.y:2157
{
var t *Type
@@ -3338,7 +3371,7 @@
}
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 @@
}
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 @@
}
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 @@
}
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 e4a5e5c..2821702 100644
--- a/src/cmd/internal/gc/y.output
+++ b/src/cmd/compile/internal/gc/y.output
@@ -3,7 +3,7 @@
$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 @@
package: . (2)
LPACKAGE shift 5
- . reduce 2 (src line 131)
+ . reduce 2 (src line 193)
package goto 4
@@ -37,7 +37,7 @@
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 @@
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 @@
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 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 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 @@
import_safety: . (19)
LNAME shift 24
- . reduce 19 (src line 264)
+ . reduce 19 (src line 326)
import_safety goto 23
@@ -140,7 +140,7 @@
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 @@
'*' 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 20
package: LPACKAGE sym ';'. (3)
- . reduce 3 (src line 138)
+ . reduce 3 (src line 200)
state 21
@@ -271,7 +271,7 @@
state 24
import_safety: LNAME. (20)
- . reduce 20 (src line 265)
+ . reduce 20 (src line 327)
state 25
@@ -284,25 +284,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 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 40
non_dcl_stmt: LFALL. (263)
- . reduce 263 (src line 1756)
+ . reduce 263 (src line 1818)
state 41
@@ -420,7 +420,7 @@
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 @@
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 @@
'[' 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 47
lconst: LCONST. (38)
- . reduce 38 (src line 354)
+ . reduce 38 (src line 416)
state 48
@@ -593,7 +593,7 @@
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 @@
'/' 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 @@
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 @@
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 @@
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 @@
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 @@
'(' shift 160
'.' shift 161
'[' shift 162
- . reduce 114 (src line 877)
+ . reduce 114 (src line 939)
state 58
@@ -1027,7 +1027,7 @@
pexpr: pexpr_no_paren. (146)
'{' shift 171
- . reduce 146 (src line 1054)
+ . reduce 146 (src line 1116)
state 67
@@ -1078,19 +1078,19 @@
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 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 @@
'[' 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 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 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 @@
$$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 88
import_here: LLITERAL. (15)
- . reduce 15 (src line 224)
+ . reduce 15 (src line 286)
state 89
@@ -1336,7 +1336,7 @@
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 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 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 109
common_dcl: LTYPE typedcl. (35)
- . reduce 35 (src line 341)
+ . reduce 35 (src line 403)
state 110
@@ -1577,7 +1577,7 @@
state 112
typedclname: sym. (47)
- . reduce 47 (src line 395)
+ . reduce 47 (src line 457)
state 113
@@ -1585,7 +1585,7 @@
fnbody: . (210)
'{' shift 242
- . reduce 210 (src line 1457)
+ . reduce 210 (src line 1519)
fnbody goto 241
@@ -1607,7 +1607,7 @@
'[' 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 @@
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 123
name: sym. (162)
- . reduce 162 (src line 1158)
+ . reduce 162 (src line 1220)
state 124
@@ -1710,23 +1710,23 @@
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 @@
oexpr_list: expr_list. (293)
',' shift 155
- . reduce 293 (src line 1909)
+ . reduce 293 (src line 1971)
state 129
@@ -1780,7 +1780,7 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 276 (src line 1840)
+ . reduce 276 (src line 1902)
state 130
@@ -1827,13 +1827,13 @@
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 @@
'[' 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 @@
'[' 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 @@
'[' 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 @@
'[' 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 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 @@
'/' 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 @@
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 @@
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 @@
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 @@
'*' 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 185
fnliteral: fnlitdcl error. (217)
- . reduce 217 (src line 1496)
+ . reduce 217 (src line 1558)
state 186
@@ -3463,13 +3463,13 @@
'/' 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 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 @@
dotname: name.'.' sym
'.' shift 314
- . reduce 189 (src line 1234)
+ . reduce 189 (src line 1296)
state 198
@@ -3665,27 +3665,27 @@
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 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 213
hidden_pkg_importsym: hidden_importsym. (310)
- . reduce 310 (src line 1985)
+ . reduce 310 (src line 2047)
state 214
@@ -3807,7 +3807,7 @@
state 216
hidden_pkgtype: hidden_pkg_importsym. (311)
- . reduce 311 (src line 1992)
+ . reduce 311 (src line 2054)
state 217
@@ -3815,7 +3815,7 @@
fnbody: . (210)
'{' shift 242
- . reduce 210 (src line 1457)
+ . reduce 210 (src line 1519)
fnbody goto 353
@@ -3845,20 +3845,20 @@
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 @@
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 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 @@
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 @@
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 @@
'*' 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 @@
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 @@
'[' 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 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 @@
'[' 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 @@
'[' 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 @@
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 @@
'*' 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 @@
'[' 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 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 50 (src line 425)
+ . reduce 50 (src line 487)
state 255
@@ -4502,7 +4502,7 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 94 (src line 795)
+ . reduce 94 (src line 857)
state 256
@@ -4545,7 +4545,7 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 95 (src line 799)
+ . reduce 95 (src line 861)
state 257
@@ -4582,7 +4582,7 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 96 (src line 803)
+ . reduce 96 (src line 865)
state 258
@@ -4619,7 +4619,7 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 97 (src line 807)
+ . reduce 97 (src line 869)
state 259
@@ -4656,7 +4656,7 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 98 (src line 811)
+ . reduce 98 (src line 873)
state 260
@@ -4693,7 +4693,7 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 99 (src line 815)
+ . reduce 99 (src line 877)
state 261
@@ -4730,7 +4730,7 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 100 (src line 819)
+ . reduce 100 (src line 881)
state 262
@@ -4767,7 +4767,7 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 101 (src line 823)
+ . reduce 101 (src line 885)
state 263
@@ -4800,7 +4800,7 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 102 (src line 827)
+ . reduce 102 (src line 889)
state 264
@@ -4833,7 +4833,7 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 103 (src line 831)
+ . reduce 103 (src line 893)
state 265
@@ -4866,7 +4866,7 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 104 (src line 835)
+ . reduce 104 (src line 897)
state 266
@@ -4899,7 +4899,7 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 105 (src line 839)
+ . reduce 105 (src line 901)
state 267
@@ -4925,7 +4925,7 @@
expr: expr.LRSH expr
expr: expr.LCOMM expr
- . reduce 106 (src line 843)
+ . reduce 106 (src line 905)
state 268
@@ -4951,7 +4951,7 @@
expr: expr.LRSH expr
expr: expr.LCOMM expr
- . reduce 107 (src line 847)
+ . reduce 107 (src line 909)
state 269
@@ -4977,7 +4977,7 @@
expr: expr.LRSH expr
expr: expr.LCOMM expr
- . reduce 108 (src line 851)
+ . reduce 108 (src line 913)
state 270
@@ -5003,7 +5003,7 @@
expr: expr.LRSH expr
expr: expr.LCOMM expr
- . reduce 109 (src line 855)
+ . reduce 109 (src line 917)
state 271
@@ -5029,7 +5029,7 @@
expr: expr.LRSH expr
expr: expr.LCOMM expr
- . reduce 110 (src line 859)
+ . reduce 110 (src line 921)
state 272
@@ -5055,7 +5055,7 @@
expr: expr.LRSH expr
expr: expr.LCOMM expr
- . reduce 111 (src line 863)
+ . reduce 111 (src line 925)
state 273
@@ -5081,7 +5081,7 @@
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 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 113 (src line 872)
+ . reduce 113 (src line 934)
state 275
@@ -5134,7 +5134,7 @@
expr_list: expr_list.',' expr
',' shift 155
- . reduce 51 (src line 430)
+ . reduce 51 (src line 492)
state 276
@@ -5142,7 +5142,7 @@
expr_list: expr_list.',' expr
',' shift 155
- . reduce 52 (src line 442)
+ . reduce 52 (src line 504)
state 277
@@ -5188,13 +5188,13 @@
'/' 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
'%' shift 147
'&' shift 148
']' shift 396
- . reduce 291 (src line 1903)
+ . reduce 291 (src line 1965)
state 295
@@ -5467,7 +5467,7 @@
'[' 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 @@
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 @@
'%' shift 147
'&' shift 148
',' shift 413
- . reduce 288 (src line 1896)
+ . reduce 288 (src line 1958)
ocomma goto 412
@@ -5609,7 +5609,7 @@
'[' 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 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 311
othertype: LCHAN LCOMM ntype. (194)
- . reduce 194 (src line 1263)
+ . reduce 194 (src line 1325)
state 312
@@ -5753,7 +5753,7 @@
state 313
ptrtype: '*' ntype. (198)
- . reduce 198 (src line 1275)
+ . reduce 198 (src line 1337)
state 314
@@ -5780,20 +5780,20 @@
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 @@
oliteral: . (302)
LLITERAL shift 428
- . reduce 302 (src line 1935)
+ . reduce 302 (src line 1997)
oliteral goto 427
@@ -5861,13 +5861,13 @@
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 @@
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 @@
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 330
interfacedcl: packname. (240)
- . reduce 240 (src line 1658)
+ . reduce 240 (src line 1720)
state 331
@@ -5942,7 +5942,7 @@
'.' 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 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 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 @@
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 356
hidden_funarg_list: hidden_funarg. (346)
- . reduce 346 (src line 2212)
+ . reduce 346 (src line 2274)
state 357
@@ -6191,7 +6191,7 @@
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 @@
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 @@
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 @@
expr_list: expr_list.',' expr
',' shift 155
- . reduce 43 (src line 379)
+ . reduce 43 (src line 441)
state 369
@@ -6379,7 +6379,7 @@
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 @@
'[' 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 @@
'[' 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 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 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 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 @@
'[' 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 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 69 (src line 624)
+ . reduce 69 (src line 686)
state 387
@@ -6728,7 +6728,7 @@
'[' 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 @@
ocomma: . (288)
',' shift 413
- . reduce 288 (src line 1896)
+ . reduce 288 (src line 1958)
ocomma goto 510
@@ -6809,7 +6809,7 @@
'[' 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 396
pexpr_no_paren: pexpr '[' expr ']'. (131)
- . reduce 131 (src line 966)
+ . reduce 131 (src line 1028)
state 397
@@ -6875,7 +6875,7 @@
'[' 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 @@
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 @@
'%' 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 @@
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 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 413
ocomma: ','. (289)
- . reduce 289 (src line 1897)
+ . reduce 289 (src line 1959)
state 414
@@ -7118,7 +7118,7 @@
state 415
fnliteral: fnlitdcl lbrace stmt_list '}'. (216)
- . reduce 216 (src line 1490)
+ . reduce 216 (src line 1552)
state 416
@@ -7128,11 +7128,11 @@
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 @@
'*' 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 @@
'*' 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 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 @@
'(' 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 @@
oliteral: . (302)
LLITERAL shift 428
- . reduce 302 (src line 1935)
+ . reduce 302 (src line 1997)
oliteral goto 529
@@ -7373,13 +7373,13 @@
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 @@
packname: LNAME.'.' sym
'.' shift 434
- . reduce 236 (src line 1621)
+ . reduce 236 (src line 1683)
state 432
@@ -7411,7 +7411,7 @@
oliteral: . (302)
LLITERAL shift 428
- . reduce 302 (src line 1935)
+ . reduce 302 (src line 1997)
oliteral goto 533
@@ -7450,7 +7450,7 @@
'(' 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 437
interfacedcl: new_name indcl. (239)
- . reduce 239 (src line 1652)
+ . reduce 239 (src line 1714)
state 438
@@ -7481,7 +7481,7 @@
'[' 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 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 @@
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 @@
'[' 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 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 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 @@
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 458
hidden_constant: hidden_literal. (342)
- . reduce 342 (src line 2195)
+ . reduce 342 (src line 2257)
state 459
@@ -7741,7 +7741,7 @@
state 460
hidden_literal: LLITERAL. (339)
- . reduce 339 (src line 2164)
+ . reduce 339 (src line 2226)
state 461
@@ -7754,7 +7754,7 @@
state 462
hidden_literal: sym. (341)
- . reduce 341 (src line 2187)
+ . reduce 341 (src line 2249)
state 463
@@ -7776,13 +7776,13 @@
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 @@
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 @@
oliteral: . (302)
LLITERAL shift 428
- . reduce 302 (src line 1935)
+ . reduce 302 (src line 1997)
oliteral goto 565
@@ -7856,13 +7856,13 @@
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 @@
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 @@
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 @@
'?' 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 @@
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 @@
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 @@
'[' 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 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 @@
'[' 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 @@
'[' 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 @@
'*' 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 @@
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 @@
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 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 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 @@
'[' 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 517
braced_keyval_list: keyval_list ocomma. (285)
- . reduce 285 (src line 1885)
+ . reduce 285 (src line 1947)
state 518
@@ -8546,7 +8546,7 @@
'[' 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 @@
'[' 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 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 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 @@
oliteral: . (302)
LLITERAL shift 428
- . reduce 302 (src line 1935)
+ . reduce 302 (src line 1997)
oliteral goto 594
@@ -8700,7 +8700,7 @@
state 533
structdcl: '*' embed oliteral. (233)
- . reduce 233 (src line 1600)
+ . reduce 233 (src line 1662)
state 534
@@ -8713,19 +8713,19 @@
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 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 @@
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 @@
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 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 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 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 560
hidden_literal: '-' LLITERAL. (340)
- . reduce 340 (src line 2169)
+ . reduce 340 (src line 2231)
state 561
@@ -8934,7 +8934,7 @@
'(' 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 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 @@
oliteral: . (302)
LLITERAL shift 428
- . reduce 302 (src line 1935)
+ . reduce 302 (src line 1997)
oliteral goto 615
@@ -8986,7 +8986,7 @@
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 @@
constdcl1: dcl_name_list ntype. (45)
'=' shift 367
- . reduce 45 (src line 386)
+ . reduce 45 (src line 448)
state 570
@@ -9020,7 +9020,7 @@
'[' 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 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 @@
'[' 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 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 67 (src line 610)
+ . reduce 67 (src line 672)
state 576
@@ -9195,7 +9195,7 @@
'/' shift 146
'%' shift 147
'&' shift 148
- . reduce 68 (src line 617)
+ . reduce 68 (src line 679)
state 577
@@ -9217,11 +9217,11 @@
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 @@
'*' 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 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 @@
'[' 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 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 @@
'/' 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 @@
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 @@
oliteral: . (302)
LLITERAL shift 428
- . reduce 302 (src line 1935)
+ . reduce 302 (src line 1997)
oliteral goto 633
@@ -9490,7 +9490,7 @@
oliteral: . (302)
LLITERAL shift 428
- . reduce 302 (src line 1935)
+ . reduce 302 (src line 1997)
oliteral goto 634
@@ -9510,7 +9510,7 @@
'[' 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 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 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 @@
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 @@
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 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 @@
'(' 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 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 @@
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 613
hidden_funres: hidden_type. (338)
- . reduce 338 (src line 2155)
+ . reduce 338 (src line 2217)
state 614
@@ -9715,7 +9715,7 @@
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 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 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 @@
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 @@
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 @@
'[' 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 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 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 @@
'[' 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 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 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 @@
'(' 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 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 @@
'(' 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 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 @@
'[' 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 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 5d24a6f..37dd6ce 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 a2f4a0e..73aef6f 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 @@
gc.Widthreg = 8
}
-func main() {
+func Main() {
gc.Thearch.Thechar = thechar
gc.Thearch.Thestring = thestring
gc.Thearch.Thelinkarch = thelinkarch
@@ -72,6 +72,7 @@
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 28ebd9c..1b936b8 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 @@
// 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 @@
check := 0
if gc.Issigned[t.Etype] {
check = 1
- if gc.Isconst(nl, gc.CTINT) && gc.Mpgetfix(nl.Val.U.Xval) != -(1<<uint64(t.Width*8-1)) {
+ if gc.Isconst(nl, gc.CTINT) && nl.Int() != -(1<<uint64(t.Width*8-1)) {
check = 0
- } else if gc.Isconst(nr, gc.CTINT) && gc.Mpgetfix(nr.Val.U.Xval) != -1 {
+ } else if gc.Isconst(nr, gc.CTINT) && nr.Int() != -1 {
check = 0
}
}
@@ -308,7 +308,7 @@
var n1 gc.Node
gc.Regalloc(&n1, nl.Type, res)
gc.Cgen(nl, &n1)
- sc := uint64(uint64(gc.Mpgetfix(nr.Val.U.Xval)))
+ sc := uint64(nr.Int())
if sc >= 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 8223fe7..2501972 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 @@
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 @@
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 @@
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 @@
}
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 4a134f1..1704f63 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 94c9b15..16eeb39 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 e28e389..c7e1827 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 fb0c2e3..fa1cb71 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 dfbdafe..1768674 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 @@
*/
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 a682e2f..0b061ff 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 @@
// 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 @@
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 @@
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 @@
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 e96b628..2b602e1 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 @@
gc.Widthreg = 4
}
-func main() {
+func Main() {
gc.Thearch.Thechar = thechar
gc.Thearch.Thestring = thestring
gc.Thearch.Thelinkarch = thelinkarch
@@ -81,6 +81,7 @@
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 5902552..dabc139 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 @@
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 @@
check := 0
if gc.Issigned[t.Etype] {
check = 1
- if gc.Isconst(nl, gc.CTINT) && gc.Mpgetfix(nl.Val.U.Xval) != -1<<uint64(t.Width*8-1) {
+ if gc.Isconst(nl, gc.CTINT) && nl.Int() != -1<<uint64(t.Width*8-1) {
check = 0
- } else if gc.Isconst(nr, gc.CTINT) && gc.Mpgetfix(nr.Val.U.Xval) != -1 {
+ } else if gc.Isconst(nr, gc.CTINT) && nr.Int() != -1 {
check = 0
}
}
@@ -391,7 +391,7 @@
var n1 gc.Node
gc.Regalloc(&n1, nl.Type, res)
gmove(&n2, &n1)
- sc := uint64(gc.Mpgetfix(nr.Val.U.Xval))
+ sc := uint64(nr.Int())
if sc >= 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 94%
rename from src/cmd/8g/gsubr.go
rename to src/cmd/compile/internal/x86/gsubr.go
index 34ddfe0..baf2517 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 @@
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 @@
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 @@
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 @@
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 @@
}
}
-/*
- * 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 @@
// 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 @@
// 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 e309aea7..8b50eab 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 1346c20..f96a1aa 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 50b5b97..8c97171 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 0000000..7b69c34
--- /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 64b2399..0cdb7d6 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -20,7 +20,6 @@
goarch string
gobin string
gohostarch string
- gohostchar string
gohostos string
goos string
goarm string
@@ -30,10 +29,8 @@
goextlinkenabled string
workdir string
tooldir string
- gochar string
oldgoos string
oldgoarch string
- oldgochar string
slash string
exe string
defaultcc string
@@ -48,17 +45,13 @@
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 @@
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 @@
// 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 @@
}
// 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 @@
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 @@
} 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 @@
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 @@
// 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 @@
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 @@
// 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 @@
// 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 f5037fa..7988129 100644
--- a/src/cmd/dist/buildtool.go
+++ b/src/cmd/dist/buildtool.go
@@ -23,31 +23,34 @@
// 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 1ed0995..f5a0dc5 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -14,6 +14,7 @@
"os/exec"
"path/filepath"
"regexp"
+ "runtime"
"strconv"
"strings"
"time"
@@ -23,8 +24,11 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
},
})
- 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 @@
},
})
}
- 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 @@
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 @@
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 @@
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 @@
})
}
+ // 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 b3be2a9..18dafc2 100644
--- a/src/cmd/doc/main.go
+++ b/src/cmd/doc/main.go
@@ -132,11 +132,12 @@
// 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 3a0aa7f..5c8976b 100644
--- a/src/cmd/doc/pkg.go
+++ b/src/cmd/doc/pkg.go
@@ -115,7 +115,7 @@
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 @@
pkg.valueSummary(pkg.doc.Vars)
pkg.funcSummary(pkg.doc.Funcs)
pkg.typeSummary()
+ pkg.bugs()
}
// packageClause prints the package clause.
@@ -253,6 +254,18 @@
}
}
+// 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 @@
}
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 @@
}
}
-// 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 @@
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 e0d4a6c..2b1cbf9 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -503,6 +503,7 @@
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 @@
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 fda126b..17ff7e0 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -547,9 +547,6 @@
goarch string
goos string
exeSuffix string
-
- archCharVal string
- archCharErr error
)
func init() {
@@ -558,16 +555,6 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
// 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 @@
}
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 @@
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 @@
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 @@
}
// 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 @@
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 @@
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 @@
// 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 863eb4d..8d427b3 100644
--- a/src/cmd/go/env.go
+++ b/src/cmd/go/env.go
@@ -49,10 +49,6 @@
{"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 65dc3ca..1722b9d 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 @@
}
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 56e8493..2062f0c 100644
--- a/src/cmd/go/help.go
+++ b/src/cmd/go/help.go
@@ -47,7 +47,7 @@
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 9466aad..601c303 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -394,25 +394,17 @@
// 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 @@
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 @@
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 408104d..2179000 100644
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -115,8 +115,12 @@
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 b5e5d1e..a1519c8 100644
--- a/src/cmd/internal/asm/lexbody.go
+++ b/src/cmd/internal/asm/lexbody.go
@@ -149,7 +149,7 @@
}
fi.P = nil
- obj.Linklinehist(Ctxt, int(Lineno), s, 0)
+ Ctxt.LineHist.Push(int(Lineno), s)
}
var thetext *obj.LSym
@@ -630,7 +630,7 @@
n, _ = i.F.Read(i.B[:])
if n == 0 {
i.F.Close()
- obj.Linklinehist(Ctxt, int(Lineno), "<pop>", 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 c488ea1..4565d3a 100644
--- a/src/cmd/internal/asm/macbody.go
+++ b/src/cmd/internal/asm/macbody.go
@@ -32,7 +32,6 @@
import (
"bytes"
- "cmd/internal/obj"
"fmt"
"os"
"strings"
@@ -683,7 +682,7 @@
}
nn:
- obj.Linklinehist(Ctxt, int(Lineno), symb, int(n))
+ Ctxt.LineHist.Update(int(Lineno), symb, int(n))
return
bad:
@@ -796,7 +795,7 @@
/*
* 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 8370a20..0000000
--- 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 9dc54d9..0000000
--- 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 cb45cb8..0000000
--- 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 0000000..5845f7d
--- /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 6e00cb5..9e64393 100644
--- a/src/cmd/internal/obj/arm64/asm7.go
+++ b/src/cmd/internal/obj/arm64/asm7.go
@@ -2677,8 +2677,8 @@
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 dde5d64..5486f0d 100644
--- a/src/cmd/internal/obj/line_test.go
+++ b/src/cmd/internal/obj/line_test.go
@@ -13,13 +13,13 @@
ctxt := new(Link)
ctxt.Hash = make(map[SymVer]*LSym)
- Linklinehist(ctxt, 1, "a.c", 0)
- Linklinehist(ctxt, 3, "a.h", 0)
- Linklinehist(ctxt, 5, "<pop>", 0)
- Linklinehist(ctxt, 7, "linedir", 2)
- Linklinehist(ctxt, 9, "<pop>", 0)
- Linklinehist(ctxt, 11, "b.c", 0)
- Linklinehist(ctxt, 13, "<pop>", 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 9f5e87b..2fc12c1 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -206,7 +206,6 @@
From Addr
From3 Addr
To Addr
- To2 Addr
Opt interface{}
Forwd *Prog
Pcond *Prog
@@ -217,12 +216,12 @@
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 2407dea..a385d60 100644
--- a/src/cmd/internal/obj/mgc0.go
+++ b/src/cmd/internal/obj/mgc0.go
@@ -22,16 +22,6 @@
// 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
InsArrayEnd
diff --git a/src/cmd/internal/obj/obj.go b/src/cmd/internal/obj/obj.go
index 39db239..af3290d 100644
--- a/src/cmd/internal/obj/obj.go
+++ b/src/cmd/internal/obj/obj.go
@@ -241,12 +241,6 @@
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 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 != "<pop>" and n == 0, the call pushes the start of a new file named f at lineno.
-// If f != "<pop>" and n > 0, the call amends the top of the stack to record that lineno
-// now corresponds to f at line n.
-// If f == "<pop>", 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 == "<pop>":
- 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 b0b2091..efecae6 100644
--- a/src/cmd/internal/obj/util.go
+++ b/src/cmd/internal/obj/util.go
@@ -213,10 +213,17 @@
}
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 (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 @@
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) 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 0c0cc04..2b9c267 100644
--- a/src/cmd/internal/obj/x86/asm6.go
+++ b/src/cmd/internal/obj/x86/asm6.go
@@ -2974,15 +2974,12 @@
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 7a4fc12..4798c8f 100644
--- a/src/cmd/internal/obj/x86/obj6.go
+++ b/src/cmd/internal/obj/x86/obj6.go
@@ -350,9 +350,6 @@
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 a6cd02b..7371c0d 100644
--- a/src/cmd/internal/objfile/macho.go
+++ b/src/cmd/internal/objfile/macho.go
@@ -13,6 +13,8 @@
"sort"
)
+const stabTypeMask = 0xe0
+
type machoFile struct {
macho *macho.File
}
@@ -34,12 +36,19 @@
// 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 a025ce6..74ec9dd3 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 @@
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 @@
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 @@
// 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 @@
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 @@
return
}
- adddynsym(ld.Ctxt, s)
+ ld.Adddynsym(ld.Ctxt, s)
if ld.Iself {
plt := ld.Linklookup(ld.Ctxt, ".plt", 0)
@@ -612,7 +594,7 @@
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 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 @@
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 95%
rename from src/cmd/6l/l.go
rename to src/cmd/link/internal/amd64/l.go
index 6b42088..2537419 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 @@
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 9e6dc60..1aa4422 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 @@
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 @@
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 @@
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 0000000..f70035b
--- /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 86%
rename from src/cmd/5l/asm.go
rename to src/cmd/link/internal/arm/asm.go
index 85ea684..39d4550 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 @@
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 @@
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 @@
return
}
- adddynsym(ctxt, s)
+ ld.Adddynsym(ctxt, s)
if ld.Iself {
plt := ld.Linklookup(ctxt, ".plt", 0)
@@ -495,7 +477,7 @@
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 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 @@
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 @@
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 97%
rename from src/cmd/5l/l.go
rename to src/cmd/link/internal/arm/l.go
index a521545..49737721 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 @@
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 fa74908..14fe7a6 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 @@
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 a17899d..3aebd8a 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 @@
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 @@
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 @@
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 @@
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 @@
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 97%
rename from src/cmd/7l/l.go
rename to src/cmd/link/internal/arm64/l.go
index 6f90acb..8d0d57e 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 @@
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 f8ac7d3..56f5815 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 @@
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 3194bd5..fd1cdd6 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 @@
} 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 @@
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 @@
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 @@
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 @@
/* 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 @@
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 @@
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 754c89f..c1cf4d7 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 @@
// 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 @@
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_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 6d90404..b8fb2e6b 100644
--- a/src/cmd/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -17,6 +17,7 @@
import (
"cmd/internal/obj"
"fmt"
+ "os"
"strings"
)
@@ -240,6 +241,7 @@
{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 @@
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 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 @@
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 @@
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 @@
const (
CIERESERVE = 16
DATAALIGNMENTFACTOR = -4
- FAKERETURNCOLUMN = 16 // TODO gdb6 doesn't like > 15?
)
func putpccfadelta(deltapc int64, cfa int64) {
@@ -1778,21 +1798,30 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
/*
* 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 @@
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 @@
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 5c17b2d..68d21f4 100644
--- a/src/cmd/internal/ld/elf.go
+++ b/src/cmd/link/internal/ld/elf.go
@@ -6,8 +6,12 @@
import (
"cmd/internal/obj"
+ "crypto/sha1"
"encoding/binary"
"fmt"
+ "path/filepath"
+ "sort"
+ "strings"
)
/*
@@ -1199,32 +1203,15 @@
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 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 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 @@
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 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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 0223bfa..80a6c6e 100644
--- a/src/cmd/internal/ld/go.go
+++ b/src/cmd/link/internal/ld/go.go
@@ -416,7 +416,11 @@
// 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 @@
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 @@
}
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 @@
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 7242301..1068bdd 100644
--- a/src/cmd/internal/ld/ld.go
+++ b/src/cmd/link/internal/ld/ld.go
@@ -109,8 +109,8 @@
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 184175e..d87f180 100644
--- a/src/cmd/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -34,7 +34,9 @@
"bufio"
"bytes"
"cmd/internal/obj"
+ "crypto/sha1"
"debug/elf"
+ "encoding/binary"
"fmt"
"io"
"io/ioutil"
@@ -86,15 +88,14 @@
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 @@
Length uint64
Next *Section
Seg *Segment
- Elfsect interface{}
+ Elfsect *ElfShdr
Reloff uint64
Rellen uint64
}
@@ -178,6 +179,7 @@
Thelinkarch *LinkArch
outfile string
dynexp []*LSym
+ dynlib []string
ldflag []string
havedynamic int
Funcalign int
@@ -473,7 +475,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 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 03da52a..33b17c5 100644
--- a/src/cmd/internal/ld/link.go
+++ b/src/cmd/link/internal/ld/link.go
@@ -34,6 +34,7 @@
"cmd/internal/obj"
"debug/elf"
"encoding/binary"
+ "fmt"
)
type LSym struct {
@@ -77,13 +78,19 @@
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 @@
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 @@
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 @@
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 ceeb7b0..3a8a881 100644
--- a/src/cmd/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -443,7 +443,8 @@
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 @@
}
// 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 @@
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 @@
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 0000000..9134373
--- /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 98%
rename from src/cmd/internal/ld/objfile.go
rename to src/cmd/link/internal/ld/objfile.go
index 3d59323..613fcb2 100644
--- a/src/cmd/internal/ld/objfile.go
+++ b/src/cmd/link/internal/ld/objfile.go
@@ -347,7 +347,7 @@
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 8568744..5b2442805 100644
--- a/src/cmd/internal/ld/pobj.go
+++ b/src/cmd/link/internal/ld/pobj.go
@@ -160,10 +160,9 @@
}
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 d6e79dc..7ceb64f 100644
--- a/src/cmd/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -32,6 +32,8 @@
import (
"cmd/internal/obj"
+ "fmt"
+ "path/filepath"
"strings"
)
@@ -128,12 +130,12 @@
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 @@
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 @@
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 @@
}
}
+ 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 @@
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 257f23e..f070921 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 @@
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 @@
return
}
- adddynsym(ctxt, s)
+ ld.Adddynsym(ctxt, s)
if ld.Iself {
plt := ld.Linklookup(ctxt, ".plt", 0)
@@ -622,70 +604,6 @@
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 97%
rename from src/cmd/9l/l.go
rename to src/cmd/link/internal/ppc64/l.go
index e7dc102..1275a34 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 @@
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 46a9239..d663b6e 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 @@
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 7231379..d30bd48 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 @@
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 @@
// 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 @@
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 @@
return
}
- adddynsym(ctxt, s)
+ ld.Adddynsym(ctxt, s)
if ld.Iself {
plt := ld.Linklookup(ctxt, ".plt", 0)
@@ -480,7 +462,7 @@
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 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 @@
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 6005085..8a811ff 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 @@
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 7b490ae..ee408f7 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 @@
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 b23f3f8..0e6c34e 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 0000000..b23f3f8
--- /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
Binary files differ
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
Binary files differ
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
Binary files differ
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
Binary files differ
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
Binary files differ
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
Binary files differ
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 9c33f4f..cd32020 100644
--- a/src/cmd/pack/pack_test.go
+++ b/src/cmd/pack/pack_test.go
@@ -13,7 +13,6 @@
"os"
"os/exec"
"path/filepath"
- "regexp"
"runtime"
"testing"
"time"
@@ -223,16 +222,14 @@
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 @@
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 @@
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 bfc8110..e4c92cd 100644
--- a/src/cmd/pprof/internal/profile/legacy_profile.go
+++ b/src/cmd/pprof/internal/profile/legacy_profile.go
@@ -554,9 +554,10 @@
}
}
- 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 5c7b0b7..53c0fab 100644
--- a/src/cmd/yacc/yacc.go
+++ b/src/cmd/yacc/yacc.go
@@ -128,6 +128,7 @@
TYPEDEF
TYPENAME
UNION
+ ERROR
)
const ENDFILE = 0
@@ -325,8 +326,24 @@
{"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 @@
}
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 @@
}
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 @@
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 @@
}
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 @@
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 $$ParserImpl struct {
lookahead func() int
- state func() int
}
func (p *$$ParserImpl) Lookahead() int {
@@ -3222,7 +3339,6 @@
func $$NewParser() $$Parser {
p := &$$ParserImpl{
lookahead: func() int { return -1 },
- state: func() int { return -1 },
}
return p
}
@@ -3253,6 +3369,13 @@
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 @@
$$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 6855a65..c6943a6 100644
--- a/src/debug/gosym/pclntab_test.go
+++ b/src/debug/gosym/pclntab_test.go
@@ -49,7 +49,7 @@
// 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 d0acaba..31223b6 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 @@
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 @@
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 @@
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 @@
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 a340e47..62d0f42 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 @@
// 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 c0bd379..8a72a31 100644
--- a/src/encoding/gob/encoder_test.go
+++ b/src/encoding/gob/encoder_test.go
@@ -976,3 +976,21 @@
}
}
}
+
+// 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 f26a7d4..613641a 100644
--- a/src/encoding/json/decode.go
+++ b/src/encoding/json/decode.go
@@ -739,7 +739,7 @@
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 7ecc8f4..f208ee8 100644
--- a/src/encoding/json/decode_test.go
+++ b/src/encoding/json/decode_test.go
@@ -1207,7 +1207,28 @@
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 4db9f35..7789bb5 100644
--- a/src/encoding/json/encode.go
+++ b/src/encoding/json/encode.go
@@ -275,8 +275,6 @@
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 7880342..66383ef 100644
--- a/src/encoding/json/scanner_test.go
+++ b/src/encoding/json/scanner_test.go
@@ -209,6 +209,7 @@
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 4e42790..0606602 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 ba99cb0..93121bb 100644
--- a/src/fmt/fmt_test.go
+++ b/src/fmt/fmt_test.go
@@ -557,10 +557,13 @@
{"%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 _ 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 099f8a5..ba984cf 100644
--- a/src/fmt/format.go
+++ b/src/fmt/format.go
@@ -163,7 +163,7 @@
}
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 @@
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 0000000..1267cc3
--- /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 0000000..ae3147a
--- /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 124da40..db6bdcf 100644
--- a/src/go/build/build.go
+++ b/src/go/build/build.go
@@ -296,11 +296,7 @@
// 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 @@
// 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 @@
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 84c1e2a..8e985aa 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -184,42 +184,43 @@
},
// 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 @@
// 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 @@
// 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 @@
// 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 78e17b2..233f8b9 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 f445b82..a4a838a 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 c698fa6..2ab6da0 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 ad4533c..79a80af 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 6a74e2d..08cdd5e 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 ee83a72..7278c0c 100644
--- a/src/go/internal/gcimporter/gcimporter.go
+++ b/src/go/internal/gcimporter/gcimporter.go
@@ -18,14 +18,14 @@
"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 5d4de39..fe4a758 100644
--- a/src/go/internal/gcimporter/gcimporter_test.go
+++ b/src/go/internal/gcimporter/gcimporter_test.go
@@ -50,9 +50,8 @@
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 4910305..f3bc4b9 100644
--- a/src/go/parser/interface.go
+++ b/src/go/parser/interface.go
@@ -91,7 +91,10 @@
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 0095d7f..18278ba 100644
--- a/src/go/parser/parser.go
+++ b/src/go/parser/parser.go
@@ -412,14 +412,17 @@
}
}
-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 @@
// 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 @@
// 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 @@
ellipsis = p.pos
p.next()
}
- if !p.atComma("argument list") {
+ if !p.atComma("argument list", token.RPAREN) {
break
}
p.next()
@@ -1323,7 +1326,7 @@
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 @@
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 4b960d9..c7bb36d 100644
--- a/src/go/parser/parser_test.go
+++ b/src/go/parser/parser_test.go
@@ -492,3 +492,42 @@
})
}
}
+
+// 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 970ef2d..ef2ffad 100644
--- a/src/go/parser/short_test.go
+++ b/src/go/parser/short_test.go
@@ -99,8 +99,9 @@
`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 cec82ea..e9476c4 100644
--- a/src/go/scanner/scanner.go
+++ b/src/go/scanner/scanner.go
@@ -706,13 +706,14 @@
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 fc450d8..0d21905 100644
--- a/src/go/scanner/scanner_test.go
+++ b/src/go/scanner/scanner_test.go
@@ -734,6 +734,41 @@
}
}
+// 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 1a384d4..0000000
--- 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 a2a55e3..ad9baa9 100644
--- a/src/go/types/api.go
+++ b/src/go/types/api.go
@@ -28,7 +28,7 @@
"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 203a9c1..c224699 100644
--- a/src/go/types/builtins.go
+++ b/src/go/types/builtins.go
@@ -8,7 +8,7 @@
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 b4c356a..7ae81eb 100644
--- a/src/go/types/check.go
+++ b/src/go/types/check.go
@@ -8,7 +8,7 @@
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 0cf9953..da65f42 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 c2c18ec..4af5b57 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -6,7 +6,7 @@
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 bc27a8b..36e1cb9 100644
--- a/src/go/types/eval_test.go
+++ b/src/go/types/eval_test.go
@@ -14,7 +14,6 @@
"strings"
"testing"
- _ "go/internal/gcimporter"
. "go/types"
)
diff --git a/src/go/types/expr.go b/src/go/types/expr.go
index f91d89e..425ae91 100644
--- a/src/go/types/expr.go
+++ b/src/go/types/expr.go
@@ -9,7 +9,7 @@
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 2404753..829e7a9 100644
--- a/src/go/types/object.go
+++ b/src/go/types/object.go
@@ -8,7 +8,7 @@
"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 88c3870..8d16706 100644
--- a/src/go/types/operand.go
+++ b/src/go/types/operand.go
@@ -9,7 +9,7 @@
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 be46b59..64dcebe 100644
--- a/src/go/types/resolver.go
+++ b/src/go/types/resolver.go
@@ -7,7 +7,7 @@
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 85dc6ae..4ff4e4d 100644
--- a/src/go/types/self_test.go
+++ b/src/go/types/self_test.go
@@ -15,7 +15,6 @@
"testing"
"time"
- _ "go/internal/gcimporter"
. "go/types"
)
@@ -31,7 +30,7 @@
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 d04dd71..28a66ce 100644
--- a/src/go/types/stdlib_test.go
+++ b/src/go/types/stdlib_test.go
@@ -146,6 +146,7 @@
"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 8b59df3..586f6cc 100644
--- a/src/go/types/stmt.go
+++ b/src/go/types/stmt.go
@@ -9,7 +9,7 @@
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 d08e0fd..595a634 100644
--- a/src/go/types/testdata/issues.src
+++ b/src/go/types/testdata/issues.src
@@ -71,3 +71,27 @@
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 afd1dab..3fc1574 100644
--- a/src/go/types/typexpr.go
+++ b/src/go/types/typexpr.go
@@ -8,7 +8,7 @@
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 @@
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 c02543e..5e445e2 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 dd5dfa7..f50a4b9 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 @@
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 @@
// 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 2d7ad8a..3702626 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 @@
}
}
}
+
+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 634f183..3184648 100644
--- a/src/html/template/css.go
+++ b/src/html/template/css.go
@@ -157,56 +157,20 @@
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 @@
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 ee01fb1..a952944 100644
--- a/src/html/template/escape.go
+++ b/src/html/template/escape.go
@@ -297,9 +297,9 @@
// 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 9c9502a..6729ebf 100644
--- a/src/html/template/escape_test.go
+++ b/src/html/template/escape_test.go
@@ -1547,6 +1547,16 @@
"($).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 9c069ef..de4aa4a 100644
--- a/src/html/template/html.go
+++ b/src/html/template/html.go
@@ -138,21 +138,24 @@
// 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%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 b9b9703..f04ee04 100644
--- a/src/html/template/html_test.go
+++ b/src/html/template/html_test.go
@@ -19,7 +19,8 @@
`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 @@
`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 999a61e..f6d166b 100644
--- a/src/html/template/js.go
+++ b/src/html/template/js.go
@@ -246,8 +246,10 @@
// `\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 @@
}
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 9bc3e5e..0000000
--- 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 0000000..d7a9b9c
--- /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 0000000..47105e0
--- /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 0000000..48e2094
--- /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 0000000..1c89ed1
--- /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 0000000..a769bb3
--- /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 0000000..8badf6f
--- /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 5f75feb..07eccb2 100644
--- a/src/internal/syscall/windows/registry/registry_test.go
+++ b/src/internal/syscall/windows/registry/registry_test.go
@@ -611,3 +611,68 @@
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 1c1771d..bb45a23 100644
--- a/src/internal/syscall/windows/registry/value.go
+++ b/src/internal/syscall/windows/registry/value.go
@@ -130,7 +130,7 @@
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 @@
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 @@
}
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 1ff6349..d7ea838 100644
--- a/src/math/big/arith.go
+++ b/src/math/big/arith.go
@@ -107,11 +107,26 @@
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 @@
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 d2d5187..b69a2c6 100644
--- a/src/math/big/arith_amd64.s
+++ b/src/math/big/arith_amd64.s
@@ -351,6 +351,34 @@
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 3ce2422..14ecab5 100644
--- a/src/math/big/bits_test.go
+++ b/src/math/big/bits_test.go
@@ -203,18 +203,18 @@
}{
// 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 3d024dc..2595e5f 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 @@
// special case 0
if len(m) == 0 {
x.mant = x.mant[:0]
+ x.exp = 0
return
}
@@ -255,4 +258,7 @@
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 d46c046..1563528 100644
--- a/src/math/big/float.go
+++ b/src/math/big/float.go
@@ -525,25 +525,6 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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<<mbits - 1) // cut off msb (implicit 1 bit)
-
// Rounding may have caused a denormal number to
// become normal. Check again.
- c := float32(1.0)
- if r.exp < emin+1 {
+ if e < emin {
// denormal number
- r.exp += mbits
- c = 1.0 / (1 << mbits) // 2**-mbits
+ if e < dmin {
+ // underflow to ±0
+ if x.neg {
+ var z float32
+ return -z, Above
+ }
+ return 0.0, Below
+ }
+ // bexp = 0
+ mant = msb32(r.mant) >> (fbits - r.prec)
+ } else {
+ // normal number: emin <= e <= emax
+ bexp = uint32(e+bias) << mbits
+ mant = msb32(r.mant) >> ebits & (1<<mbits - 1) // cut off msb (implicit 1 bit)
}
- // emin+1 <= r.exp <= emax+1
- e := uint32(r.exp-emin) << mbits
- return c * math.Float32frombits(s|e|m), r.acc
+ return math.Float32frombits(sign | bexp | mant), r.acc
case zero:
if x.neg {
@@ -980,64 +984,71 @@
emax = bias // 1023 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 float64
- 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 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<<mbits - 1) // cut off msb (implicit 1 bit)
-
// Rounding may have caused a denormal number to
// become normal. Check again.
- c := 1.0
- if r.exp < emin+1 {
+ if e < emin {
// denormal number
- r.exp += mbits
- c = 1.0 / (1 << mbits) // 2**-mbits
+ if e < dmin {
+ // underflow to ±0
+ if x.neg {
+ var z float64
+ return -z, Above
+ }
+ return 0.0, Below
+ }
+ // bexp = 0
+ mant = msb64(r.mant) >> (fbits - r.prec)
+ } else {
+ // normal number: emin <= e <= emax
+ bexp = uint64(e+bias) << mbits
+ mant = msb64(r.mant) >> ebits & (1<<mbits - 1) // cut off msb (implicit 1 bit)
}
- // emin+1 <= r.exp <= emax+1
- e := uint64(r.exp-emin) << mbits
- return c * math.Float64frombits(s|e|m), r.acc
+ return math.Float64frombits(sign | bexp | mant), r.acc
case zero:
if x.neg {
diff --git a/src/math/big/float_test.go b/src/math/big/float_test.go
index 5b5a024..7df9fc7 100644
--- a/src/math/big/float_test.go
+++ b/src/math/big/float_test.go
@@ -203,6 +203,18 @@
return x.Cmp(y) == 0 && x.Signbit() == y.Signbit()
}
+func alike32(x, y float32) bool {
+ // we can ignore NaNs
+ return x == y && math.Signbit(float64(x)) == math.Signbit(float64(y))
+
+}
+
+func alike64(x, y float64) bool {
+ // we can ignore NaNs
+ return x == y && math.Signbit(x) == math.Signbit(y)
+
+}
+
func TestFloatMantExp(t *testing.T) {
for _, test := range []struct {
x string
@@ -828,52 +840,69 @@
out float32
acc Accuracy
}{
- {"-Inf", float32(math.Inf(-1)), Exact},
- {"-0x1.ffffff0p2147483646", float32(-math.Inf(+1)), Below}, // overflow in rounding
- {"-1e10000", float32(math.Inf(-1)), Below}, // overflow
- {"-0x1p128", float32(math.Inf(-1)), Below}, // overflow
- {"-0x1.ffffff0p127", float32(-math.Inf(+1)), Below}, // overflow
- {"-0x1.fffffe8p127", -math.MaxFloat32, Above},
- {"-0x1.fffffe0p127", -math.MaxFloat32, Exact},
- {"-12345.000000000000000000001", -12345, Above},
- {"-12345.0", -12345, Exact},
- {"-1.000000000000000000001", -1, Above},
- {"-1", -1, Exact},
- {"-0x0.000002p-126", -math.SmallestNonzeroFloat32, Exact},
- {"-0x0.000002p-127", -0, Above}, // underflow
- {"-1e-1000", -0, Above}, // underflow
{"0", 0, Exact},
- {"1e-1000", 0, Below}, // underflow
- {"0x0.000002p-127", 0, Below}, // underflow
- {"0x0.000002p-126", math.SmallestNonzeroFloat32, Exact},
+
+ // underflow
+ {"1e-1000", 0, Below},
+ {"0x0.000002p-127", 0, Below},
+ {"0x.0000010p-126", 0, Below},
+
+ // denormals
+ {"1.401298464e-45", math.SmallestNonzeroFloat32, Above}, // rounded up to smallest denormal
+ {"0x.ffffff8p-149", math.SmallestNonzeroFloat32, Above}, // rounded up to smallest denormal
+ {"0x.0000018p-126", math.SmallestNonzeroFloat32, Above}, // rounded up to smallest denormal
+ {"0x.0000020p-126", math.SmallestNonzeroFloat32, Exact},
+ {"0x.8p-148", math.SmallestNonzeroFloat32, Exact},
+ {"1p-149", math.SmallestNonzeroFloat32, Exact},
+ {"0x.fffffep-126", math.Float32frombits(0x7fffff), Exact}, // largest denormal
+
+ // normals
+ {"0x.ffffffp-126", math.Float32frombits(0x00800000), Above}, // rounded up to smallest normal
+ {"1p-126", math.Float32frombits(0x00800000), Exact}, // smallest normal
+ {"0x1.fffffep-126", math.Float32frombits(0x00ffffff), Exact},
+ {"0x1.ffffffp-126", math.Float32frombits(0x01000000), Above}, // rounded up
{"1", 1, Exact},
{"1.000000000000000000001", 1, Below},
{"12345.0", 12345, Exact},
{"12345.000000000000000000001", 12345, Below},
{"0x1.fffffe0p127", math.MaxFloat32, Exact},
{"0x1.fffffe8p127", math.MaxFloat32, Below},
- {"0x1.ffffff0p127", float32(math.Inf(+1)), Above}, // overflow
- {"0x1p128", float32(math.Inf(+1)), Above}, // overflow
- {"1e10000", float32(math.Inf(+1)), Above}, // overflow
+
+ // overflow
+ {"0x1.ffffff0p127", float32(math.Inf(+1)), Above},
+ {"0x1p128", float32(math.Inf(+1)), Above},
+ {"1e10000", float32(math.Inf(+1)), Above},
{"0x1.ffffff0p2147483646", float32(math.Inf(+1)), Above}, // overflow in rounding
- {"+Inf", float32(math.Inf(+1)), Exact},
+
+ // inf
+ {"Inf", float32(math.Inf(+1)), Exact},
} {
- // conversion should match strconv where syntax is agreeable
- if f, err := strconv.ParseFloat(test.x, 32); err == nil && float32(f) != test.out {
- t.Errorf("%s: got %g; want %g (incorrect test data)", test.x, f, test.out)
- }
+ for i := 0; i < 2; i++ {
+ // test both signs
+ tx, tout, tacc := test.x, test.out, test.acc
+ if i != 0 {
+ tx = "-" + tx
+ tout = -tout
+ tacc = -tacc
+ }
- x := makeFloat(test.x)
- out, acc := x.Float32()
- if out != test.out || acc != test.acc {
- t.Errorf("%s: got %g (%#x, %s); want %g (%#x, %s)", test.x, out, math.Float32bits(out), acc, test.out, math.Float32bits(test.out), test.acc)
- }
+ // conversion should match strconv where syntax is agreeable
+ if f, err := strconv.ParseFloat(tx, 32); err == nil && !alike32(float32(f), tout) {
+ t.Errorf("%s: got %g; want %g (incorrect test data)", tx, f, tout)
+ }
- // test that x.SetFloat64(float64(f)).Float32() == f
- var x2 Float
- out2, acc2 := x2.SetFloat64(float64(out)).Float32()
- if out2 != out || acc2 != Exact {
- t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out)
+ x := makeFloat(tx)
+ out, acc := x.Float32()
+ if !alike32(out, tout) || acc != tacc {
+ t.Errorf("%s: got %g (%#x, %s); want %g (%#x, %s)", tx, out, math.Float32bits(out), acc, test.out, math.Float32bits(test.out), tacc)
+ }
+
+ // test that x.SetFloat64(float64(f)).Float32() == f
+ var x2 Float
+ out2, acc2 := x2.SetFloat64(float64(out)).Float32()
+ if !alike32(out2, out) || acc2 != Exact {
+ t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out)
+ }
}
}
}
@@ -885,35 +914,36 @@
out float64
acc Accuracy
}{
- {"-Inf", math.Inf(-1), Exact},
- {"-0x1.fffffffffffff8p2147483646", -math.Inf(+1), Below}, // overflow in rounding
- {"-1e10000", math.Inf(-1), Below}, // overflow
- {"-0x1p1024", math.Inf(-1), Below}, // overflow
- {"-0x1.fffffffffffff8p1023", -math.Inf(+1), Below}, // overflow
- {"-0x1.fffffffffffff4p1023", -math.MaxFloat64, Above},
- {"-0x1.fffffffffffff0p1023", -math.MaxFloat64, Exact},
- {"-12345.000000000000000000001", -12345, Above},
- {"-12345.0", -12345, Exact},
- {"-1.000000000000000000001", -1, Above},
- {"-1", -1, Exact},
- {"-0x0.0000000000001p-1022", -math.SmallestNonzeroFloat64, Exact},
- {"-0x0.0000000000001p-1023", -0, Above}, // underflow
- {"-1e-1000", -0, Above}, // underflow
{"0", 0, Exact},
- {"1e-1000", 0, Below}, // underflow
- {"0x0.0000000000001p-1023", 0, Below}, // underflow
- {"0x0.0000000000001p-1022", math.SmallestNonzeroFloat64, Exact},
+
+ // underflow
+ {"1e-1000", 0, Below},
+ {"0x0.0000000000001p-1023", 0, Below},
+ {"0x0.00000000000008p-1022", 0, Below},
+
+ // denormals
+ {"0x0.0000000000000cp-1022", math.SmallestNonzeroFloat64, Above}, // rounded up to smallest denormal
+ {"0x0.0000000000001p-1022", math.SmallestNonzeroFloat64, Exact}, // smallest denormal
+ {"0x.8p-1073", math.SmallestNonzeroFloat64, Exact},
+ {"1p-1074", math.SmallestNonzeroFloat64, Exact},
+ {"0x.fffffffffffffp-1022", math.Float64frombits(0x000fffffffffffff), Exact}, // largest denormal
+
+ // normals
+ {"0x.fffffffffffff8p-1022", math.Float64frombits(0x0010000000000000), Above}, // rounded up to smallest normal
+ {"1p-1022", math.Float64frombits(0x0010000000000000), Exact}, // smallest normal
{"1", 1, Exact},
{"1.000000000000000000001", 1, Below},
{"12345.0", 12345, Exact},
{"12345.000000000000000000001", 12345, Below},
{"0x1.fffffffffffff0p1023", math.MaxFloat64, Exact},
{"0x1.fffffffffffff4p1023", math.MaxFloat64, Below},
- {"0x1.fffffffffffff8p1023", math.Inf(+1), Above}, // overflow
- {"0x1p1024", math.Inf(+1), Above}, // overflow
- {"1e10000", math.Inf(+1), Above}, // overflow
+
+ // overflow
+ {"0x1.fffffffffffff8p1023", math.Inf(+1), Above},
+ {"0x1p1024", math.Inf(+1), Above},
+ {"1e10000", math.Inf(+1), Above},
{"0x1.fffffffffffff8p2147483646", math.Inf(+1), Above}, // overflow in rounding
- {"+Inf", math.Inf(+1), Exact},
+ {"Inf", math.Inf(+1), Exact},
// selected denormalized values that were handled incorrectly in the past
{"0x.fffffffffffffp-1022", smallestNormalFloat64 - math.SmallestNonzeroFloat64, Exact},
@@ -924,22 +954,32 @@
// http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
{"2.2250738585072012e-308", 2.2250738585072014e-308, Above},
} {
- // conversion should match strconv where syntax is agreeable
- if f, err := strconv.ParseFloat(test.x, 64); err == nil && f != test.out {
- t.Errorf("%s: got %g; want %g (incorrect test data)", test.x, f, test.out)
- }
+ for i := 0; i < 2; i++ {
+ // test both signs
+ tx, tout, tacc := test.x, test.out, test.acc
+ if i != 0 {
+ tx = "-" + tx
+ tout = -tout
+ tacc = -tacc
+ }
- x := makeFloat(test.x)
- out, acc := x.Float64()
- if out != test.out || acc != test.acc {
- t.Errorf("%s: got %g (%#x, %s); want %g (%#x, %s)", test.x, out, math.Float64bits(out), acc, test.out, math.Float64bits(test.out), test.acc)
- }
+ // conversion should match strconv where syntax is agreeable
+ if f, err := strconv.ParseFloat(tx, 64); err == nil && !alike64(f, tout) {
+ t.Errorf("%s: got %g; want %g (incorrect test data)", tx, f, tout)
+ }
- // test that x.SetFloat64(f).Float64() == f
- var x2 Float
- out2, acc2 := x2.SetFloat64(out).Float64()
- if out2 != out || acc2 != Exact {
- t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out)
+ x := makeFloat(tx)
+ out, acc := x.Float64()
+ if !alike64(out, tout) || acc != tacc {
+ t.Errorf("%s: got %g (%#x, %s); want %g (%#x, %s)", tx, out, math.Float64bits(out), acc, test.out, math.Float64bits(test.out), tacc)
+ }
+
+ // test that x.SetFloat64(f).Float64() == f
+ var x2 Float
+ out2, acc2 := x2.SetFloat64(out).Float64()
+ if !alike64(out2, out) || acc2 != Exact {
+ t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out)
+ }
}
}
}
@@ -1523,32 +1563,32 @@
x, y, want string
acc Accuracy
}{
- {4, ToNearestEven, '+', "0", "0", "0", Exact}, // smoke test
- {4, ToNearestEven, '+', "0x.8p0", "0x.8p0", "0x.8p1", Exact}, // smoke test
+ {4, ToNearestEven, '+', "0", "0", "0", Exact}, // smoke test
+ {4, ToNearestEven, '+', "0x.8p+0", "0x.8p+0", "0x.8p+1", Exact}, // smoke test
- {4, ToNearestEven, '+', "0", "0x.8p2147483647", "0x.8p2147483647", Exact},
- {4, ToNearestEven, '+', "0x.8p2147483500", "0x.8p2147483647", "0x.8p2147483647", Below}, // rounded to zero
- {4, ToNearestEven, '+', "0x.8p2147483647", "0x.8p2147483647", "+Inf", Above}, // exponent overflow in +
- {4, ToNearestEven, '+', "-0x.8p2147483647", "-0x.8p2147483647", "-Inf", Below}, // exponent overflow in +
- {4, ToNearestEven, '-', "-0x.8p2147483647", "0x.8p2147483647", "-Inf", Below}, // exponent overflow in -
+ {4, ToNearestEven, '+', "0", "0x.8p2147483647", "0x.8p+2147483647", Exact},
+ {4, ToNearestEven, '+', "0x.8p2147483500", "0x.8p2147483647", "0x.8p+2147483647", Below}, // rounded to zero
+ {4, ToNearestEven, '+', "0x.8p2147483647", "0x.8p2147483647", "+Inf", Above}, // exponent overflow in +
+ {4, ToNearestEven, '+', "-0x.8p2147483647", "-0x.8p2147483647", "-Inf", Below}, // exponent overflow in +
+ {4, ToNearestEven, '-', "-0x.8p2147483647", "0x.8p2147483647", "-Inf", Below}, // exponent overflow in -
- {4, ToZero, '+', "0x.fp2147483647", "0x.8p2147483643", "0x.fp2147483647", Below}, // rounded to zero
- {4, ToNearestEven, '+', "0x.fp2147483647", "0x.8p2147483643", "+Inf", Above}, // exponent overflow in rounding
- {4, AwayFromZero, '+', "0x.fp2147483647", "0x.8p2147483643", "+Inf", Above}, // exponent overflow in rounding
+ {4, ToZero, '+', "0x.fp2147483647", "0x.8p2147483643", "0x.fp+2147483647", Below}, // rounded to zero
+ {4, ToNearestEven, '+', "0x.fp2147483647", "0x.8p2147483643", "+Inf", Above}, // exponent overflow in rounding
+ {4, AwayFromZero, '+', "0x.fp2147483647", "0x.8p2147483643", "+Inf", Above}, // exponent overflow in rounding
- {4, AwayFromZero, '-', "-0x.fp2147483647", "0x.8p2147483644", "-Inf", Below}, // exponent overflow in rounding
- {4, ToNearestEven, '-', "-0x.fp2147483647", "0x.8p2147483643", "-Inf", Below}, // exponent overflow in rounding
- {4, ToZero, '-', "-0x.fp2147483647", "0x.8p2147483643", "-0x.fp2147483647", Above}, // rounded to zero
+ {4, AwayFromZero, '-', "-0x.fp2147483647", "0x.8p2147483644", "-Inf", Below}, // exponent overflow in rounding
+ {4, ToNearestEven, '-', "-0x.fp2147483647", "0x.8p2147483643", "-Inf", Below}, // exponent overflow in rounding
+ {4, ToZero, '-', "-0x.fp2147483647", "0x.8p2147483643", "-0x.fp+2147483647", Above}, // rounded to zero
{4, ToNearestEven, '+', "0", "0x.8p-2147483648", "0x.8p-2147483648", Exact},
{4, ToNearestEven, '+', "0x.8p-2147483648", "0x.8p-2147483648", "0x.8p-2147483647", Exact},
- {4, ToNearestEven, '*', "1", "0x.8p2147483647", "0x.8p2147483647", Exact},
+ {4, ToNearestEven, '*', "1", "0x.8p2147483647", "0x.8p+2147483647", Exact},
{4, ToNearestEven, '*', "2", "0x.8p2147483647", "+Inf", Above}, // exponent overflow in *
{4, ToNearestEven, '*', "-2", "0x.8p2147483647", "-Inf", Below}, // exponent overflow in *
{4, ToNearestEven, '/', "0.5", "0x.8p2147483647", "0x.8p-2147483646", Exact},
- {4, ToNearestEven, '/', "0x.8p0", "0x.8p2147483647", "0x.8p-2147483646", Exact},
+ {4, ToNearestEven, '/', "0x.8p+0", "0x.8p2147483647", "0x.8p-2147483646", Exact},
{4, ToNearestEven, '/', "0x.8p-1", "0x.8p2147483647", "0x.8p-2147483647", Exact},
{4, ToNearestEven, '/', "0x.8p-2", "0x.8p2147483647", "0x.8p-2147483648", Exact},
{4, ToNearestEven, '/', "0x.8p-3", "0x.8p2147483647", "0", Below}, // exponent underflow in /
@@ -1659,7 +1699,7 @@
want = +1
}
if got != want {
- t.Errorf("(%g).Cmp(%g) = %s; want %s", x, y, got, want)
+ t.Errorf("(%g).Cmp(%g) = %v; want %v", x, y, got, want)
}
}
}
diff --git a/src/math/big/floatconv.go b/src/math/big/floatconv.go
index b929d12..dc62b45 100644
--- a/src/math/big/floatconv.go
+++ b/src/math/big/floatconv.go
@@ -2,14 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// This file implements float-to-string conversion functions.
+// This file implements string-to-Float conversion functions.
package big
import (
"fmt"
"io"
- "strconv"
"strings"
)
@@ -67,6 +66,7 @@
// defined if an error is reported.
//
// BUG(gri) The Float.Scan signature conflicts with Scan(s fmt.ScanState, ch rune) error.
+// (https://github.com/golang/go/issues/10938)
func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
prec := z.prec
if prec == 0 {
@@ -243,131 +243,3 @@
func ParseFloat(s string, base int, prec uint, mode RoundingMode) (f *Float, b int, err error) {
return new(Float).SetPrec(prec).SetMode(mode).Parse(s, base)
}
-
-// 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.
-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))
-}
-
-// Append appends the string form of the floating-point number x,
-// as generated by x.Format, to buf and returns the extended buffer.
-func (x *Float) Append(buf []byte, format byte, prec int) []byte {
- // TODO(gri) factor out handling of sign?
-
- // Inf
- if x.IsInf() {
- var ch byte = '+'
- if x.neg {
- ch = '-'
- }
- buf = append(buf, ch)
- return append(buf, "Inf"...)
- }
-
- // easy formats
- switch format {
- case 'b':
- return x.bstring(buf)
- case 'p':
- return x.pstring(buf)
- }
-
- return x.bigFtoa(buf, format, prec)
-}
-
-// BUG(gri): Float.String uses x.Format('g', 10) rather than x.Format('g', -1).
-func (x *Float) String() string {
- return x.Format('g', 10)
-}
-
-// bstring 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.
-func (x *Float) bstring(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
-
- // 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)
-}
-
-// 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 96c01ee..9fc2b89 100644
--- a/src/math/big/floatconv_test.go
+++ b/src/math/big/floatconv_test.go
@@ -125,12 +125,16 @@
{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 @@
{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 @@
{"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 @@
{"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 7db1023..d9d39ed 100644
--- a/src/math/big/floatexample_test.go
+++ b/src/math/big/floatexample_test.go
@@ -21,9 +21,9 @@
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 0a9edfd..4c3e743 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 @@
// 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 @@
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 @@
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 @@
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 7157a54..6545bc1 100644
--- a/src/math/big/nat.go
+++ b/src/math/big/nat.go
@@ -216,6 +216,34 @@
}
}
+// 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 @@
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 @@
// 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 @@
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<<n; i++ {
+ powers[i] = powers[i].montgomery(powers[i-1], powers[1], m, k0, numWords)
+ }
+
+ // initialize z = 1 (Montgomery 1)
+ z = z.make(numWords)
+ copy(z, powers[0])
+
+ zz = zz.make(numWords)
+
+ // same windowed exponent, but with Montgomery multiplications
+ for i := len(y) - 1; i >= 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 b25a89f..7ac3cb8 100644
--- a/src/math/big/nat_test.go
+++ b/src/math/big/nat_test.go
@@ -205,11 +205,11 @@
}
}
-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 @@
}
}
+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 ab0dc49..c61494f 100644
--- a/src/math/rand/rand_test.go
+++ b/src/math/rand/rand_test.go
@@ -8,6 +8,8 @@
"errors"
"fmt"
"math"
+ "os"
+ "runtime"
"testing"
)
@@ -322,10 +324,17 @@
}
}
-// 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 0000000..9796f50
--- /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 0000000..02236ea
--- /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?= <eric@example.org>, =?utf-8?q?Ana=C3=AFs?= <anais@example.org>")
+ 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 <eric@example.org>, Anaïs <anais@example.org>
+ // ¡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é" <jose@example.org>`, 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é" <jose@example.org>`},
+ {"=?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 55ea86a..4d5ab23 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 5a4411f..fab515f 100644
--- a/src/net/dnsclient_unix.go
+++ b/src/net/dnsclient_unix.go
@@ -214,95 +214,106 @@
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 @@
// 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 1b88e77..06c9ad3 100644
--- a/src/net/dnsclient_unix_test.go
+++ b/src/net/dnsclient_unix_test.go
@@ -94,10 +94,8 @@
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 @@
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 @@
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) 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 @@
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 @@
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 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 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 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 cc660c9..be07dc6 100644
--- a/src/net/dnsname_test.go
+++ b/src/net/dnsname_test.go
@@ -68,7 +68,7 @@
}
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 c65d3f9..772e0c7 100644
--- a/src/net/error_test.go
+++ b/src/net/error_test.go
@@ -521,7 +521,7 @@
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 20611ff..d5ff2be 100644
--- a/src/net/external_test.go
+++ b/src/net/external_test.go
@@ -15,33 +15,23 @@
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 @@
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 @@
}
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 64e94fe..827045b 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 @@
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 @@
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 @@
}
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 @@
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 1aad477..1d0686c 100644
--- a/src/net/file.go
+++ b/src/net/file.go
@@ -46,3 +46,47 @@
}
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 0000000..ffe3c61
--- /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 "<nil>" }
+
+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 0000000..e04fea3
--- /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 892775a..efe416f 100644
--- a/src/net/file_plan9.go
+++ b/src/net/file_plan9.go
@@ -135,3 +135,11 @@
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 0f7460c..41ca78b 100644
--- a/src/net/file_stub.go
+++ b/src/net/file_stub.go
@@ -11,6 +11,8 @@
"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 147ca1e..df884d1 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 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 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 @@
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 241fa17..1ed72d5 100644
--- a/src/net/file_windows.go
+++ b/src/net/file_windows.go
@@ -23,3 +23,13 @@
// 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 dc499a9..b1d8799 100644
--- a/src/net/http/client_test.go
+++ b/src/net/http/client_test.go
@@ -927,7 +927,14 @@
<-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 9294deb..6d7c698 100644
--- a/src/net/http/internal/chunked.go
+++ b/src/net/http/internal/chunked.go
@@ -173,8 +173,12 @@
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 @@
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 5640344..289d53d 100644
--- a/src/net/http/transfer.go
+++ b/src/net/http/transfer.go
@@ -43,6 +43,7 @@
Close bool
TransferEncoding []string
Trailer Header
+ IsResponse bool
}
func newTransferWriter(r interface{}) (t *transferWriter, err error) {
@@ -89,6 +90,7 @@
}
}
case *Response:
+ t.IsResponse = true
if rr.Request != nil {
t.Method = rr.Request.Method
}
@@ -206,6 +208,9 @@
// 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 ace5889..ca1a3ab 100644
--- a/src/net/http/transport_test.go
+++ b/src/net/http/transport_test.go
@@ -23,6 +23,7 @@
"net/http/httptest"
"net/url"
"os"
+ "reflect"
"runtime"
"strconv"
"strings"
@@ -2447,6 +2448,104 @@
}
}
+// 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 0e5c2e3..567d18d 100644
--- a/src/net/interface_test.go
+++ b/src/net/interface_test.go
@@ -229,7 +229,7 @@
}
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 BenchmarkInterfaceByIndex(b *testing.B) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
+ testHookUninstaller.Do(uninstallTestHooks)
ifi := loopbackInterface()
if ifi == nil {
@@ -253,7 +253,7 @@
}
func BenchmarkInterfaceByName(b *testing.B) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
+ testHookUninstaller.Do(uninstallTestHooks)
ifi := loopbackInterface()
if ifi == nil {
@@ -267,7 +267,7 @@
}
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 BenchmarkInterfacesAndAddrs(b *testing.B) {
- testHookUninstaller.Do(func() { uninstallTestHooks() })
+ testHookUninstaller.Do(uninstallTestHooks)
ifi := loopbackInterface()
if ifi == nil {
@@ -291,7 +291,7 @@
}
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 3ae1c6b..60e581f 100644
--- a/src/net/internal/socktest/main_test.go
+++ b/src/net/internal/socktest/main_test.go
@@ -9,6 +9,7 @@
import (
"net/internal/socktest"
"os"
+ "sync"
"syscall"
"testing"
)
@@ -27,6 +28,21 @@
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 5e558a2..4e38c7a 100644
--- a/src/net/internal/socktest/switch.go
+++ b/src/net/internal/socktest/switch.go
@@ -10,12 +10,6 @@
"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 @@
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 @@
// 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 2b89276..14c0c22 100644
--- a/src/net/internal/socktest/switch_unix.go
+++ b/src/net/internal/socktest/switch_unix.go
@@ -22,7 +22,7 @@
// 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 3cee49b..4f1d597 100644
--- a/src/net/internal/socktest/switch_windows.go
+++ b/src/net/internal/socktest/switch_windows.go
@@ -22,7 +22,7 @@
// 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 4089f8c..f983e26 100644
--- a/src/net/internal/socktest/sys_unix.go
+++ b/src/net/internal/socktest/sys_unix.go
@@ -10,6 +10,8 @@
// 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 907e01b..07af0e7 100644
--- a/src/net/internal/socktest/sys_windows.go
+++ b/src/net/internal/socktest/sys_windows.go
@@ -8,6 +8,8 @@
// 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 24f67ca..b1939cd 100644
--- a/src/net/ip_test.go
+++ b/src/net/ip_test.go
@@ -53,7 +53,7 @@
}
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 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 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 56b9872..83eaf85 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 @@
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 @@
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 8f43c84..89d4d7e 100644
--- a/src/net/listen_test.go
+++ b/src/net/listen_test.go
@@ -218,8 +218,6 @@
// 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 @@
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 @@
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 1f36184..064bc0b 100644
--- a/src/net/lookup_test.go
+++ b/src/net/lookup_test.go
@@ -23,17 +23,34 @@
}
}
+// 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 @@
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 @@
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 @@
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)
+ 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)
+ }
}
- if !strings.HasSuffix(cname, ".icann.org.") {
- t.Errorf("got %q; want a record containing .icann.org.", 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 f3f698c..04cbfd3 100644
--- a/src/net/mail/message.go
+++ b/src/net/mail/message.go
@@ -20,9 +20,9 @@
"bytes"
"errors"
"fmt"
- "internal/mime"
"io"
"log"
+ "mime"
"net/textproto"
"strings"
"time"
@@ -138,12 +138,30 @@
// Parses a single RFC 5322 address, e.g. "Barry Gibbs <bg@example.com>"
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 <gf@example.com>" 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 <gf@example.com>" 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 @@
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 @@
// 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 @@
}, 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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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) 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 6ba48be..43574c6 100644
--- a/src/net/mail/message_test.go
+++ b/src/net/mail/message_test.go
@@ -6,7 +6,9 @@
import (
"bytes"
+ "io"
"io/ioutil"
+ "mime"
"reflect"
"strings"
"testing"
@@ -278,6 +280,175 @@
}
}
+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 <jdoe@machine.example>`,
+ []*Address{{
+ Name: "John Doe",
+ Address: "jdoe@machine.example",
+ }},
+ },
+ // RFC 5322, Appendix A.1.2
+ {
+ `"Joe Q. Public" <john.q.public@example.com>`,
+ []*Address{{
+ Name: "Joe Q. Public",
+ Address: "john.q.public@example.com",
+ }},
+ },
+ {
+ `Mary Smith <mary@x.test>, jdoe@example.org, Who? <one@y.test>`,
+ []*Address{
+ {
+ Name: "Mary Smith",
+ Address: "mary@x.test",
+ },
+ {
+ Address: "jdoe@example.org",
+ },
+ {
+ Name: "Who?",
+ Address: "one@y.test",
+ },
+ },
+ },
+ {
+ `<boss@nil.test>, "Giant; \"Big\" Box" <sysservices@example.net>`,
+ []*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?= <joerg@example.com>`,
+ []*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?= <joerg@example.com>`,
+ []*Address{
+ {
+ Name: `Jorg Doe`,
+ Address: "joerg@example.com",
+ },
+ },
+ },
+ // RFC 2047 "Q"-encoded ISO-8859-15 address.
+ {
+ `=?ISO-8859-15?Q?J=F6rg_Doe?= <joerg@example.com>`,
+ []*Address{
+ {
+ Name: `Jörg Doe`,
+ Address: "joerg@example.com",
+ },
+ },
+ },
+ // RFC 2047 "B"-encoded windows-1252 address.
+ {
+ `=?windows-1252?q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>`,
+ []*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==?= <joerg@example.com>`,
+ []*Address{
+ {
+ Name: `Jörg`,
+ Address: "joerg@example.com",
+ },
+ },
+ },
+ // Custom example of RFC 2047 "B"-encoded UTF-8 address.
+ {
+ `=?UTF-8?B?SsO2cmc=?= <joerg@example.com>`,
+ []*Address{
+ {
+ Name: `Jörg`,
+ Address: "joerg@example.com",
+ },
+ },
+ },
+ // Custom example with "." in name. For issue 4938
+ {
+ `Asem H. <noreply@example.com>`,
+ []*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 a56b9cd..f3f8b1a 100644
--- a/src/net/main_test.go
+++ b/src/net/main_test.go
@@ -30,10 +30,16 @@
// 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 @@
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 @@
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 @@
{"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 @@
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 62bcfa4..dd6f4df 100644
--- a/src/net/mockserver_test.go
+++ b/src/net/mockserver_test.go
@@ -186,7 +186,7 @@
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 21b4796..da03e10 100644
--- a/src/net/net_windows_test.go
+++ b/src/net/net_windows_test.go
@@ -15,16 +15,31 @@
"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 @@
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 @@
}()
// 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 @@
}
// 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 b700091..d624852 100644
--- a/src/net/platform_test.go
+++ b/src/net/platform_test.go
@@ -14,7 +14,8 @@
// 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 @@
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 00e4dbf..1b4a586 100644
--- a/src/net/sockopt_bsd.go
+++ b/src/net/sockopt_bsd.go
@@ -25,7 +25,7 @@
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 6229df2..2191c91 100644
--- a/src/net/tcp_test.go
+++ b/src/net/tcp_test.go
@@ -58,7 +58,7 @@
}
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 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 @@
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 @@
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 1f43521..51a8e97 100644
--- a/src/net/tcpsock_posix.go
+++ b/src/net/tcpsock_posix.go
@@ -13,11 +13,6 @@
"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 cafa375..9688c21 100644
--- a/src/net/timeout_test.go
+++ b/src/net/timeout_test.go
@@ -696,14 +696,18 @@
}
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 2213468..b25f96a 100644
--- a/src/net/udp_test.go
+++ b/src/net/udp_test.go
@@ -238,55 +238,32 @@
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 @@
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 949f666..7bbcf6e 100644
--- a/src/net/udpsock_plan9.go
+++ b/src/net/udpsock_plan9.go
@@ -200,9 +200,16 @@
}
// 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 2e43068..36ada17 100644
--- a/src/net/udpsock_posix.go
+++ b/src/net/udpsock_posix.go
@@ -220,32 +220,39 @@
}
// 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 877b2ef..9a99f74 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -4388,18 +4388,16 @@
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 @@
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 @@
roundup(3*4, PtrSize) + PtrSize + 2,
roundup(roundup(3*4, PtrSize)+PtrSize+2, argAlign),
r,
- s,
+ r,
})
funcLayoutTests = append(funcLayoutTests,
@@ -4442,8 +4438,8 @@
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 @@
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 @@
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 @@
PtrSize,
roundup(PtrSize, argAlign),
[]byte{},
- append([]byte{BitsScalar}, naclExtra...),
+ []byte{},
})
funcLayoutTests = append(funcLayoutTests,
@@ -4491,7 +4487,7 @@
0,
0,
[]byte{},
- []byte{BitsScalar},
+ []byte{},
})
funcLayoutTests = append(funcLayoutTests,
@@ -4501,8 +4497,8 @@
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 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 c89e9c1..a4e2e7e 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 @@
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 @@
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 @@
}
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 5315bd3..e55a0d1 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -247,17 +247,17 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
-// 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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 fb9e85a..91c38c9 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -10,7 +10,7 @@
"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 6fc864d..f35ec78 100755
--- a/src/run.bash
+++ b/src/run.bash
@@ -35,4 +35,4 @@
[ "$(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 b024d7a..d41696a 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 932b2b7..15f4cc6 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 79421e8..3c5456f 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 c3fe4f0..0ec2093 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 549a635..1a3165c 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 ee453c0..de6dd91 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 aa028a1..9a55c71 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 36353d1..0f9aeb8 100644
--- a/src/runtime/asm_amd64.s
+++ b/src/runtime/asm_amd64.s
@@ -1693,8 +1693,10 @@
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 50a3024..f84afe0 100644
--- a/src/runtime/atomic_pointer.go
+++ b/src/runtime/atomic_pointer.go
@@ -20,18 +20,12 @@
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 @@
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 @@
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_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 @@
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 3ecaac1..9aec3b0 100644
--- a/src/runtime/debug.go
+++ b/src/runtime/debug.go
@@ -22,17 +22,12 @@
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 e0c8b17..3fddcc8 100644
--- a/src/runtime/export_test.go
+++ b/src/runtime/export_test.go
@@ -76,24 +76,17 @@
}
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 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 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 540d7b5..476c3c5 100644
--- a/src/runtime/extern.go
+++ b/src/runtime/extern.go
@@ -58,18 +58,6 @@
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 6abec4c..e3e0c3a 100644
--- a/src/runtime/gc_test.go
+++ b/src/runtime/gc_test.go
@@ -6,6 +6,7 @@
import (
"os"
+ "reflect"
"runtime"
"runtime/debug"
"testing"
@@ -197,45 +198,166 @@
}
}
-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 66b0353..f330bf2 100644
--- a/src/runtime/gcinfo_test.go
+++ b/src/runtime/gcinfo_test.go
@@ -10,8 +10,14 @@
"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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
var (
// BSS
+ bssPtr Ptr
bssScalarPtr ScalarPtr
bssPtrScalar PtrScalar
bssBigStruct BigStruct
@@ -175,6 +186,7 @@
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 9ca3399..b199330 100644
--- a/src/runtime/hashmap.go
+++ b/src/runtime/hashmap.go
@@ -233,6 +233,9 @@
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)<<B); B++ {
@@ -990,3 +993,60 @@
func reflect_ismapkey(t *_type) bool {
return ismapkey(t)
}
+
+var zerobuf struct {
+ lock mutex
+ p *byte
+ size uintptr
+}
+
+var zerotiny [1024]byte
+
+// mapzero ensures that t.zero points at a zero value for type t.
+// Types known to the compiler are in read-only memory and all point
+// to a single zero in the bss of a large enough size.
+// Types allocated by package reflect are in writable memory and
+// start out with zero set to nil; we initialize those on demand.
+func mapzero(t *_type) {
+ // On ARM, atomicloadp is implemented as xadd(p, 0),
+ // so we cannot use atomicloadp on read-only memory.
+ // Check whether the pointer is in the heap; if not, it's not writable
+ // so the zero value must already be set.
+ if GOARCH == "arm" && !inheap(uintptr(unsafe.Pointer(t))) {
+ if t.zero == nil {
+ print("runtime: map element ", *t._string, " missing zero value\n")
+ throw("mapzero")
+ }
+ return
+ }
+
+ // Already done?
+ // Check without lock, so must use atomicload to sync with atomicstore in allocation case below.
+ if atomicloadp(unsafe.Pointer(&t.zero)) != nil {
+ return
+ }
+
+ // Small enough for static buffer?
+ if t.size <= uintptr(len(zerotiny)) {
+ atomicstorep(unsafe.Pointer(&t.zero), unsafe.Pointer(&zerotiny[0]))
+ return
+ }
+
+ // Use allocated buffer.
+ lock(&zerobuf.lock)
+ if zerobuf.size < t.size {
+ if zerobuf.size == 0 {
+ zerobuf.size = 4 * 1024
+ }
+ for zerobuf.size < t.size {
+ zerobuf.size *= 2
+ if zerobuf.size == 0 {
+ // need >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 e18aa79..c0fff3f 100644
--- a/src/runtime/heapdump.go
+++ b/src/runtime/heapdump.go
@@ -15,20 +15,13 @@
//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 @@
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 68f221d..4da4d88 100644
--- a/src/runtime/lfstack_test.go
+++ b/src/runtime/lfstack_test.go
@@ -24,8 +24,12 @@
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 1619ccb..2d7e556 100644
--- a/src/runtime/malloc.go
+++ b/src/runtime/malloc.go
@@ -424,9 +424,6 @@
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 @@
if mp.mallocing != 0 {
throw("malloc deadlock")
}
+ if mp.gsignal == getg() {
+ throw("malloc during signal")
+ }
mp.mallocing = 1
shouldhelpgc := false
@@ -669,10 +669,6 @@
})
}
- 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 eb58817..53a0a00 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 @@
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 @@
// 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 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
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 @@
//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 f0c7520..b20908f 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<<typeBitsWidth - 1
+ heapBitsShift = 1 // shift offset between successive bitPointer or bitMarked entries
+ heapBitmapScale = ptrSize * (8 / 2) // number of data bytes described by one heap bitmap byte
- heapBitsWidth = 4
- heapBitmapScale = ptrSize * (8 / heapBitsWidth) // number of data bytes per heap bitmap byte
- bitMarked = 2
- typeShift = 2
+ // all mark/pointer bits in a byte
+ bitMarkedAll = bitMarked | bitMarked<<heapBitsShift | bitMarked<<(2*heapBitsShift) | bitMarked<<(3*heapBitsShift)
+ bitPointerAll = bitPointer | bitPointer<<heapBitsShift | bitPointer<<(2*heapBitsShift) | bitPointer<<(3*heapBitsShift)
)
-// Information from the compiler about the layout of stack frames.
-type bitvector struct {
- n int32 // # of bits
- bytedata *uint8
-}
-
// addb returns the byte pointer p+n.
//go:nowritebarrier
func addb(p *byte, n uintptr) *byte {
- return (*byte)(add(unsafe.Pointer(p), n))
+ // Note: wrote out full expression instead of calling add(p, n)
+ // to reduce the number of temporaries generated by the
+ // compiler for this trivial expression during inlining.
+ return (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + n))
}
// subtractb returns the byte pointer p-n.
//go:nowritebarrier
func subtractb(p *byte, n uintptr) *byte {
- return (*byte)(add(unsafe.Pointer(p), -n))
+ // Note: wrote out full expression instead of calling add(p, -n)
+ // to reduce the number of temporaries generated by the
+ // compiler for this trivial expression during inlining.
+ return (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) - n))
+}
+
+// add1 returns the byte pointer p+1.
+//go:nowritebarrier
+func add1(p *byte) *byte {
+ // Note: wrote out full expression instead of calling addb(p, 1)
+ // to reduce the number of temporaries generated by the
+ // compiler for this trivial expression during inlining.
+ return (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + 1))
+}
+
+// subtract1 returns the byte pointer p-1.
+//go:nowritebarrier
+func subtract1(p *byte) *byte {
+ // Note: wrote out full expression instead of calling subtractb(p, 1)
+ // to reduce the number of temporaries generated by the
+ // compiler for this trivial expression during inlining.
+ return (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) - 1))
}
// mHeap_MapBits is called each time arena_used is extended.
@@ -140,9 +148,13 @@
// heapBitsForAddr returns the heapBits for the address addr.
// The caller must have already checked that addr is in the range [mheap_.arena_start, mheap_.arena_used).
+//
+// nosplit because it is used during write barriers and must not be preempted.
+//go:nosplit
func heapBitsForAddr(addr uintptr) heapBits {
+ // 2 bits per work, 4 pairs per byte, and a mask is hard coded.
off := (addr - mheap_.arena_start) / ptrSize
- return heapBits{(*uint8)(unsafe.Pointer(mheap_.arena_start - off/2 - 1)), uint32(4 * (off & 1))}
+ return heapBits{(*uint8)(unsafe.Pointer(mheap_.arena_start - off/4 - 1)), uint32(off & 3)}
}
// heapBitsForSpan returns the heapBits for the span base address base.
@@ -229,20 +241,39 @@
// That is, if h describes address p, h.next() describes p+ptrSize.
// Note that next does not modify h. The caller must record the result.
func (h heapBits) next() heapBits {
- if h.shift == 0 {
- return heapBits{h.bitp, 4}
+ if h.shift < 3*heapBitsShift {
+ return heapBits{h.bitp, h.shift + heapBitsShift}
}
- return heapBits{subtractb(h.bitp, 1), 0}
+ return heapBits{subtract1(h.bitp), 0}
+}
+
+// forward returns the heapBits describing n pointer-sized words ahead of h in memory.
+// That is, if h describes address p, h.forward(n) describes p+n*ptrSize.
+// h.forward(1) is equivalent to h.next(), just slower.
+// Note that forward does not modify h. The caller must record the result.
+// bits returns the heap bits for the current word.
+func (h heapBits) forward(n uintptr) heapBits {
+ n += uintptr(h.shift) / heapBitsShift
+ return heapBits{subtractb(h.bitp, n/4), uint32(n%4) * heapBitsShift}
+}
+
+// The caller can test isMarked and isPointer by &-ing with bitMarked and bitPointer.
+// The result includes in its higher bits the bits for subsequent words
+// described by the same bitmap byte.
+func (h heapBits) bits() uint32 {
+ return uint32(*h.bitp) >> 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) != 0
}
// setMarked sets the marked bit in the heap bits, atomically.
+// h must describe the initial word of the object.
func (h heapBits) setMarked() {
- // Each byte of GC bitmap holds info for two words.
+ // Each byte of GC bitmap holds info for four words.
// Might be racing with other updates, so use atomic update always.
// We used to be clever here and use a non-atomic update in certain
// cases, but it's not worth the risk.
@@ -250,30 +281,103 @@
}
// setMarkedNonAtomic sets the marked bit in the heap bits, non-atomically.
+// h must describe the initial word of the object.
func (h heapBits) setMarkedNonAtomic() {
*h.bitp |= bitMarked << h.shift
}
-// typeBits returns the heap bits' type bits.
-func (h heapBits) typeBits() uint8 {
- return (*h.bitp >> (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<<heapBitsShift) != 0 {
+ return true
+ }
+ if size == 2*ptrSize {
+ return false
+ }
+ // At least a 4-word object. Check scan bit (aka marked bit) in third word.
+ if h.shift == 0 {
+ return b&(bitMarked<<(2*heapBitsShift)) != 0
+ }
+ return uint32(*subtract1(h.bitp))&bitMarked != 0
}
// isCheckmarked reports whether the heap bits have the checkmarked bit set.
-func (h heapBits) isCheckmarked() bool {
- typ := h.typeBits()
- return typ == typeScalarCheckmarked || typ == typePointerCheckmarked
+// It must be told how large the object at h is, because the encoding of the
+// checkmark bit varies by size.
+// h must describe the initial word of the object.
+func (h heapBits) isCheckmarked(size uintptr) bool {
+ if size == ptrSize {
+ return (*h.bitp>>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)<<h.shift)
+// It must be told how large the object at h is, because the encoding of the
+// checkmark bit varies by size.
+// h must describe the initial word of the object.
+func (h heapBits) setCheckmarked(size uintptr) {
+ if size == ptrSize {
+ atomicor8(h.bitp, bitPointer<<h.shift)
+ return
+ }
+ atomicor8(h.bitp, bitMarked<<(heapBitsShift+h.shift))
+}
+
+// heapBitsBulkBarrier executes writebarrierptr_nostore
+// for every pointer slot in the memory range [p, p+size),
+// using the heap bitmap to locate those pointer slots.
+// This executes the write barriers necessary after a memmove.
+// Both p and size must be pointer-aligned.
+// The range [p, p+size) must lie within a single allocation.
+//
+// Callers should call heapBitsBulkBarrier immediately after
+// calling memmove(p, src, size). This function is marked nosplit
+// to avoid being preempted; the GC must not stop the goroutine
+// betwen the memmove and the execution of the barriers.
+//
+// The heap bitmap is not maintained for allocations containing
+// no pointers at all; any caller of heapBitsBulkBarrier must first
+// make sure the underlying allocation contains pointers, usually
+// by checking typ.kind&kindNoPointers.
+//
+//go:nosplit
+func heapBitsBulkBarrier(p, size uintptr) {
+ if (p|size)&(ptrSize-1) != 0 {
+ throw("heapBitsBulkBarrier: unaligned arguments")
+ }
+ if !writeBarrierEnabled || !inheap(p) {
+ return
+ }
+
+ h := heapBitsForAddr(p)
+ for i := uintptr(0); i < size; i += ptrSize {
+ if h.isPointer() {
+ x := (*uintptr)(unsafe.Pointer(p + i))
+ writebarrierptr_nostore(x, *x)
+ }
+ h = h.next()
}
}
@@ -291,99 +395,59 @@
throw("initSpan: unaligned length")
}
nbyte := total / heapBitmapScale
+ if ptrSize == 8 && size == ptrSize {
+ end := h.bitp
+ bitp := subtractb(end, nbyte-1)
+ for {
+ *bitp = bitPointerAll
+ if bitp == end {
+ break
+ }
+ bitp = add1(bitp)
+ }
+ return
+ }
memclr(unsafe.Pointer(subtractb(h.bitp, nbyte-1)), nbyte)
}
// initCheckmarkSpan initializes a span for being checkmarked.
-// This would be a no-op except that we need to rewrite any
-// typeDead bits in the first word of the object into typeScalar
-// followed by a typeDead in the second word of the object.
+// It clears the checkmark bits, which are set to 1 in normal operation.
func (h heapBits) initCheckmarkSpan(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.
- // There is no second word in these objects, so all we have
- // to do is rewrite typeDead to typeScalar by adding the 1<<typeShift bit.
+ // 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)
-
- if (x>>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 @@
// 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<<heapBitsShift) != 0 {
+ x &^= bitMarked << heapBitsShift
} else {
- x &^= typeMask << (4 + typeShift)
f(base + (i+1)*ptrSize)
}
+ if x&(bitMarked<<(2*heapBitsShift)) != 0 {
+ x &^= bitMarked << (2 * heapBitsShift)
+ } else {
+ f(base + (i+2)*ptrSize)
+ }
+ if x&(bitMarked<<(3*heapBitsShift)) != 0 {
+ x &^= bitMarked << (3 * heapBitsShift)
+ } else {
+ f(base + (i+3)*ptrSize)
+ }
*bitp = uint8(x)
- bitp = subtractb(bitp, 1)
+ bitp = subtract1(bitp)
}
- return
- }
- bitp := h.bitp
- step := size / heapBitmapScale
- for i := uintptr(0); i < n; i++ {
- x := int(*bitp)
- if x&bitMarked != 0 {
- x &^= bitMarked
- } else {
- x = 0
- f(base + i*size)
+ case size%(4*ptrSize) == 0:
+ // Mark bit is in first word of each object.
+ // Each object starts at bit 0 of a heap bitmap byte.
+ bitp := h.bitp
+ step := size / heapBitmapScale
+ for i := uintptr(0); i < n; i++ {
+ x := uint32(*bitp)
+ if x&bitMarked != 0 {
+ x &^= bitMarked
+ } else {
+ x = 0
+ f(base + i*size)
+ }
+ *bitp = uint8(x)
+ bitp = subtractb(bitp, step)
}
- *bitp = uint8(x)
- bitp = subtractb(bitp, step)
+
+ case size%(4*ptrSize) == 2*ptrSize:
+ // Mark bit is in first word of each object,
+ // but every other object starts halfway through a heap bitmap byte.
+ // Unroll loop 2x to handle alternating shift count and step size.
+ bitp := h.bitp
+ step := size / heapBitmapScale
+ var i uintptr
+ for i = uintptr(0); i < n; i += 2 {
+ x := uint32(*bitp)
+ if x&bitMarked != 0 {
+ x &^= bitMarked
+ } else {
+ x &^= bitMarked | bitPointer | (bitMarked|bitPointer)<<heapBitsShift
+ f(base + i*size)
+ if size > 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)
+ }
}
}
-// 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<<h.shift)
+ }
+ } else {
+ // 2-element slice of pointer.
+ if gcphase == _GCoff {
+ *h.bitp |= (bitPointer | bitPointer<<heapBitsShift) << h.shift
+ } else {
+ atomicor8(h.bitp, (bitPointer|bitPointer<<heapBitsShift)<<h.shift)
}
}
- prog = addb(prog, round(uintptr(siz)*typeBitsWidth, 8)/8)
+ return
+ }
+ // Otherwise typ.size must be 2*ptrSize, and typ.kind&kindGCProg == 0.
+ if doubleCheck {
+ if typ.size != 2*ptrSize || typ.kind&kindGCProg != 0 {
+ print("runtime: heapBitsSetType size=", size, " but typ.size=", typ.size, " gcprog=", typ.kind&kindGCProg != 0, "\n")
+ throw("heapBitsSetType")
+ }
+ }
+ b := uint32(*ptrmask)
+ hb := b & 3
+ if gcphase == _GCoff {
+ *h.bitp |= uint8(hb << h.shift)
+ } else {
+ atomicor8(h.bitp, uint8(hb<<h.shift))
+ }
+ return
+ }
- case insArray:
- prog = (*byte)(add(unsafe.Pointer(prog), 1))
- siz := uintptr(0)
- for i := uintptr(0); i < ptrSize; i++ {
- siz = (siz << 8) + uintptr(*(*byte)(add(unsafe.Pointer(prog), ptrSize-i-1)))
- }
- prog = (*byte)(add(unsafe.Pointer(prog), ptrSize))
- var prog1 *byte
- for i := uintptr(0); i < siz; i++ {
- prog1 = unrollgcprog1(&mask[0], prog, &pos, inplace, sparse)
- }
- if *prog1 != insArrayEnd {
- throw("unrollgcprog: array does not end with insArrayEnd")
- }
- prog = (*byte)(add(unsafe.Pointer(prog1), 1))
+ // Copy from 1-bit ptrmask into 2-bit bitmap.
+ // The basic approach is to use a single uintptr as a bit buffer,
+ // alternating between reloading the buffer and writing bitmap bytes.
+ // In general, one load can supply two bitmap byte writes.
+ // This is a lot of lines of code, but it compiles into relatively few
+ // machine instructions.
- case insArrayEnd, insEnd:
- *ppos = pos
- return prog
+ var (
+ // Ptrmask input.
+ p *byte // last ptrmask byte read
+ b uintptr // ptrmask bits already loaded
+ nb uintptr // number of bits in b at next read
+ endp *byte // final ptrmask byte to read (then repeat)
+ endnb uintptr // number of valid bits in *endp
+ pbits uintptr // alternate source of bits
+
+ // Heap bitmap output.
+ w uintptr // words processed
+ nw uintptr // number of words to process
+ hbitp *byte // next heap bitmap byte to write
+ hb uintptr // bits being prepared for *hbitp
+ )
+
+ hbitp = h.bitp
+
+ // Handle GC program. Delayed until this part of the code
+ // so that we can use the same double-checking mechanism
+ // as the 1-bit case. Nothing above could have encountered
+ // GC programs: the cases were all too small.
+ if typ.kind&kindGCProg != 0 {
+ heapBitsSetTypeGCProg(h, typ.ptrdata, typ.size, dataSize, size, addb(typ.gcdata, 4))
+ if doubleCheck {
+ // Double-check the heap bits written by GC program
+ // by running the GC program to create a 1-bit pointer mask
+ // and then jumping to the double-check code below.
+ // This doesn't catch bugs shared between the 1-bit and 4-bit
+ // GC program execution, but it does catch mistakes specific
+ // to just one of those and bugs in heapBitsSetTypeGCProg's
+ // implementation of arrays.
+ lock(&debugPtrmask.lock)
+ if debugPtrmask.data == nil {
+ debugPtrmask.data = (*byte)(persistentalloc(1<<20, 1, &memstats.other_sys))
+ }
+ ptrmask = debugPtrmask.data
+ runGCProg(addb(typ.gcdata, 4), nil, ptrmask, 1)
+ goto Phase4
+ }
+ return
+ }
+
+ // Note about sizes:
+ //
+ // typ.size is the number of words in the object,
+ // and typ.ptrdata is the number of words in the prefix
+ // of the object that contains pointers. That is, the final
+ // typ.size - typ.ptrdata words contain no pointers.
+ // This allows optimization of a common pattern where
+ // an object has a small header followed by a large scalar
+ // buffer. If we know the pointers are over, we don't have
+ // to scan the buffer's heap bitmap at all.
+ // The 1-bit ptrmasks are sized to contain only bits for
+ // the typ.ptrdata prefix, zero padded out to a full byte
+ // of bitmap. This code sets nw (below) so that heap bitmap
+ // bits are only written for the typ.ptrdata prefix; if there is
+ // more room in the allocated object, the next heap bitmap
+ // entry is a 00, indicating that there are no more pointers
+ // to scan. So only the ptrmask for the ptrdata bytes is needed.
+ //
+ // Replicated copies are not as nice: if there is an array of
+ // objects with scalar tails, all but the last tail does have to
+ // be initialized, because there is no way to say "skip forward".
+ // However, because of the possibility of a repeated type with
+ // size not a multiple of 4 pointers (one heap bitmap byte),
+ // the code already must handle the last ptrmask byte specially
+ // by treating it as containing only the bits for endnb pointers,
+ // where endnb <= 4. We represent large scalar tails that must
+ // be expanded in the replication by setting endnb larger than 4.
+ // This will have the effect of reading many bits out of b,
+ // but once the real bits are shifted out, b will supply as many
+ // zero bits as we try to read, which is exactly what we need.
+
+ p = ptrmask
+ if typ.size < dataSize {
+ // Filling in bits for an array of typ.
+ // Set up for repetition of ptrmask during main loop.
+ // Note that ptrmask describes only a prefix of
+ const maxBits = ptrSize*8 - 7
+ if typ.ptrdata/ptrSize <= maxBits {
+ // Entire ptrmask fits in uintptr with room for a byte fragment.
+ // Load into pbits and never read from ptrmask again.
+ // This is especially important when the ptrmask has
+ // fewer than 8 bits in it; otherwise the reload in the middle
+ // of the Phase 2 loop would itself need to loop to gather
+ // at least 8 bits.
+
+ // Accumulate ptrmask into b.
+ // ptrmask is sized to describe only typ.ptrdata, but we record
+ // it as describing typ.size bytes, since all the high bits are zero.
+ nb = typ.ptrdata / ptrSize
+ for i := uintptr(0); i < nb; i += 8 {
+ b |= uintptr(*p) << i
+ p = add1(p)
+ }
+ nb = typ.size / ptrSize
+
+ // Replicate ptrmask to fill entire pbits uintptr.
+ // Doubling and truncating is fewer steps than
+ // iterating by nb each time. (nb could be 1.)
+ // Since we loaded typ.ptrdata/ptrSize bits
+ // but are pretending to have typ.size/ptrSize,
+ // there might be no replication necessary/possible.
+ pbits = b
+ endnb = nb
+ if nb+nb <= maxBits {
+ for endnb <= ptrSize*8 {
+ pbits |= pbits << endnb
+ endnb += endnb
+ }
+ // Truncate to a multiple of original ptrmask.
+ endnb = maxBits / nb * nb
+ pbits &= 1<<endnb - 1
+ b = pbits
+ nb = endnb
+ }
+
+ // Clear p and endp as sentinel for using pbits.
+ // Checked during Phase 2 loop.
+ p = nil
+ endp = nil
+ } else {
+ // Ptrmask is larger. Read it multiple times.
+ n := (typ.ptrdata/ptrSize+7)/8 - 1
+ endp = addb(ptrmask, n)
+ endnb = typ.size/ptrSize - n*8
+ }
+ }
+ if p != nil {
+ b = uintptr(*p)
+ p = add1(p)
+ nb = 8
+ }
+
+ if typ.size == dataSize {
+ // Single entry: can stop once we reach the non-pointer data.
+ nw = typ.ptrdata / ptrSize
+ } else {
+ // Repeated instances of typ in an array.
+ // Have to process first N-1 entries in full, but can stop
+ // once we reach the non-pointer data in the final entry.
+ nw = ((dataSize/typ.size-1)*typ.size + typ.ptrdata) / ptrSize
+ }
+ if nw == 0 {
+ // No pointers! Caller was supposed to check.
+ println("runtime: invalid type ", *typ._string)
+ throw("heapBitsSetType: called with non-pointer type")
+ return
+ }
+ if nw < 2 {
+ // Must write at least 2 words, because the "no scan"
+ // encoding doesn't take effect until the third word.
+ nw = 2
+ }
+
+ // Phase 1: Special case for leading byte (shift==0) or half-byte (shift==4).
+ // The leading byte is special because it contains the bits for words 0 and 1,
+ // which do not have the marked bits set.
+ // The leading half-byte is special because it's a half a byte and must be
+ // manipulated atomically.
+ switch {
+ default:
+ throw("heapBitsSetType: unexpected shift")
+
+ case h.shift == 0:
+ // Ptrmask and heap bitmap are aligned.
+ // Handle first byte of bitmap specially.
+ // The first byte we write out contains the first two words of the object.
+ // In those words, the mark bits are mark and checkmark, respectively,
+ // and must not be set. In all following words, we want to set the mark bit
+ // as a signal that the object continues to the next 2-bit entry in the bitmap.
+ hb = b & bitPointerAll
+ hb |= bitMarked<<(2*heapBitsShift) | bitMarked<<(3*heapBitsShift)
+ if w += 4; w >= 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)<<heapBitsShift) | uint8(hb)
+ } else {
+ atomicand8(hbitp, ^uint8(bitPointer|bitMarked|(bitPointer|bitMarked)<<heapBitsShift))
+ atomicor8(hbitp, uint8(hb))
+ }
+ }
+
+Phase4:
+ // Phase 4: all done, but perhaps double check.
+ if doubleCheck {
+ end := heapBitsForAddr(x + size)
+ if typ.kind&kindGCProg == 0 && (hbitp != end.bitp || (w == nw+2) != (end.shift == 2)) {
+ println("ended at wrong bitmap byte for", *typ._string, "x", dataSize/typ.size)
+ print("typ.size=", typ.size, " typ.ptrdata=", typ.ptrdata, " dataSize=", dataSize, " size=", size, "\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("ended at hbitp=", hbitp, " but next starts at bitp=", end.bitp, " shift=", end.shift, "\n")
+ throw("bad heapBitsSetType")
+ }
+
+ // Double-check that bits to be written were written correctly.
+ // Does not check that other bits were not written, unfortunately.
+ h := heapBitsForAddr(x)
+ nptr := typ.ptrdata / ptrSize
+ ndata := typ.size / ptrSize
+ count := dataSize / typ.size
+ totalptr := ((count-1)*typ.size + typ.ptrdata) / ptrSize
+ for i := uintptr(0); i < size/ptrSize; i++ {
+ j := i % ndata
+ var have, want uint8
+ have = (*h.bitp >> 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")
}
- unlock(&unroll)
+ 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<<maxBits - 1
+ npattern = maxBits
+ } else {
+ npattern = c
+ }
+ } else {
+ b := pattern
+ nb := npattern
+ if nb+nb <= maxBits {
+ // Double pattern until the whole uintptr is filled.
+ for nb <= ptrSize*8 {
+ b |= b << nb
+ nb += nb
+ }
+ // Trim away incomplete copy of original pattern in high bits.
+ // TODO(rsc): Replace with table lookup or loop on systems without divide?
+ nb = maxBits / npattern * npattern
+ b &= 1<<nb - 1
+ pattern = b
+ npattern = nb
+ }
+ }
+
+ // Add pattern to bit buffer and flush bit buffer, c/npattern times.
+ // Since pattern contains >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<<c - 1
+ bits |= pattern << nbits
+ nbits += c
+ }
+ continue Run
+ }
+
+ // Repeat; n too large to fit in a register.
+ // Since nbits <= 7, we know the first few bytes of repeated data
+ // are already written to memory.
+ off := n - nbits // n > 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<<c - 1)) << nbits
+ nbits += c
+ }
+ } else {
+ // Leading src fragment.
+ src = addb(src, (off+3)/4)
+ if frag := off & 3; frag != 0 {
+ bits |= (uintptr(*src) & 0xf) >> (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<<c - 1)) << nbits
+ nbits += c
+ }
+ }
+ }
+
+ // Write any final bits out, using full-byte writes, even for the final byte.
+ var totalBits uintptr
+ if size == 1 {
+ totalBits = (uintptr(unsafe.Pointer(dst))-uintptr(unsafe.Pointer(dstStart)))*8 + nbits
+ nbits += -nbits & 7
+ for ; nbits > 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<<heapBitsShift
+ }
+ return totalBits
+}
+
+func dumpGCProg(p *byte) {
+ nptr := 0
+ for {
+ x := *p
+ p = add1(p)
+ if x == 0 {
+ print("\t", nptr, " end\n")
+ break
+ }
+ if x&0x80 == 0 {
+ print("\t", nptr, " lit ", x, ":")
+ n := int(x+7) / 8
+ for i := 0; i < n; i++ {
+ print(" ", hex(*p))
+ p = add1(p)
+ }
+ print("\n")
+ nptr += int(x)
+ } else {
+ nbit := int(x &^ 0x80)
+ if nbit == 0 {
+ for nb := uint(0); ; nb += 7 {
+ x := *p
+ p = add1(p)
+ nbit |= int(x&0x7f) << nb
+ if x&0x80 == 0 {
+ break
+ }
+ }
+ }
+ count := 0
+ for nb := uint(0); ; nb += 7 {
+ x := *p
+ p = add1(p)
+ count |= int(x&0x7f) << nb
+ if x&0x80 == 0 {
+ break
+ }
+ }
+ print("\t", nptr, " repeat ", nbit, " × ", count, "\n")
+ nptr += nbit * count
+ }
+ }
}
// Testing.
@@ -748,36 +1510,46 @@
return true
}
-// Returns GC type info for object p for testing.
-func getgcmask(p unsafe.Pointer, t *_type, mask **byte, len *uintptr) {
- *mask = nil
- *len = 0
+// gcbits returns the GC type info for x, for testing.
+// The result is the bitmap entries (0 or 1), one entry per byte.
+//go:linkname reflect_gcbits reflect.gcbits
+func reflect_gcbits(x interface{}) []byte {
+ ret := getgcmask(x)
+ typ := (*ptrtype)(unsafe.Pointer((*eface)(unsafe.Pointer(&x))._type)).elem
+ nptr := typ.ptrdata / ptrSize
+ for uintptr(len(ret)) > 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 @@
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 _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
+ }
}
- 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
- }
+ 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 9bd36d1..db5b2dc 100644
--- a/src/runtime/mgc.go
+++ b/src/runtime/mgc.go
@@ -127,13 +127,22 @@
_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 @@
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 @@
in = -1
}
gcpercent = in
- heapminimum = heapminimum * uint64(gcpercent) / 100
+ heapminimum = defaultHeapMinimum * uint64(gcpercent) / 100
unlock(&mheap_.lock)
return out
}
@@ -197,7 +206,6 @@
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 @@
//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 @@
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 @@
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 @@
setGCPhase(_GCscan)
// Concurrent scan.
- starttheworld()
+ startTheWorldWithSema()
if debug.gctrace > 0 {
tScan = nanotime()
}
@@ -858,7 +866,7 @@
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 @@
// 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 @@
}
}
+// 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 9d78dde..62fa338 100644
--- a/src/runtime/mgcmark.go
+++ b/src/runtime/mgcmark.go
@@ -261,7 +261,7 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 9c32ae8..b7feb84 100644
--- a/src/runtime/mgcwork.go
+++ b/src/runtime/mgcwork.go
@@ -7,7 +7,7 @@
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 @@
}
}
+// 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 10878ee..04fa050 100644
--- a/src/runtime/mheap.go
+++ b/src/runtime/mheap.go
@@ -28,6 +28,15 @@
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 @@
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 @@
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 @@
// 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 4544344..a618bd5 100644
--- a/src/runtime/mprof.go
+++ b/src/runtime/mprof.go
@@ -521,9 +521,7 @@
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 @@
}
}
- gp.m.preemptoff = ""
- semrelease(&worldsema)
- systemstack(starttheworld)
+ startTheWorld()
}
return n, ok
@@ -565,10 +561,7 @@
// 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 @@
}
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 c8e5249..3eff7f6 100644
--- a/src/runtime/mstats.go
+++ b/src/runtime/mstats.go
@@ -153,24 +153,13 @@
// 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 10cf460..1b74e3e 100644
--- a/src/runtime/os1_darwin.go
+++ b/src/runtime/os1_darwin.go
@@ -8,7 +8,6 @@
//extern SigTabTT runtime·sigtab[];
-var sigset_none = uint32(0)
var sigset_all = ^uint32(0)
func unimplemented(name string) {
@@ -126,17 +125,36 @@
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 @@
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 a590aea..eb42b54 100644
--- a/src/runtime/os1_dragonfly.go
+++ b/src/runtime/os1_dragonfly.go
@@ -12,7 +12,6 @@
_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 @@
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 @@
// 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 @@
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 8719a49..f7f34bd 100644
--- a/src/runtime/os1_freebsd.go
+++ b/src/runtime/os1_freebsd.go
@@ -12,7 +12,6 @@
_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 @@
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 @@
// 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 @@
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 e4b18c7..02f98d7 100644
--- a/src/runtime/os1_linux.go
+++ b/src/runtime/os1_linux.go
@@ -6,7 +6,6 @@
import "unsafe"
-var sigset_none sigset
var sigset_all sigset = sigset{^uint32(0), ^uint32(0)}
// Linux futex.
@@ -190,17 +189,36 @@
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 @@
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 dbb5dec..66e60f8 100644
--- a/src/runtime/os1_nacl.go
+++ b/src/runtime/os1_nacl.go
@@ -15,6 +15,9 @@
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 8df74b5..3fb0598 100644
--- a/src/runtime/os1_netbsd.go
+++ b/src/runtime/os1_netbsd.go
@@ -17,7 +17,6 @@
_CLOCK_MONOTONIC = 3
)
-var sigset_none = sigset{}
var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
// From NetBSD's <sys/sysctl.h>
@@ -139,6 +138,14 @@
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 @@
// 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 @@
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 95729a5..5ccf642 100644
--- a/src/runtime/os1_openbsd.go
+++ b/src/runtime/os1_openbsd.go
@@ -148,6 +148,14 @@
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 @@
// 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 @@
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 c026218..bda7057 100644
--- a/src/runtime/os1_plan9.go
+++ b/src/runtime/os1_plan9.go
@@ -18,6 +18,9 @@
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 @@
} 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 5719b32..bc472d0 100644
--- a/src/runtime/os1_windows.go
+++ b/src/runtime/os1_windows.go
@@ -292,6 +292,9 @@
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 69ac5b4..e4fe92d 100644
--- a/src/runtime/os3_solaris.go
+++ b/src/runtime/os3_solaris.go
@@ -114,7 +114,6 @@
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 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 @@
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 @@
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 0e4086c..47563f4 100644
--- a/src/runtime/panic.go
+++ b/src/runtime/panic.go
@@ -188,16 +188,6 @@
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 @@
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 b3d0ae9..4290edb 100644
--- a/src/runtime/pprof/pprof.go
+++ b/src/runtime/pprof/pprof.go
@@ -442,35 +442,33 @@
// 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 f725fc8..805b96e 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -203,7 +203,7 @@
// 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 00535da..c070f7d 100644
--- a/src/runtime/proc1.go
+++ b/src/runtime/proc1.go
@@ -59,7 +59,6 @@
goargs()
goenvs()
parsedebugvars()
- wbshadowinit()
gcinit()
sched.lastpoll = uint64(nanotime())
@@ -212,7 +211,7 @@
// 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 @@
}
}
-// 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 @@
}
}
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 @@
_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 @@
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 @@
_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 @@
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 @@
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 @@
}
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 @@
_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 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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
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 @@
}
}
-// 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 @@
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 @@
// 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 @@
}
}
-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 4c5712d..4471ee5 100644
--- a/src/runtime/proc_test.go
+++ b/src/runtime/proc_test.go
@@ -7,6 +7,7 @@
import (
"math"
"runtime"
+ "runtime/debug"
"sync"
"sync/atomic"
"syscall"
@@ -104,8 +105,8 @@
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 fe7d38a..f4014b2 100644
--- a/src/runtime/runtime-gdb_test.go
+++ b/src/runtime/runtime-gdb_test.go
@@ -59,7 +59,7 @@
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 @@
// 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 ac539b9..3ee5d5d 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -266,6 +266,7 @@
// 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 @@
// 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 @@
}
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 @@
}
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 d4cccbf..f65562a 100644
--- a/src/runtime/runtime_test.go
+++ b/src/runtime/runtime_test.go
@@ -6,13 +6,8 @@
import (
"io"
- "io/ioutil"
- "os"
- "os/exec"
. "runtime"
"runtime/debug"
- "strconv"
- "strings"
"testing"
"unsafe"
)
@@ -88,53 +83,6 @@
}
}
-// 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 7577d43..d3e9dac 100644
--- a/src/runtime/signal1_unix.go
+++ b/src/runtime/signal1_unix.go
@@ -19,6 +19,19 @@
// 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 @@
}
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 @@
}
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 @@
}
}
- 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 32ecce0..6cd1865 100644
--- a/src/runtime/signal_darwin.go
+++ b/src/runtime/signal_darwin.go
@@ -16,14 +16,14 @@
/* 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 @@
/* 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 f8250b9..2f25b59 100644
--- a/src/runtime/signal_linux.go
+++ b/src/runtime/signal_linux.go
@@ -16,20 +16,20 @@
/* 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 @@
/* 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 78afc59..d93a450 100644
--- a/src/runtime/signal_netbsd.go
+++ b/src/runtime/signal_netbsd.go
@@ -14,14 +14,14 @@
/* 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 @@
/* 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 2986c5a..d8ac676 100644
--- a/src/runtime/signal_solaris.go
+++ b/src/runtime/signal_solaris.go
@@ -14,21 +14,21 @@
/* 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 @@
/* 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 da8a1c5..b2fce53 100644
--- a/src/runtime/signal_windows.go
+++ b/src/runtime/signal_windows.go
@@ -131,7 +131,9 @@
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 38f0a57..f000fab 100644
--- a/src/runtime/sigqueue_plan9.go
+++ b/src/runtime/sigqueue_plan9.go
@@ -17,21 +17,29 @@
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 @@
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 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 5ccc659..79b6118 100644
--- a/src/runtime/slice.go
+++ b/src/runtime/slice.go
@@ -84,10 +84,13 @@
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 f74694b..27427af 100644
--- a/src/runtime/stack1.go
+++ b/src/runtime/stack1.go
@@ -352,6 +352,12 @@
}
}
+// 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 @@
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 25f5bf4..687f067 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -32,6 +32,8 @@
// 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 @@
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 @@
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 3b7501b..6da7bad 100644
--- a/src/runtime/trace.go
+++ b/src/runtime/trace.go
@@ -132,10 +132,7 @@
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 @@
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 @@
unlock(&trace.bufLock)
- _g_.m.preemptoff = ""
- semrelease(&worldsema)
- systemstack(starttheworld)
+ startTheWorld()
return nil
}
@@ -186,19 +179,14 @@
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 @@
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 @@
// 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 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 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 9f34e37..5ed601e 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -46,6 +46,9 @@
timerprocPC uintptr
gcBgMarkWorkerPC uintptr
systemstack_switchPC uintptr
+ systemstackPC uintptr
+
+ gogoPC uintptr
externalthreadhandlerp uintptr // initialized elsewhere
)
@@ -69,6 +72,10 @@
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 @@
// 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 @@
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 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 48df2a4..45bdac8 100644
--- a/src/runtime/type.go
+++ b/src/runtime/type.go
@@ -20,17 +20,10 @@
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 d59c78e..468c37f 100644
--- a/src/strconv/ftoa.go
+++ b/src/strconv/ftoa.go
@@ -223,9 +223,8 @@
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 ced2ca86..3aa30c7 100644
--- a/src/syscall/exec_linux.go
+++ b/src/syscall/exec_linux.go
@@ -132,26 +132,6 @@
}
}
- // 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 @@
}
}
+ // 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 1b7cd64..85fab4f 100755
--- a/src/syscall/mkall.sh
+++ b/src/syscall/mkall.sh
@@ -179,7 +179,7 @@
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 @@
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 d25527b..438de6e 100755
--- a/src/syscall/mkerrors.sh
+++ b/src/syscall/mkerrors.sh
@@ -87,7 +87,9 @@
includes_Linux='
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
+#ifndef __LP64__
#define _FILE_OFFSET_BITS 64
+#endif
#define _GNU_SOURCE
#include <bits/sockaddr.h>
diff --git a/src/syscall/mksysnum_linux.pl b/src/syscall/mksysnum_linux.pl
index 7a8add8..b6fbcb5 100755
--- a/src/syscall/mksysnum_linux.pl
+++ b/src/syscall/mksysnum_linux.pl
@@ -28,7 +28,8 @@
}
my $prev;
-while(<>){
+open(GCC, "gcc -E -dD $ARGV[0] |") || die "can't run gcc";
+while(<GCC>){
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 05d4044..4f88d51 100644
--- a/src/syscall/syscall_linux.go
+++ b/src/syscall/syscall_linux.go
@@ -852,7 +852,6 @@
//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 98636a5..9ee1c1c 100644
--- a/src/syscall/syscall_linux_386.go
+++ b/src/syscall/syscall_linux_386.go
@@ -66,6 +66,7 @@
//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 fad9c32..6fbef21 100644
--- a/src/syscall/syscall_linux_amd64.go
+++ b/src/syscall/syscall_linux_amd64.go
@@ -16,6 +16,7 @@
//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 f0cc25e..218d6b8 100644
--- a/src/syscall/syscall_linux_arm.go
+++ b/src/syscall/syscall_linux_arm.go
@@ -87,6 +87,7 @@
//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 3c4eabc..7ca4164 100644
--- a/src/syscall/syscall_linux_arm64.go
+++ b/src/syscall/syscall_linux_arm64.go
@@ -124,11 +124,14 @@
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 5318c61..10489d9 100644
--- a/src/syscall/syscall_linux_ppc64x.go
+++ b/src/syscall/syscall_linux_ppc64x.go
@@ -19,6 +19,7 @@
//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 0000000..40fce6d
--- /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 01fc670..af92013 100644
--- a/src/syscall/syscall_unix_test.go
+++ b/src/syscall/syscall_unix_test.go
@@ -60,20 +60,58 @@
// 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 @@
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 bb3a161..debadaa 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 05ab48e..d4262ba 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 7e800d4..a64f373 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 a3841a2..98f431c 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 59bff75..15d300f 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 cd3aa80..bbad05f 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 9edce6e..b36125b 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 f29dd05..0c844f1 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 7aa8ff0..d433a4f 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 94d051d..dd86a3b 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 dcaaef7..2a9c0f9 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 f3d708b..35d9ace 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 15e0770..1c769cd 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 @@
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 fdecdf2..73727a4 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 @@
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 1e3dff7..6f32def 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 1469d00..a6d1701 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 1a88c0d..7f99279 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 0829834..540d310 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 e9fa37c..ae5b8c9 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 5376fe6..c49ebcf 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 3f4cbfd..62ec81b 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 0ce383f..23e7b5e 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 2370630..6e63d9a 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 c9426ee..f996a50 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 828b167..c260cc7 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 5eef138..88e09d3 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 7cb21c9..30f29e5 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 3cb87c1..93059d1 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 3404183..84096b0 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 e7cf745..620fba2 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 @@
// 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 @@
// 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 b23573b..16cafbf 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 @@
// 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 @@
// 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 054cf40..9bc3a54 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 @@
// 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 @@
// 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 26a14b7..2ee58cf 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 @@
// 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 3262180..4f61114 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 @@
// 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 @@
// 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 3262180..4073a0f 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 @@
// 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 @@
// 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 d0ff66c..bf3f9e3 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 592c8d8..3f08da6 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 10657ad..77d46c3 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 5131c25..e24c3b7 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 0e26f68..7aa75ab 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 4aae104..21f482b 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 f6a1983..df7df1e 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 93f5fc0..1d64070 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 f59739c..f40fb31 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 06f1f04..a424e78 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 06f1f04..d58556b 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 be9bc28..cabab7e 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 abdef77..c6f8342 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 abdef77..7189abe 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 1a53f13..1d76861 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 723b1aa..ddf8e83 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 4b086b9..277478d 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 dfca558..5e47217 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 dfca558..df8928c 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 dfca558..f670a59 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 c40b5f1..c277ed9 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 7cf70a4..978a4d3 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 7068e4e..5061cba 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 4bcf057..d53712c 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 0567fd0..82d253a 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 52c63e3..3af4e83 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 c570965..c8af210 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 c570965..e342a3c 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 c570965..1f5b569 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 3b9ac4c..c19f6de 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 3b9ac4c..86e04cd 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 8457c14..38b43ca 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 43b3d8b..be198f8 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 13724c3..7298d02 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 65b02ae..ec95d51 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 ec87f54..91c4470 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 65b02ae..1d65cfd 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 954ffd7..00120d0 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 b809eea..d972fb6 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 a05908a..0a5a10b 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 9303816..5d7acd5 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 a887f31..dd198cb 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 adf95ca..a39489e 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 1ae9718..f446e41 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 1cb1d43..dcb1178 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 fe43836..33d1b7f 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 @@
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 c6b6f16..27ca004 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 @@
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 6add325..1752c6c 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 4451fc1..b8d4b0b 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 4e853ea..c21d875 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 2e4d9dd..0376d3a 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 f07bc71..bf23626 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 5f8fb12..e1d8938 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 77275a5..2471519 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 61339a6..30baf27 100644
--- a/src/testing/example.go
+++ b/src/testing/example.go
@@ -43,7 +43,7 @@
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 280d76a..2a1c45f 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 @@
}
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 d3eadfd..eacc0a2 100644
--- a/src/text/scanner/scanner.go
+++ b/src/text/scanner/scanner.go
@@ -314,7 +314,9 @@
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 @@
}
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 aca17b1..798bed7 100644
--- a/src/text/scanner/scanner_test.go
+++ b/src/text/scanner/scanner_test.go
@@ -619,13 +619,12 @@
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 @@
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 e6e1287..ebafb4b 100644
--- a/src/text/template/exec.go
+++ b/src/text/template/exec.go
@@ -660,7 +660,7 @@
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 abce27f..0f1ad62 100644
--- a/src/text/template/exec_test.go
+++ b/src/text/template/exec_test.go
@@ -531,6 +531,8 @@
{"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 @@
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 @@
"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 cdd187b..ccd0dfc 100644
--- a/src/text/template/funcs.go
+++ b/src/text/template/funcs.go
@@ -92,6 +92,8 @@
// 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 8611faa..a7c5c8c 100644
--- a/src/text/template/template.go
+++ b/src/text/template/template.go
@@ -7,18 +7,20 @@
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 @@
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 @@
// 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 @@
// 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 7e689b8..6fd4509 100644
--- a/src/time/export_windows_test.go
+++ b/src/time/export_windows_test.go
@@ -8,3 +8,7 @@
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 0300e84..fbf3f8d3 100644
--- a/src/time/time.go
+++ b/src/time/time.go
@@ -966,6 +966,8 @@
// 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 9f987ab..d04ebec 100644
--- a/src/time/zoneinfo_windows.go
+++ b/src/time/zoneinfo_windows.go
@@ -49,7 +49,7 @@
// 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 9db81b7..5f1141d 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 @@
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 a06c326..d8b1486 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
fi
run 'gccgo -O2 regex-dna.go' a.$EXE <x
run 'gccgo -O2 regex-dna-parallel.go' a.$EXE <x
- run 'gc regex-dna' $O.$EXE <x
- run 'gc regex-dna-parallel' $O.$EXE <x
- run 'gc_B regex-dna' $O.$EXE <x
+ run 'gc regex-dna' a.$EXE <x
+ run 'gc regex-dna-parallel' a.$EXE <x
+ run 'gc_B regex-dna' a.$EXE <x
rm x
}
@@ -176,8 +175,8 @@
runonly echo 'spectral-norm 5500'
run "gcc $gccm -O2 spectral-norm.c -lm" a.$EXE 5500
run 'gccgo -O2 spectral-norm.go' a.$EXE -n 5500
- run 'gc spectral-norm' $O.$EXE -n 5500
- run 'gc_B spectral-norm' $O.$EXE -n 5500
+ run 'gc spectral-norm' a.$EXE -n 5500
+ run 'gc_B spectral-norm' a.$EXE -n 5500
}
knucleotide() {
@@ -189,9 +188,9 @@
fi
run 'gccgo -O2 k-nucleotide.go' a.$EXE <x
run 'gccgo -O2 k-nucleotide-parallel.go' a.$EXE <x
- run 'gc k-nucleotide' $O.$EXE <x
- run 'gc k-nucleotide-parallel' $O.$EXE <x
- run 'gc_B k-nucleotide' $O.$EXE <x
+ run 'gc k-nucleotide' a.$EXE <x
+ run 'gc k-nucleotide-parallel' a.$EXE <x
+ run 'gc_B k-nucleotide' a.$EXE <x
rm x
}
@@ -199,16 +198,16 @@
runonly echo 'mandelbrot 16000'
run "gcc $gccm -O2 mandelbrot.c" a.$EXE 16000
run 'gccgo -O2 mandelbrot.go' a.$EXE -n 16000
- run 'gc mandelbrot' $O.$EXE -n 16000
- run 'gc_B mandelbrot' $O.$EXE -n 16000
+ run 'gc mandelbrot' a.$EXE -n 16000
+ run 'gc_B mandelbrot' a.$EXE -n 16000
}
meteor() {
runonly echo 'meteor 2098'
run "gcc $gccm -O2 meteor-contest.c" a.$EXE 2098
run 'gccgo -O2 meteor-contest.go' a.$EXE -n 2098
- run 'gc meteor-contest' $O.$EXE -n 2098
- run 'gc_B meteor-contest' $O.$EXE -n 2098
+ run 'gc meteor-contest' a.$EXE -n 2098
+ run 'gc_B meteor-contest' a.$EXE -n 2098
}
pidigits() {
@@ -217,22 +216,22 @@
run "gcc $gccm -O2 pidigits.c -lgmp" a.$EXE 10000
fi
run 'gccgo -O2 pidigits.go' a.$EXE -n 10000
- run 'gc pidigits' $O.$EXE -n 10000
- run 'gc_B pidigits' $O.$EXE -n 10000
+ run 'gc pidigits' a.$EXE -n 10000
+ run 'gc_B pidigits' a.$EXE -n 10000
}
threadring() {
runonly echo 'threadring 50000000'
run "gcc $gccm -O2 threadring.c -lpthread" a.$EXE 50000000
run 'gccgo -O2 threadring.go' a.$EXE -n 50000000
- run 'gc threadring' $O.$EXE -n 50000000
+ run 'gc threadring' a.$EXE -n 50000000
}
chameneos() {
runonly echo 'chameneos 6000000'
run "gcc $gccm -O2 chameneosredux.c -lpthread" a.$EXE 6000000
run 'gccgo -O2 chameneosredux.go' a.$EXE 6000000
- run 'gc chameneosredux' $O.$EXE 6000000
+ run 'gc chameneosredux' a.$EXE 6000000
}
case $# in
diff --git a/test/convlit.go b/test/convlit.go
index 8a6145d..904e1e6 100644
--- a/test/convlit.go
+++ b/test/convlit.go
@@ -9,6 +9,8 @@
package main
+import "unsafe"
+
// explicit conversion of constants
var x1 = string(1)
var x2 string = string(1)
@@ -18,6 +20,11 @@
var x6 = int(1e100) // ERROR "overflow"
var x7 = float32(1e1000) // ERROR "overflow"
+// unsafe.Pointer can only convert to/from uintptr
+var _ = string(unsafe.Pointer(uintptr(65))) // ERROR "convert"
+var _ = float64(unsafe.Pointer(uintptr(65))) // ERROR "convert"
+var _ = int(unsafe.Pointer(uintptr(65))) // ERROR "convert"
+
// implicit conversions merit scrutiny
var s string
var bad1 string = 1 // ERROR "conver|incompatible|invalid|cannot"
diff --git a/test/escape2.go b/test/escape2.go
index cc71471..dfc37ed 100644
--- a/test/escape2.go
+++ b/test/escape2.go
@@ -787,7 +787,7 @@
}
// does not leak m
-func foo94(m map[*int]*int, b bool) *int { // ERROR "foo94 m does not escape$"
+func foo94(m map[*int]*int, b bool) *int { // ERROR "leaking param: m to result ~r2 level=1"
for k, v := range m {
if b {
return k
@@ -802,8 +802,8 @@
m[x] = x
}
-// does not leak m
-func foo96(m []*int) *int { // ERROR "foo96 m does not escape$"
+// does not leak m but does leak content
+func foo96(m []*int) *int { // ERROR "leaking param: m to result ~r1 level=1"
return m[0]
}
@@ -823,7 +823,7 @@
}
// does not leak m
-func foo100(m []*int) *int { // ERROR "foo100 m does not escape$"
+func foo100(m []*int) *int { // ERROR "leaking param: m to result ~r1 level=1"
for _, v := range m {
return v
}
@@ -863,8 +863,8 @@
copy(y, x)
}
-// does not leak x
-func foo105(x []*int) { // ERROR "foo105 x does not escape$"
+// does not leak x but does leak content
+func foo105(x []*int) { // ERROR "leaking param content: x"
_ = append(y, x...)
}
@@ -894,7 +894,7 @@
return m[nil]
}
-func foo111(x *int) *int { // ERROR "leaking param: x$"
+func foo111(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0"
m := []*int{x} // ERROR "foo111 \[\]\*int literal does not escape$"
return m[0]
}
diff --git a/test/escape2n.go b/test/escape2n.go
index bf8c534..56f05eb 100644
--- a/test/escape2n.go
+++ b/test/escape2n.go
@@ -787,7 +787,7 @@
}
// does not leak m
-func foo94(m map[*int]*int, b bool) *int { // ERROR "foo94 m does not escape$"
+func foo94(m map[*int]*int, b bool) *int { // ERROR "leaking param: m to result ~r2 level=1"
for k, v := range m {
if b {
return k
@@ -802,8 +802,8 @@
m[x] = x
}
-// does not leak m
-func foo96(m []*int) *int { // ERROR "foo96 m does not escape$"
+// does not leak m but does leak content
+func foo96(m []*int) *int { // ERROR "leaking param: m to result ~r1 level=1"
return m[0]
}
@@ -823,7 +823,7 @@
}
// does not leak m
-func foo100(m []*int) *int { // ERROR "foo100 m does not escape$"
+func foo100(m []*int) *int { // ERROR "leaking param: m to result ~r1 level=1"
for _, v := range m {
return v
}
@@ -863,8 +863,8 @@
copy(y, x)
}
-// does not leak x
-func foo105(x []*int) { // ERROR "foo105 x does not escape$"
+// does not leak x but does leak content
+func foo105(x []*int) { // ERROR "leaking param content: x"
_ = append(y, x...)
}
@@ -894,7 +894,7 @@
return m[nil]
}
-func foo111(x *int) *int { // ERROR "leaking param: x$"
+func foo111(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0"
m := []*int{x} // ERROR "foo111 \[\]\*int literal does not escape$"
return m[0]
}
diff --git a/test/escape5.go b/test/escape5.go
index 1d411b3..6a138ea 100644
--- a/test/escape5.go
+++ b/test/escape5.go
@@ -117,7 +117,6 @@
return p, q
}
-
var global interface{}
type T1 struct {
@@ -141,12 +140,12 @@
func f9() {
var j T1 // ERROR "moved to heap: j"
- f8(&j) // ERROR "&j escapes to heap"
+ f8(&j) // ERROR "&j escapes to heap"
}
func f10() {
// These don't escape but are too big for the stack
- var x [1<<30]byte // ERROR "moved to heap: x"
- var y = make([]byte, 1<<30) // ERROR "does not escape"
+ var x [1 << 30]byte // ERROR "moved to heap: x"
+ var y = make([]byte, 1<<30) // ERROR "make\(\[\]byte, 1 << 30\) escapes to heap"
_ = x[0] + y[0]
}
diff --git a/test/escape_array.go b/test/escape_array.go
index ac51fe7..5da7771 100644
--- a/test/escape_array.go
+++ b/test/escape_array.go
@@ -4,10 +4,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Test escape analysis for function parameters.
-
-// In this test almost everything is BAD except the simplest cases
-// where input directly flows to output.
+// Test escape analysis for arrays and some large things
package foo
@@ -59,14 +56,67 @@
return x[1]
}
-// BAD: would be nice to record that *y (content) is what leaks, not y itself
func fum(x *U, y **string) *string { // ERROR "leaking param: x to result ~r2 level=1$" "leaking param content: y$"
x[0] = *y
return x[1]
}
-// BAD: would be nice to record that y[0] (content) is what leaks, not y itself
func fuo(x *U, y *U) *string { // ERROR "leaking param: x to result ~r2 level=1$" "leaking param content: y$"
x[0] = y[0]
return x[1]
}
+
+// These two tests verify that:
+// small array literals are stack allocated;
+// pointers stored in small array literals do not escape;
+// large array literals are heap allocated;
+// pointers stored in large array literals escape.
+func hugeLeaks1(x **string, y **string) { // ERROR "leaking param content: x" "hugeLeaks1 y does not escape" "mark escaped content: x"
+ a := [10]*string{*y}
+ _ = a
+ // 4 x 4,000,000 exceeds MaxStackVarSize, therefore it must be heap allocated if pointers are 4 bytes or larger.
+ b := [4000000]*string{*x} // ERROR "moved to heap: b"
+ _ = b
+}
+
+func hugeLeaks2(x *string, y *string) { // ERROR "leaking param: x" "hugeLeaks2 y does not escape"
+ a := [10]*string{y}
+ _ = a
+ // 4 x 4,000,000 exceeds MaxStackVarSize, therefore it must be heap allocated if pointers are 4 bytes or larger.
+ b := [4000000]*string{x} // ERROR "moved to heap: b"
+ _ = b
+}
+
+// BAD: x need not leak.
+func doesNew1(x *string, y *string) { // ERROR "leaking param: x" "leaking param: y"
+ a := new([10]*string) // ERROR "new\(\[10\]\*string\) does not escape"
+ a[0] = x
+ b := new([65537]*string) // ERROR "new\(\[65537\]\*string\) escapes to heap"
+ b[0] = y
+}
+
+type a10 struct {
+ s *string
+ i [10]int32
+}
+
+type a65537 struct {
+ s *string
+ i [65537]int32
+}
+
+// BAD: x need not leak.
+func doesNew2(x *string, y *string) { // ERROR "leaking param: x" "leaking param: y"
+ a := new(a10) // ERROR "new\(a10\) does not escape"
+ a.s = x
+ b := new(a65537) // ERROR "new\(a65537\) escapes to heap"
+ b.s = y
+}
+
+// BAD: x need not leak.
+func doesMakeSlice(x *string, y *string) { // ERROR "leaking param: x" "leaking param: y"
+ a := make([]*string, 10) // ERROR "make\(\[\]\*string, 10\) does not escape"
+ a[0] = x
+ b := make([]*string, 65537) // ERROR "make\(\[\]\*string, 65537\) escapes to heap"
+ b[0] = y
+}
diff --git a/test/escape_calls.go b/test/escape_calls.go
index f289670..8c9a6da 100644
--- a/test/escape_calls.go
+++ b/test/escape_calls.go
@@ -42,3 +42,13 @@
*np = n
return w + wl + wr
}
+
+// Test for bug where func var f used prototype's escape analysis results.
+func prototype(xyz []string) {} // ERROR "prototype xyz does not escape"
+func bar() {
+ var got [][]string
+ f := prototype
+ f = func(ss []string) { got = append(got, ss) } // ERROR "leaking param: ss" "func literal does not escape"
+ s := "string"
+ f([]string{s}) // ERROR "\[\]string literal escapes to heap"
+}
diff --git a/test/escape_slice.go b/test/escape_slice.go
index 9315e27..0b65997 100644
--- a/test/escape_slice.go
+++ b/test/escape_slice.go
@@ -8,6 +8,11 @@
package escape
+import (
+ "os"
+ "strings"
+)
+
var sink interface{}
func slice0() {
@@ -71,9 +76,8 @@
}
func slice8() {
- // BAD: i should not escape here
- i := 0 // ERROR "moved to heap: i"
- s := []*int{&i} // ERROR "&i escapes to heap" "literal does not escape"
+ i := 0
+ s := []*int{&i} // ERROR "&i does not escape" "literal does not escape"
_ = s
}
@@ -88,3 +92,74 @@
s := []*int{&i} // ERROR "&i escapes to heap" "literal escapes to heap"
return s
}
+
+func envForDir(dir string) []string { // ERROR "dir does not escape"
+ env := os.Environ()
+ return mergeEnvLists([]string{"PWD=" + dir}, env) // ERROR ".PWD=. \+ dir escapes to heap" "\[\]string literal does not escape"
+}
+
+func mergeEnvLists(in, out []string) []string { // ERROR "leaking param content: in" "leaking param content: out" "leaking param: out to result ~r2 level=0"
+NextVar:
+ for _, inkv := range in {
+ k := strings.SplitAfterN(inkv, "=", 2)[0]
+ for i, outkv := range out {
+ if strings.HasPrefix(outkv, k) {
+ out[i] = inkv
+ continue NextVar
+ }
+ }
+ out = append(out, inkv)
+ }
+ return out
+}
+
+const (
+ IPv4len = 4
+ IPv6len = 16
+)
+
+var v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}
+
+func IPv4(a, b, c, d byte) IP {
+ p := make(IP, IPv6len) // ERROR "make\(IP, IPv6len\) escapes to heap"
+ copy(p, v4InV6Prefix)
+ p[12] = a
+ p[13] = b
+ p[14] = c
+ p[15] = d
+ return p
+}
+
+type IP []byte
+
+type IPAddr struct {
+ IP IP
+ Zone string // IPv6 scoped addressing zone
+}
+
+type resolveIPAddrTest struct {
+ network string
+ litAddrOrName string
+ addr *IPAddr
+ err error
+}
+
+var resolveIPAddrTests = []resolveIPAddrTest{
+ {"ip", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+ {"ip4", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+ {"ip4:icmp", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+}
+
+func setupTestData() {
+ resolveIPAddrTests = append(resolveIPAddrTests,
+ []resolveIPAddrTest{ // ERROR "\[\]resolveIPAddrTest literal does not escape"
+ {"ip",
+ "localhost",
+ &IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr literal escapes to heap"
+ nil},
+ {"ip4",
+ "localhost",
+ &IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr literal escapes to heap"
+ nil},
+ }...)
+}
diff --git a/test/fixedbugs/bug248.go b/test/fixedbugs/bug248.go
index 3d9a408..173b46f 100644
--- a/test/fixedbugs/bug248.go
+++ b/test/fixedbugs/bug248.go
@@ -9,16 +9,12 @@
import (
"fmt"
- "go/build"
"os"
"os/exec"
"path/filepath"
)
func main() {
- a, err := build.ArchChar(build.Default.GOARCH)
- check(err)
-
// TODO: If we get rid of errchk, re-enable this test on Windows.
errchk, err := filepath.Abs("errchk")
check(err)
@@ -26,17 +22,17 @@
err = os.Chdir(filepath.Join("fixedbugs", "bug248.dir"))
check(err)
- run("go", "tool", a+"g", "bug0.go")
- run("go", "tool", a+"g", "bug1.go")
- run("go", "tool", a+"g", "bug2.go")
- run(errchk, "go", "tool", a+"g", "-e", "bug3.go")
- run("go", "tool", a+"l", "bug2."+a)
- run(fmt.Sprintf(".%c%s.out", filepath.Separator, a))
+ run("go", "tool", "compile", "bug0.go")
+ run("go", "tool", "compile", "bug1.go")
+ run("go", "tool", "compile", "bug2.go")
+ run(errchk, "go", "tool", "compile", "-e", "bug3.go")
+ run("go", "tool", "link", "bug2.o")
+ run(fmt.Sprintf(".%ca.out", filepath.Separator))
- os.Remove("bug0." + a)
- os.Remove("bug1." + a)
- os.Remove("bug2." + a)
- os.Remove(a + ".out")
+ os.Remove("bug0.o")
+ os.Remove("bug1.o")
+ os.Remove("bug2.o")
+ os.Remove("a.out")
}
func run(name string, args ...string) {
diff --git a/test/fixedbugs/bug302.go b/test/fixedbugs/bug302.go
index 327d522..42345a9 100644
--- a/test/fixedbugs/bug302.go
+++ b/test/fixedbugs/bug302.go
@@ -9,26 +9,18 @@
import (
"fmt"
- "go/build"
"os"
"os/exec"
"path/filepath"
- "runtime"
)
func main() {
- a, err := build.ArchChar(runtime.GOARCH)
- if err != nil {
- fmt.Println("BUG:", err)
- os.Exit(1)
- }
-
- run("go", "tool", a+"g", filepath.Join("fixedbugs", "bug302.dir", "p.go"))
- run("go", "tool", "pack", "grc", "pp.a", "p."+a)
- run("go", "tool", a+"g", "-I", ".", filepath.Join("fixedbugs", "bug302.dir", "main.go"))
- os.Remove("p."+a)
+ run("go", "tool", "compile", filepath.Join("fixedbugs", "bug302.dir", "p.go"))
+ run("go", "tool", "pack", "grc", "pp.a", "p.o")
+ run("go", "tool", "compile", "-I", ".", filepath.Join("fixedbugs", "bug302.dir", "main.go"))
+ os.Remove("p.o")
os.Remove("pp.a")
- os.Remove("main."+a)
+ os.Remove("main.o")
}
func run(cmd string, args ...string) {
diff --git a/test/fixedbugs/bug345.go b/test/fixedbugs/bug345.go
index 745144f..e291a55 100644
--- a/test/fixedbugs/bug345.go
+++ b/test/fixedbugs/bug345.go
@@ -9,16 +9,12 @@
import (
"fmt"
- "go/build"
"os"
"os/exec"
"path/filepath"
)
func main() {
- a, err := build.ArchChar(build.Default.GOARCH)
- check(err)
-
// TODO: If we get rid of errchk, re-enable this test on Plan 9 and Windows.
errchk, err := filepath.Abs("errchk")
check(err)
@@ -26,9 +22,9 @@
err = os.Chdir(filepath.Join(".", "fixedbugs", "bug345.dir"))
check(err)
- run("go", "tool", a+"g", "io.go")
- run(errchk, "go", "tool", a+"g", "-e", "main.go")
- os.Remove("io." + a)
+ run("go", "tool", "compile", "io.go")
+ run(errchk, "go", "tool", "compile", "-e", "main.go")
+ os.Remove("io.o")
}
func run(name string, args ...string) {
diff --git a/test/fixedbugs/bug369.go b/test/fixedbugs/bug369.go
index 519703f..dd48da8 100644
--- a/test/fixedbugs/bug369.go
+++ b/test/fixedbugs/bug369.go
@@ -11,28 +11,24 @@
import (
"fmt"
- "go/build"
"os"
"os/exec"
"path/filepath"
)
func main() {
- a, err := build.ArchChar(build.Default.GOARCH)
+ err := os.Chdir(filepath.Join(".", "fixedbugs", "bug369.dir"))
check(err)
- err = os.Chdir(filepath.Join(".", "fixedbugs", "bug369.dir"))
- check(err)
-
- run("go", "tool", a+"g", "-N", "-o", "slow."+a, "pkg.go")
- run("go", "tool", a+"g", "-o", "fast."+a, "pkg.go")
- run("go", "tool", a+"g", "-o", "main."+a, "main.go")
- run("go", "tool", a+"l", "-o", "a.exe", "main."+a)
+ run("go", "tool", "compile", "-N", "-o", "slow.o", "pkg.go")
+ run("go", "tool", "compile", "-o", "fast.o", "pkg.go")
+ run("go", "tool", "compile", "-o", "main.o", "main.go")
+ run("go", "tool", "link", "-o", "a.exe", "main.o")
run("." + string(filepath.Separator) + "a.exe")
- os.Remove("slow." + a)
- os.Remove("fast." + a)
- os.Remove("main." + a)
+ os.Remove("slow.o")
+ os.Remove("fast.o")
+ os.Remove("main.o")
os.Remove("a.exe")
}
diff --git a/test/fixedbugs/issue10925.go b/test/fixedbugs/issue10925.go
new file mode 100644
index 0000000..30add82
--- /dev/null
+++ b/test/fixedbugs/issue10925.go
@@ -0,0 +1,23 @@
+// run
+
+// 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 "fmt"
+
+func prototype(xyz []string) {}
+func main() {
+ var got [][]string
+ f := prototype
+ f = func(ss []string) { got = append(got, ss) }
+ for _, s := range []string{"one", "two", "three"} {
+ f([]string{s})
+ }
+ if got[0][0] != "one" || got[1][0] != "two" || got[2][0] != "three" {
+ // Bug's wrong output was [[three] [three] [three]]
+ fmt.Println("Expected [[one] [two] [three]], got", got)
+ }
+}
diff --git a/test/fixedbugs/issue7746.go b/test/fixedbugs/issue7746.go
new file mode 100644
index 0000000..0dc119d
--- /dev/null
+++ b/test/fixedbugs/issue7746.go
@@ -0,0 +1,133 @@
+// errorcheck
+
+// 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
+
+const (
+ c0 = 1 << 100
+ c1 = c0 * c0
+ c2 = c1 * c1
+ c3 = c2 * c2 // ERROR "overflow"
+ c4 = c3 * c3
+ c5 = c4 * c4
+ c6 = c5 * c5
+ c7 = c6 * c6
+ c8 = c7 * c7
+ c9 = c8 * c8
+ c10 = c9 * c9
+ c11 = c10 * c10
+ c12 = c11 * c11
+ c13 = c12 * c12
+ c14 = c13 * c13
+ c15 = c14 * c14
+ c16 = c15 * c15
+ c17 = c16 * c16
+ c18 = c17 * c17
+ c19 = c18 * c18
+ c20 = c19 * c19
+ c21 = c20 * c20
+ c22 = c21 * c21
+ c23 = c22 * c22
+ c24 = c23 * c23
+ c25 = c24 * c24
+ c26 = c25 * c25
+ c27 = c26 * c26
+ c28 = c27 * c27
+ c29 = c28 * c28
+ c30 = c29 * c29
+ c31 = c30 * c30
+ c32 = c31 * c31
+ c33 = c32 * c32
+ c34 = c33 * c33
+ c35 = c34 * c34
+ c36 = c35 * c35
+ c37 = c36 * c36
+ c38 = c37 * c37
+ c39 = c38 * c38
+ c40 = c39 * c39
+ c41 = c40 * c40
+ c42 = c41 * c41
+ c43 = c42 * c42
+ c44 = c43 * c43
+ c45 = c44 * c44
+ c46 = c45 * c45
+ c47 = c46 * c46
+ c48 = c47 * c47
+ c49 = c48 * c48
+ c50 = c49 * c49
+ c51 = c50 * c50
+ c52 = c51 * c51
+ c53 = c52 * c52
+ c54 = c53 * c53
+ c55 = c54 * c54
+ c56 = c55 * c55
+ c57 = c56 * c56
+ c58 = c57 * c57
+ c59 = c58 * c58
+ c60 = c59 * c59
+ c61 = c60 * c60
+ c62 = c61 * c61
+ c63 = c62 * c62
+ c64 = c63 * c63
+ c65 = c64 * c64
+ c66 = c65 * c65
+ c67 = c66 * c66
+ c68 = c67 * c67
+ c69 = c68 * c68
+ c70 = c69 * c69
+ c71 = c70 * c70
+ c72 = c71 * c71
+ c73 = c72 * c72
+ c74 = c73 * c73
+ c75 = c74 * c74
+ c76 = c75 * c75
+ c77 = c76 * c76
+ c78 = c77 * c77
+ c79 = c78 * c78
+ c80 = c79 * c79
+ c81 = c80 * c80
+ c82 = c81 * c81
+ c83 = c82 * c82
+ c84 = c83 * c83
+ c85 = c84 * c84
+ c86 = c85 * c85
+ c87 = c86 * c86
+ c88 = c87 * c87
+ c89 = c88 * c88
+ c90 = c89 * c89
+ c91 = c90 * c90
+ c92 = c91 * c91
+ c93 = c92 * c92
+ c94 = c93 * c93
+ c95 = c94 * c94
+ c96 = c95 * c95
+ c97 = c96 * c96
+ c98 = c97 * c97
+ c99 = c98 * c98
+ c100 = c99 * c99
+)
+
+func main() {
+ println(c1 / c1)
+ println(c2 / c2)
+ println(c3 / c3)
+ println(c4 / c4)
+ println(c5 / c5)
+ println(c6 / c6)
+ println(c7 / c7)
+ println(c8 / c8)
+ println(c9 / c9)
+ println(c10 / c10)
+ println(c20 / c20)
+ println(c30 / c30)
+ println(c40 / c40)
+ println(c50 / c50)
+ println(c60 / c60)
+ println(c70 / c70)
+ println(c80 / c80)
+ println(c90 / c90)
+ println(c100 / c100)
+}
diff --git a/test/fixedbugs/issue8183.go b/test/fixedbugs/issue8183.go
new file mode 100644
index 0000000..7104f1e
--- /dev/null
+++ b/test/fixedbugs/issue8183.go
@@ -0,0 +1,23 @@
+// errorcheck
+
+// 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.
+
+// Tests correct reporting of line numbers for errors involving iota,
+// Issue #8183.
+package foo
+
+const (
+ ok = byte(iota + 253)
+ bad
+ barn
+ bard // ERROR "constant 256 overflows byte"
+)
+
+const (
+ c = len([1 - iota]int{})
+ d
+ e // ERROR "array bound must be non-negative" "const initializer len\(composite literal\) is not a constant"
+ f // ERROR "array bound must be non-negative" "const initializer len\(composite literal\) is not a constant"
+)
diff --git a/test/fixedbugs/issue8745.go b/test/fixedbugs/issue8745.go
new file mode 100644
index 0000000..f3a70af
--- /dev/null
+++ b/test/fixedbugs/issue8745.go
@@ -0,0 +1,13 @@
+// errorcheck
+
+// 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 that the error says s[2] is a byte, not a uint8.
+
+package p
+
+func f(s string) {
+ var _ float64 = s[2] // ERROR "cannot use.*type byte.*as type float64"
+}
diff --git a/test/fixedbugs/issue9036.go b/test/fixedbugs/issue9036.go
new file mode 100644
index 0000000..283159e
--- /dev/null
+++ b/test/fixedbugs/issue9036.go
@@ -0,0 +1,29 @@
+// errorcheck
+
+// 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.
+
+// Expects to see error messages on "p" exponents.
+
+package main
+
+import "fmt"
+
+const (
+ x1 = 1.1 // float
+ x2 = 1e10 // float
+ x3 = 0x1e10 // integer (e is a hex digit)
+ x4 = 0x1p10 // ERROR "malformed floating point constant"
+ x5 = 1p10 // ERROR "malformed floating point constant"
+ x6 = 0p0 // ERROR "malformed floating point constant"
+)
+
+func main() {
+ fmt.Printf("%g %T\n", x1, x1)
+ fmt.Printf("%g %T\n", x2, x2)
+ fmt.Printf("%g %T\n", x3, x3)
+ fmt.Printf("%g %T\n", x4, x4)
+ fmt.Printf("%g %T\n", x5, x5)
+ fmt.Printf("%g %T\n", x6, x6)
+}
diff --git a/test/fixedbugs/issue9355.go b/test/fixedbugs/issue9355.go
index bdc0dd0..40c9ba9 100644
--- a/test/fixedbugs/issue9355.go
+++ b/test/fixedbugs/issue9355.go
@@ -8,7 +8,6 @@
import (
"fmt"
- "go/build"
"os"
"os/exec"
"path/filepath"
@@ -20,14 +19,12 @@
if runtime.Compiler != "gc" || runtime.GOOS == "nacl" {
return
}
- a, err := build.ArchChar(runtime.GOARCH)
+
+ err := os.Chdir(filepath.Join("fixedbugs", "issue9355.dir"))
check(err)
- err = os.Chdir(filepath.Join("fixedbugs", "issue9355.dir"))
- check(err)
-
- out := run("go", "tool", a+"g", "-S", "a.go")
- os.Remove("a." + a)
+ out := run("go", "tool", "compile", "-S", "a.go")
+ os.Remove("a.o")
// 6g/8g print the offset as dec, but 5g/9g print the offset as hex.
patterns := []string{
diff --git a/test/fixedbugs/issue9521.go b/test/fixedbugs/issue9521.go
new file mode 100644
index 0000000..ef0a5a6
--- /dev/null
+++ b/test/fixedbugs/issue9521.go
@@ -0,0 +1,18 @@
+// errorcheck
+
+// 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 an incorrect use of the blank identifer is caught.
+// Does not compile.
+
+package main
+
+func f() (_, _ []int) { return }
+func g() (x []int, y float64) { return }
+
+func main() {
+ _ = append(f()) // ERROR "cannot append \[\]int value to \[\]int"
+ _ = append(g()) // ERROR "cannot append float64 value to \[\]int"
+}
diff --git a/test/nosplit.go b/test/nosplit.go
index bd7a8dd..8864137 100644
--- a/test/nosplit.go
+++ b/test/nosplit.go
@@ -184,19 +184,10 @@
goarch = runtime.GOARCH
}
- thechar := ""
- if gochar, err := exec.Command("go", "env", "GOCHAR").Output(); err != nil {
- bug()
- fmt.Printf("running go env GOCHAR: %v\n", err)
- return
- } else {
- thechar = strings.TrimSpace(string(gochar))
- }
-
- version, err := exec.Command("go", "tool", thechar+"g", "-V").Output()
+ version, err := exec.Command("go", "tool", "compile", "-V").Output()
if err != nil {
bug()
- fmt.Printf("running go tool %sg -V: %v\n", thechar, err)
+ fmt.Printf("running go tool compile -V: %v\n", err)
return
}
if strings.Contains(string(version), "framepointer") {
diff --git a/test/rotate0.go b/test/rotate0.go
index 9c4f560..400b225 100644
--- a/test/rotate0.go
+++ b/test/rotate0.go
@@ -1,5 +1,3 @@
-// skip
-
// runoutput ./rotate.go
// Copyright 2013 The Go Authors. All rights reserved.
diff --git a/test/rotate1.go b/test/rotate1.go
index 2d9b797..98b0b1c 100644
--- a/test/rotate1.go
+++ b/test/rotate1.go
@@ -1,5 +1,3 @@
-// skip
-
// runoutput ./rotate.go
// Copyright 2013 The Go Authors. All rights reserved.
diff --git a/test/rotate2.go b/test/rotate2.go
index 9044625..c50f8ce 100644
--- a/test/rotate2.go
+++ b/test/rotate2.go
@@ -1,5 +1,3 @@
-// skip
-
// runoutput ./rotate.go
// Copyright 2013 The Go Authors. All rights reserved.
diff --git a/test/rotate3.go b/test/rotate3.go
index b6b71c8..73d47d8 100644
--- a/test/rotate3.go
+++ b/test/rotate3.go
@@ -1,5 +1,3 @@
-// skip
-
// runoutput ./rotate.go
// Copyright 2013 The Go Authors. All rights reserved.
diff --git a/test/run.go b/test/run.go
index 10ba7a8..47a6298 100644
--- a/test/run.go
+++ b/test/run.go
@@ -15,7 +15,6 @@
"errors"
"flag"
"fmt"
- "go/build"
"io/ioutil"
"log"
"os"
@@ -41,12 +40,6 @@
)
var (
- // gc and ld are [568][gl].
- gc, ld string
-
- // letter is the build.ArchChar
- letter string
-
goos, goarch string
// dirs are the directories to look for *.go files in.
@@ -84,11 +77,6 @@
ratec = make(chan bool, *numParallel)
rungatec = make(chan bool, *runoutputLimit)
- var err error
- letter, err = build.ArchChar(build.Default.GOARCH)
- check(err)
- gc = letter + "g"
- ld = letter + "l"
var tests []*test
if flag.NArg() > 0 {
@@ -192,11 +180,11 @@
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 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 @@
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 @@
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 b0a91ce..c9afd3b7 100644
--- a/test/sinit_run.go
+++ b/test/sinit_run.go
@@ -12,26 +12,19 @@
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 0000000..c9d089f
--- /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 1f25d91..9b741a6 100644
--- a/test/writebarrier.go
+++ b/test/writebarrier.go
@@ -28,7 +28,7 @@
*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 @@
*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 @@
*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 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"
+}