aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2017-02-16 19:35:02 -0500
committerRuss Cox <rsc@golang.org>2017-02-16 19:45:42 -0500
commit3b4fc5d1c67f363322fb141a3a1444b83cccea3a (patch)
tree642bcd2d227ab5041623eb84a1b9d68d5d885027
parentf8b4123613a2cb0c453726033a03a1968205ccae (diff)
parent758a7281abb539018d6458ccb429615206fcc002 (diff)
downloadgo-3b4fc5d1c67f363322fb141a3a1444b83cccea3a.tar.gz
go-3b4fc5d1c67f363322fb141a3a1444b83cccea3a.zip
[dev.typealias] all: merge go1.8 into dev.typealias
This should provide a way for people who want to try "Go 1.8 with type aliases" to do so. Removed go1.8 VERSION file as part of merge. Change-Id: I60d79439677d9980de7b5575e2e6cb9c23be02b6
-rw-r--r--README.md46
-rw-r--r--doc/contribute.html2
-rw-r--r--doc/debugging_with_gdb.html17
-rw-r--r--doc/devel/release.html7
-rw-r--r--doc/gccgo_install.html63
-rw-r--r--doc/go1.8.html42
-rw-r--r--doc/install-source.html3
-rw-r--r--doc/install.html4
-rw-r--r--misc/cgo/test/issue18146.go2
-rw-r--r--misc/cgo/test/sigaltstack.go6
-rw-r--r--misc/cgo/testplugin/src/iface/main.go46
-rw-r--r--misc/cgo/testplugin/src/iface_a/a.go17
-rw-r--r--misc/cgo/testplugin/src/iface_b/b.go17
-rw-r--r--misc/cgo/testplugin/src/iface_i/i.go17
-rw-r--r--misc/cgo/testplugin/src/issue18676/dynamodbstreamsevt/definition.go13
-rw-r--r--misc/cgo/testplugin/src/issue18676/main.go31
-rw-r--r--misc/cgo/testplugin/src/issue18676/plugin.go11
-rwxr-xr-xmisc/cgo/testplugin/test.bash16
-rw-r--r--misc/cgo/testsanitizers/msan_shared.go12
-rwxr-xr-xmisc/cgo/testsanitizers/test.bash21
-rw-r--r--misc/cgo/testshared/shared_test.go11
-rw-r--r--misc/cgo/testshared/src/depBase/dep.go2
-rw-r--r--misc/cgo/testshared/src/exe/exe.go9
-rw-r--r--misc/cgo/testshared/src/iface/main.go17
-rw-r--r--misc/cgo/testshared/src/iface_a/a.go17
-rw-r--r--misc/cgo/testshared/src/iface_b/b.go17
-rw-r--r--misc/cgo/testshared/src/iface_i/i.go17
-rw-r--r--misc/ios/go_darwin_arm_exec.go27
-rw-r--r--src/cmd/compile/internal/gc/alg.go8
-rw-r--r--src/cmd/compile/internal/gc/bimport.go4
-rw-r--r--src/cmd/compile/internal/gc/builtin.go1
-rw-r--r--src/cmd/compile/internal/gc/builtin/runtime.go1
-rw-r--r--src/cmd/compile/internal/gc/main.go13
-rw-r--r--src/cmd/compile/internal/gc/noder.go3
-rw-r--r--src/cmd/compile/internal/gc/ssa.go3
-rw-r--r--src/cmd/compile/internal/gc/subr.go4
-rw-r--r--src/cmd/compile/internal/gc/util.go9
-rw-r--r--src/cmd/compile/internal/gc/walk.go8
-rw-r--r--src/cmd/compile/internal/s390x/ssa.go7
-rw-r--r--src/cmd/compile/internal/ssa/compile.go11
-rw-r--r--src/cmd/compile/internal/ssa/func.go1
-rw-r--r--src/cmd/compile/internal/ssa/gen/PPC64.rules6
-rw-r--r--src/cmd/compile/internal/ssa/gen/S390X.rules115
-rw-r--r--src/cmd/compile/internal/ssa/gen/S390XOps.go3
-rw-r--r--src/cmd/compile/internal/ssa/loopreschedchecks.go517
-rw-r--r--src/cmd/compile/internal/ssa/nilcheck.go22
-rw-r--r--src/cmd/compile/internal/ssa/opGen.go28
-rw-r--r--src/cmd/compile/internal/ssa/rewritePPC64.go12
-rw-r--r--src/cmd/compile/internal/ssa/rewriteS390X.go328
-rw-r--r--src/cmd/compile/internal/ssa/sparsetree.go33
-rw-r--r--src/cmd/compile/internal/ssa/writebarrier.go2
-rw-r--r--src/cmd/dist/test.go5
-rw-r--r--src/cmd/go/alldocs.go9
-rw-r--r--src/cmd/go/bug.go7
-rw-r--r--src/cmd/go/go_test.go30
-rw-r--r--src/cmd/go/pkg.go2
-rw-r--r--src/cmd/go/test.go41
-rw-r--r--src/cmd/go/testdata/timeoutbench_test.go10
-rw-r--r--src/cmd/internal/obj/go.go6
-rw-r--r--src/cmd/link/internal/ld/dwarf.go2
-rw-r--r--src/cmd/link/internal/ld/pcln.go2
-rw-r--r--src/compress/bzip2/bzip2_test.go24
-rw-r--r--src/compress/flate/deflate.go7
-rw-r--r--src/compress/flate/deflate_test.go31
-rw-r--r--src/compress/flate/deflatefast.go19
-rw-r--r--src/compress/gzip/issue14937_test.go14
-rw-r--r--src/crypto/dsa/dsa_test.go4
-rw-r--r--src/crypto/tls/cipher_suites.go6
-rw-r--r--src/crypto/tls/tls.go4
-rw-r--r--src/crypto/x509/cert_pool.go11
-rw-r--r--src/crypto/x509/root_windows.go5
-rw-r--r--src/crypto/x509/x509_test.go4
-rw-r--r--src/database/sql/sql.go91
-rw-r--r--src/database/sql/sql_test.go123
-rw-r--r--src/go/ast/scope.go4
-rw-r--r--src/go/doc/doc_test.go5
-rw-r--r--src/go/parser/performance_test.go13
-rw-r--r--src/go/printer/nodes.go17
-rw-r--r--src/go/printer/printer.go11
-rw-r--r--src/go/printer/testdata/comments2.golden59
-rw-r--r--src/go/printer/testdata/comments2.input63
-rw-r--r--src/go/types/stdlib_test.go1
-rw-r--r--src/net/http/client.go39
-rw-r--r--src/net/http/client_test.go67
-rw-r--r--src/net/http/serve_test.go139
-rw-r--r--src/net/http/server.go10
-rw-r--r--src/net/http/transport_test.go114
-rw-r--r--src/os/os_test.go5
-rw-r--r--src/reflect/all_test.go68
-rw-r--r--src/reflect/value.go8
-rw-r--r--src/runtime/memmove_amd64.s10
-rw-r--r--src/runtime/memmove_test.go20
-rw-r--r--src/runtime/mgc.go12
-rw-r--r--src/runtime/mgcsweep.go5
-rw-r--r--src/runtime/msan.go4
-rw-r--r--src/runtime/plugin.go4
-rw-r--r--src/runtime/proc.go10
-rw-r--r--src/runtime/runtime-gdb_test.go22
-rw-r--r--src/runtime/symtab.go19
-rw-r--r--src/runtime/sys_linux_amd64.s4
-rw-r--r--src/runtime/testdata/testprogcgo/threadpprof.go2
-rw-r--r--src/runtime/testdata/testprogcgo/traceback.go6
-rw-r--r--src/syscall/mkpost.go7
-rw-r--r--src/syscall/ztypes_linux_s390x.go2
-rw-r--r--src/testing/benchmark.go7
-rw-r--r--src/testing/sub_test.go17
-rw-r--r--src/testing/testing.go13
-rw-r--r--src/vendor/golang_org/x/crypto/poly1305/sum_amd64.s10
-rw-r--r--src/vendor/golang_org/x/crypto/poly1305/sum_arm.s14
-rw-r--r--test/fixedbugs/issue10958.go95
-rw-r--r--test/fixedbugs/issue18459.go13
-rw-r--r--test/fixedbugs/issue18661.go18
-rw-r--r--test/fixedbugs/issue18725.go24
-rw-r--r--test/fixedbugs/issue18808.go63
-rw-r--r--test/fixedbugs/issue18906.go36
-rw-r--r--test/live.go3
-rw-r--r--test/nilptr3.go36
-rw-r--r--test/opt_branchlikely.go3
-rw-r--r--test/run.go63
119 files changed, 2659 insertions, 612 deletions
diff --git a/README.md b/README.md
index 281deecdfb..672cdf5568 100644
--- a/README.md
+++ b/README.md
@@ -5,39 +5,37 @@ reliable, and efficient software.
![Gopher image](doc/gopher/fiveyears.jpg)
-For documentation about how to install and use Go,
-visit https://golang.org/ or load doc/install-source.html
-in your web browser.
-
Our canonical Git repository is located at https://go.googlesource.com/go.
There is a mirror of the repository at https://github.com/golang/go.
-Go is the work of hundreds of contributors. We appreciate your help!
+Unless otherwise noted, the Go source files are distributed under the
+BSD-style license found in the LICENSE file.
-To contribute, please read the contribution guidelines:
- https://golang.org/doc/contribute.html
+### Download and Install
-##### Note that we do not accept pull requests and that we use the issue tracker for bug reports and proposals only. Please ask questions on https://forum.golangbridge.org or https://groups.google.com/forum/#!forum/golang-nuts.
+#### Binary Distributions
-Unless otherwise noted, the Go source files are distributed
-under the BSD-style license found in the LICENSE file.
+Official binary distributions are available at https://golang.org/dl/.
---
+After downloading a binary release, visit https://golang.org/doc/install
+or load doc/install.html in your web browser for installation
+instructions.
-## Binary Distribution Notes
+#### Install From Source
-If you have just untarred a binary Go distribution, you need to set
-the environment variable $GOROOT to the full path of the go
-directory (the one containing this file). You can omit the
-variable if you unpack it into /usr/local/go, or if you rebuild
-from sources by running all.bash (see doc/install-source.html).
-You should also add the Go binary directory $GOROOT/bin
-to your shell's path.
+If a binary distribution is not available for your combination of
+operating system and architecture, visit
+https://golang.org/doc/install/source or load doc/install-source.html
+in your web browser for source installation instructions.
-For example, if you extracted the tar file into $HOME/go, you might
-put the following in your .profile:
+### Contributing
- export GOROOT=$HOME/go
- export PATH=$PATH:$GOROOT/bin
+Go is the work of hundreds of contributors. We appreciate your help!
+
+To contribute, please read the contribution guidelines:
+ https://golang.org/doc/contribute.html
-See https://golang.org/doc/install or doc/install.html for more details.
+Note that the Go project does not use GitHub pull requests, and that
+we use the issue tracker for bug reports and proposals only. See
+https://golang.org/wiki/Questions for a list of places to ask
+questions about the Go language.
diff --git a/doc/contribute.html b/doc/contribute.html
index bcf7b25c51..f1a5b27998 100644
--- a/doc/contribute.html
+++ b/doc/contribute.html
@@ -698,7 +698,7 @@ These files will be periodically updated based on the commit logs.
<p>Code that you contribute should use the standard copyright header:</p>
<pre>
-// Copyright 2016 The Go Authors. All rights reserved.
+// Copyright 2017 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.
</pre>
diff --git a/doc/debugging_with_gdb.html b/doc/debugging_with_gdb.html
index 52a6e76723..f0e65ea291 100644
--- a/doc/debugging_with_gdb.html
+++ b/doc/debugging_with_gdb.html
@@ -4,7 +4,8 @@
}-->
<p><i>
-This applies to the <code>gc</code> toolchain. Gccgo has native gdb support.
+This applies to the standard toolchain (the <code>gc</code> Go
+compiler and tools). Gccgo has native gdb support.
Besides this overview you might want to consult the
<a href="http://sourceware.org/gdb/current/onlinedocs/gdb/">GDB manual</a>.
</i></p>
@@ -49,6 +50,14 @@ when debugging, pass the flags <code>-gcflags "-N -l"</code> to the
debugged.
</p>
+<p>
+If you want to use gdb to inspect a core dump, you can trigger a dump
+on a program crash, on systems that permit it, by setting
+<code>GOTRACEBACK=crash</code> in the environment (see the
+<a href="/pkg/runtime/#hdr-Environment_Variables"> runtime package
+documentation</a> for more info).
+</p>
+
<h3 id="Common_Operations">Common Operations</h3>
<ul>
@@ -130,7 +139,7 @@ the DWARF code.
<p>
If you're interested in what the debugging information looks like, run
-'<code>objdump -W 6.out</code>' and browse through the <code>.debug_*</code>
+'<code>objdump -W a.out</code>' and browse through the <code>.debug_*</code>
sections.
</p>
@@ -377,7 +386,9 @@ $3 = struct hchan&lt;*testing.T&gt;
</pre>
<p>
-That <code>struct hchan&lt;*testing.T&gt;</code> is the runtime-internal representation of a channel. It is currently empty, or gdb would have pretty-printed it's contents.
+That <code>struct hchan&lt;*testing.T&gt;</code> is the
+runtime-internal representation of a channel. It is currently empty,
+or gdb would have pretty-printed its contents.
</p>
<p>
diff --git a/doc/devel/release.html b/doc/devel/release.html
index 51957dff28..be340a35fd 100644
--- a/doc/devel/release.html
+++ b/doc/devel/release.html
@@ -69,6 +69,13 @@ See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.7.4">Go
1.7.4 milestone</a> on our issue tracker for details.
</p>
+<p>
+go1.7.5 (released 2017/01/26) includes fixes to the compiler, runtime,
+and the <code>crypto/x509</code> and <code>time</code> packages.
+See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.7.5">Go
+1.7.5 milestone</a> on our issue tracker for details.
+</p>
+
<h2 id="go1.6">go1.6 (released 2016/02/17)</h2>
<p>
diff --git a/doc/gccgo_install.html b/doc/gccgo_install.html
index ef27fd1818..4f6a911541 100644
--- a/doc/gccgo_install.html
+++ b/doc/gccgo_install.html
@@ -52,6 +52,19 @@ user libraries. The Go 1.4 runtime is not fully merged, but that
should not be visible to Go programs.
</p>
+<p>
+The GCC 6 releases include a complete implementation of the Go 1.6.1
+user libraries. The Go 1.6 runtime is not fully merged, but that
+should not be visible to Go programs.
+</p>
+
+<p>
+The GCC 7 releases are expected to include a complete implementation
+of the Go 1.8 user libraries. As with earlier releases, the Go 1.8
+runtime is not fully merged, but that should not be visible to Go
+programs.
+</p>
+
<h2 id="Source_code">Source code</h2>
<p>
@@ -160,23 +173,6 @@ make
make install
</pre>
-<h3 id="Ubuntu">A note on Ubuntu</h3>
-
-<p>
-Current versions of Ubuntu and versions of GCC before 4.8 disagree on
-where system libraries and header files are found. This is not a
-gccgo issue. When building older versions of GCC, setting these
-environment variables while configuring and building gccgo may fix the
-problem.
-</p>
-
-<pre>
-LIBRARY_PATH=/usr/lib/x86_64-linux-gnu
-C_INCLUDE_PATH=/usr/include/x86_64-linux-gnu
-CPLUS_INCLUDE_PATH=/usr/include/x86_64-linux-gnu
-export LIBRARY_PATH C_INCLUDE_PATH CPLUS_INCLUDE_PATH
-</pre>
-
<h2 id="Using_gccgo">Using gccgo</h2>
<p>
@@ -364,12 +360,15 @@ or with C++ code compiled using <code>extern "C"</code>.
<h3 id="Types">Types</h3>
<p>
-Basic types map directly: an <code>int</code> in Go is an <code>int</code>
-in C, an <code>int32</code> is an <code>int32_t</code>,
-etc. Go <code>byte</code> is equivalent to C <code>unsigned
-char</code>.
-Pointers in Go are pointers in C. A Go <code>struct</code> is the same as C
-<code>struct</code> with the same fields and types.
+Basic types map directly: an <code>int32</code> in Go is
+an <code>int32_t</code> in C, an <code>int64</code> is
+an <code>int64_t</code>, etc.
+The Go type <code>int</code> is an integer that is the same size as a
+pointer, and as such corresponds to the C type <code>intptr_t</code>.
+Go <code>byte</code> is equivalent to C <code>unsigned char</code>.
+Pointers in Go are pointers in C.
+A Go <code>struct</code> is the same as C <code>struct</code> with the
+same fields and types.
</p>
<p>
@@ -380,7 +379,7 @@ structure (this is <b style="color: red;">subject to change</b>):
<pre>
struct __go_string {
const unsigned char *__data;
- int __length;
+ intptr_t __length;
};
</pre>
@@ -400,8 +399,8 @@ A slice in Go is a structure. The current definition is
<pre>
struct __go_slice {
void *__values;
- int __count;
- int __capacity;
+ intptr_t __count;
+ intptr_t __capacity;
};
</pre>
@@ -526,15 +525,3 @@ This procedure is full of unstated caveats and restrictions and we make no
guarantee that it will not change in the future. It is more useful as a
starting point for real Go code than as a regular procedure.
</p>
-
-<h2 id="RTEMS_Port">RTEMS Port</h2>
-<p>
-The gccgo compiler has been ported to <a href="http://www.rtems.com/">
-<code>RTEMS</code></a>. <code>RTEMS</code> is a real-time executive
-that provides a high performance environment for embedded applications
-on a range of processors and embedded hardware. The current gccgo
-port is for x86. The goal is to extend the port to most of the
-<a href="http://www.rtems.org/wiki/index.php/SupportedCPUs">
-architectures supported by <code>RTEMS</code></a>. For more information on the port,
-as well as instructions on how to install it, please see this
-<a href="http://www.rtems.org/wiki/index.php/GCCGoRTEMS"><code>RTEMS</code> Wiki page</a>.
diff --git a/doc/go1.8.html b/doc/go1.8.html
index 2ac478632e..9e3145e8b8 100644
--- a/doc/go1.8.html
+++ b/doc/go1.8.html
@@ -93,7 +93,8 @@ On OpenBSD, Go now requires OpenBSD 5.9 or later. <!-- CL 34093 -->
<p>
The Plan 9 port's networking support is now much more complete
and matches the behavior of Unix and Windows with respect to deadlines
-and cancelation.
+and cancelation. For Plan 9 kernel requirements, see the
+<a href="https://golang.org/wiki/Plan9">Plan 9 wiki page</a>.
</p>
<p>
@@ -434,11 +435,11 @@ version of gccgo.
<h3 id="plugin">Plugins</h3>
<p>
- Go now supports a “<code>plugin</code>” build mode for generating
- plugins written in Go, and a
+ Go now provides early support for plugins with a “<code>plugin</code>”
+ build mode for generating plugins written in Go, and a
new <a href="/pkg/plugin/"><code>plugin</code></a> package for
- loading such plugins at run time. Plugin support is only currently
- available on Linux.
+ loading such plugins at run time. Plugin support is currently only
+ available on Linux. Please report any issues.
</p>
<h2 id="runtime">Runtime</h2>
@@ -798,9 +799,9 @@ Optimizations and minor bug fixes are not listed.
hardware support for AES-GCM is present.
</p>
- <p> <!-- CL 27315 -->
+ <p> <!-- CL 27315, CL 35290 -->
AES-128-CBC cipher suites with SHA-256 are also
- now supported.
+ now supported, but disabled by default.
</p>
</dd>
@@ -808,11 +809,6 @@ Optimizations and minor bug fixes are not listed.
<dl id="crypto_x509"><dt><a href="/pkg/crypto/x509/">crypto/x509</a></dt>
<dd>
- <p> <!-- CL 30578 -->
- <a href="/pkg/crypto/x509/#SystemCertPool"><code>SystemCertPool</code></a>
- is now implemented on Windows.
- </p>
-
<p> <!-- CL 24743 -->
PSS signatures are now supported.
</p>
@@ -863,11 +859,12 @@ crypto/x509: return error for missing SerialNumber (CL 27238)
<p>
The <a href="/pkg/database/sql#IsolationLevel"><code>IsolationLevel</code></a>
can now be set when starting a transaction by setting the isolation level
- on the <code>Context</code> then passing that <code>Context</code> to
- <a href="/pkg/database/sql#DB.BeginContext"><code>DB.BeginContext</code></a>.
+ on <a href="/pkg/database/sql#TxOptions.Isolation"><code>TxOptions.Isolation</code></a> and passing
+ it to <a href="/pkg/database/sql#DB.BeginTx"><code>DB.BeginTx</code></a>.
An error will be returned if an isolation level is selected that the driver
does not support. A read-only attribute may also be set on the transaction
- with <a href="/pkg/database/sql/#ReadOnlyContext"><code>ReadOnlyContext</code></a>.
+ by setting <a href="/pkg/database/sql/#TxOptions.ReadOnly"><code>TxOptions.ReadOnly</code></a>
+ to true.
</p>
<p>
Queries now expose the SQL column type information for drivers that support it.
@@ -1617,9 +1614,9 @@ crypto/x509: return error for missing SerialNumber (CL 27238)
June 31 and July 32.
</p>
- <p> <!-- CL 33029 -->
+ <p> <!-- CL 33029 --> <!-- CL 34816 -->
The <code>tzdata</code> database has been updated to version
- 2016i for systems that don't already have a local time zone
+ 2016j for systems that don't already have a local time zone
database.
</p>
@@ -1649,6 +1646,17 @@ crypto/x509: return error for missing SerialNumber (CL 27238)
and only the overall execution of the test binary would fail.
</p>
+ <p><!-- CL 32455 -->
+ The signature of the
+ <a href="/pkg/testing/#MainStart"><code>MainStart</code></a>
+ function has changed, as allowed by the documentation. It is an
+ internal detail and not part of the Go 1 compatibility promise.
+ If you're not calling <code>MainStart</code> directly but see
+ errors, that likely means you set the
+ normally-empty <code>GOROOT</code> environment variable and it
+ doesn't match the version of your <code>go</code> command's binary.
+ </p>
+
</dd>
</dl>
diff --git a/doc/install-source.html b/doc/install-source.html
index 4bf0ba35fb..efe864cb1a 100644
--- a/doc/install-source.html
+++ b/doc/install-source.html
@@ -147,6 +147,9 @@ either the git branch <code>release-branch.go1.4</code> or
which contains the Go 1.4 source code plus accumulated fixes
to keep the tools running on newer operating systems.
(Go 1.4 was the last distribution in which the tool chain was written in C.)
+After unpacking the Go 1.4 source, <code>cd</code> to
+the <code>src</code> subdirectory and run <code>make.bash</code> (or,
+on Windows, <code>make.bat</code>).
</p>
<p>
diff --git a/doc/install.html b/doc/install.html
index 2143d591cb..6bff75c5a0 100644
--- a/doc/install.html
+++ b/doc/install.html
@@ -250,7 +250,7 @@ $ <b>cd $HOME/go/src/hello</b>
$ <b>go build</b>
</pre>
-<pre class="testWindows" style="display: none">
+<pre class="testWindows">
C:\&gt; <b>cd %USERPROFILE%\go\src\hello</b>
C:\Users\Gopher\go\src\hello&gt; <b>go build</b>
</pre>
@@ -267,7 +267,7 @@ $ <b>./hello</b>
hello, world
</pre>
-<pre class="testWindows" style="display: none">
+<pre class="testWindows">
C:\Users\Gopher\go\src\hello&gt; <b>hello</b>
hello, world
</pre>
diff --git a/misc/cgo/test/issue18146.go b/misc/cgo/test/issue18146.go
index ffb04e9037..3c600463f0 100644
--- a/misc/cgo/test/issue18146.go
+++ b/misc/cgo/test/issue18146.go
@@ -73,7 +73,7 @@ func test18146(t *testing.T) {
}
runtime.GOMAXPROCS(threads)
argv := append(os.Args, "-test.run=NoSuchTestExists")
- if err := syscall.Exec(os.Args[0], argv, nil); err != nil {
+ if err := syscall.Exec(os.Args[0], argv, os.Environ()); err != nil {
t.Fatal(err)
}
}
diff --git a/misc/cgo/test/sigaltstack.go b/misc/cgo/test/sigaltstack.go
index b16adc7d88..2b7a1ec9ad 100644
--- a/misc/cgo/test/sigaltstack.go
+++ b/misc/cgo/test/sigaltstack.go
@@ -17,7 +17,7 @@ package cgotest
static stack_t oss;
static char signalStack[SIGSTKSZ];
-static void changeSignalStack() {
+static void changeSignalStack(void) {
stack_t ss;
memset(&ss, 0, sizeof ss);
ss.ss_sp = signalStack;
@@ -29,7 +29,7 @@ static void changeSignalStack() {
}
}
-static void restoreSignalStack() {
+static void restoreSignalStack(void) {
#if (defined(__x86_64__) || defined(__i386__)) && defined(__APPLE__)
// The Darwin C library enforces a minimum that the kernel does not.
// This is OK since we allocated this much space in mpreinit,
@@ -42,7 +42,7 @@ static void restoreSignalStack() {
}
}
-static int zero() {
+static int zero(void) {
return 0;
}
*/
diff --git a/misc/cgo/testplugin/src/iface/main.go b/misc/cgo/testplugin/src/iface/main.go
new file mode 100644
index 0000000000..5e7e4d8b48
--- /dev/null
+++ b/misc/cgo/testplugin/src/iface/main.go
@@ -0,0 +1,46 @@
+// Copyright 2017 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 (
+ "iface_i"
+ "log"
+ "plugin"
+)
+
+func main() {
+ a, err := plugin.Open("iface_a.so")
+ if err != nil {
+ log.Fatalf(`plugin.Open("iface_a.so"): %v`, err)
+ }
+ b, err := plugin.Open("iface_b.so")
+ if err != nil {
+ log.Fatalf(`plugin.Open("iface_b.so"): %v`, err)
+ }
+
+ af, err := a.Lookup("F")
+ if err != nil {
+ log.Fatalf(`a.Lookup("F") failed: %v`, err)
+ }
+ bf, err := b.Lookup("F")
+ if err != nil {
+ log.Fatalf(`b.Lookup("F") failed: %v`, err)
+ }
+ if af.(func() interface{})() != bf.(func() interface{})() {
+ panic("empty interfaces not equal")
+ }
+
+ ag, err := a.Lookup("G")
+ if err != nil {
+ log.Fatalf(`a.Lookup("G") failed: %v`, err)
+ }
+ bg, err := b.Lookup("G")
+ if err != nil {
+ log.Fatalf(`b.Lookup("G") failed: %v`, err)
+ }
+ if ag.(func() iface_i.I)() != bg.(func() iface_i.I)() {
+ panic("nonempty interfaces not equal")
+ }
+}
diff --git a/misc/cgo/testplugin/src/iface_a/a.go b/misc/cgo/testplugin/src/iface_a/a.go
new file mode 100644
index 0000000000..29d2e27764
--- /dev/null
+++ b/misc/cgo/testplugin/src/iface_a/a.go
@@ -0,0 +1,17 @@
+// Copyright 2017 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 "iface_i"
+
+//go:noinline
+func F() interface{} {
+ return (*iface_i.T)(nil)
+}
+
+//go:noinline
+func G() iface_i.I {
+ return (*iface_i.T)(nil)
+}
diff --git a/misc/cgo/testplugin/src/iface_b/b.go b/misc/cgo/testplugin/src/iface_b/b.go
new file mode 100644
index 0000000000..29d2e27764
--- /dev/null
+++ b/misc/cgo/testplugin/src/iface_b/b.go
@@ -0,0 +1,17 @@
+// Copyright 2017 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 "iface_i"
+
+//go:noinline
+func F() interface{} {
+ return (*iface_i.T)(nil)
+}
+
+//go:noinline
+func G() iface_i.I {
+ return (*iface_i.T)(nil)
+}
diff --git a/misc/cgo/testplugin/src/iface_i/i.go b/misc/cgo/testplugin/src/iface_i/i.go
new file mode 100644
index 0000000000..31c80387c7
--- /dev/null
+++ b/misc/cgo/testplugin/src/iface_i/i.go
@@ -0,0 +1,17 @@
+// Copyright 2017 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 iface_i
+
+type I interface {
+ M()
+}
+
+type T struct {
+}
+
+func (t *T) M() {
+}
+
+// *T implements I
diff --git a/misc/cgo/testplugin/src/issue18676/dynamodbstreamsevt/definition.go b/misc/cgo/testplugin/src/issue18676/dynamodbstreamsevt/definition.go
new file mode 100644
index 0000000000..70fd054d08
--- /dev/null
+++ b/misc/cgo/testplugin/src/issue18676/dynamodbstreamsevt/definition.go
@@ -0,0 +1,13 @@
+// Copyright 2017 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 dynamodbstreamsevt
+
+import "encoding/json"
+
+var foo json.RawMessage
+
+type Event struct{}
+
+func (e *Event) Dummy() {}
diff --git a/misc/cgo/testplugin/src/issue18676/main.go b/misc/cgo/testplugin/src/issue18676/main.go
new file mode 100644
index 0000000000..c75409dafe
--- /dev/null
+++ b/misc/cgo/testplugin/src/issue18676/main.go
@@ -0,0 +1,31 @@
+// Copyright 2017 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.
+
+// The bug happened like this:
+// 1) The main binary adds an itab for *json.UnsupportedValueError / error
+// (concrete type / interface type). This itab goes in hash bucket 0x111.
+// 2) The plugin adds that same itab again. That makes a cycle in the itab
+// chain rooted at hash bucket 0x111.
+// 3) The main binary then asks for the itab for *dynamodbstreamsevt.Event /
+// json.Unmarshaler. This itab happens to also live in bucket 0x111.
+// The lookup code goes into an infinite loop searching for this itab.
+// The code is carefully crafted so that the two itabs are both from the
+// same bucket, and so that the second itab doesn't exist in
+// the itab hashmap yet (so the entire linked list must be searched).
+package main
+
+import (
+ "encoding/json"
+ "issue18676/dynamodbstreamsevt"
+ "plugin"
+)
+
+func main() {
+ plugin.Open("plugin.so")
+
+ var x interface{} = (*dynamodbstreamsevt.Event)(nil)
+ if _, ok := x.(json.Unmarshaler); !ok {
+ println("something")
+ }
+}
diff --git a/misc/cgo/testplugin/src/issue18676/plugin.go b/misc/cgo/testplugin/src/issue18676/plugin.go
new file mode 100644
index 0000000000..8a3b85a75c
--- /dev/null
+++ b/misc/cgo/testplugin/src/issue18676/plugin.go
@@ -0,0 +1,11 @@
+// Copyright 2017 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 "C"
+
+import "issue18676/dynamodbstreamsevt"
+
+func F(evt *dynamodbstreamsevt.Event) {}
diff --git a/misc/cgo/testplugin/test.bash b/misc/cgo/testplugin/test.bash
index fee99a758c..ab7430acc3 100755
--- a/misc/cgo/testplugin/test.bash
+++ b/misc/cgo/testplugin/test.bash
@@ -15,8 +15,8 @@ goos=$(go env GOOS)
goarch=$(go env GOARCH)
function cleanup() {
- rm -f plugin*.so unnamed*.so
- rm -rf host pkg sub
+ rm -f plugin*.so unnamed*.so iface*.so
+ rm -rf host pkg sub iface issue18676
}
trap cleanup EXIT
@@ -32,3 +32,15 @@ GOPATH=$(pwd) go build -buildmode=plugin unnamed2.go
GOPATH=$(pwd) go build host
LD_LIBRARY_PATH=$(pwd) ./host
+
+# Test that types and itabs get properly uniqified.
+GOPATH=$(pwd) go build -buildmode=plugin iface_a
+GOPATH=$(pwd) go build -buildmode=plugin iface_b
+GOPATH=$(pwd) go build iface
+LD_LIBRARY_PATH=$(pwd) ./iface
+
+# Test for issue 18676 - make sure we don't add the same itab twice.
+# The buggy code hangs forever, so use a timeout to check for that.
+GOPATH=$(pwd) go build -buildmode=plugin -o plugin.so src/issue18676/plugin.go
+GOPATH=$(pwd) go build -o issue18676 src/issue18676/main.go
+timeout 10s ./issue18676
diff --git a/misc/cgo/testsanitizers/msan_shared.go b/misc/cgo/testsanitizers/msan_shared.go
new file mode 100644
index 0000000000..966947cac3
--- /dev/null
+++ b/misc/cgo/testsanitizers/msan_shared.go
@@ -0,0 +1,12 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This program segfaulted during libpreinit when built with -msan:
+// http://golang.org/issue/18707
+
+package main
+
+import "C"
+
+func main() {}
diff --git a/misc/cgo/testsanitizers/test.bash b/misc/cgo/testsanitizers/test.bash
index dfc6d3819a..4da85020d8 100755
--- a/misc/cgo/testsanitizers/test.bash
+++ b/misc/cgo/testsanitizers/test.bash
@@ -68,6 +68,25 @@ fi
status=0
+testmsanshared() {
+ goos=$(go env GOOS)
+ suffix="-installsuffix testsanitizers"
+ libext="so"
+ if [ "$goos" == "darwin" ]; then
+ libext="dylib"
+ fi
+ go build -msan -buildmode=c-shared $suffix -o ${TMPDIR}/libmsanshared.$libext msan_shared.go
+
+ echo 'int main() { return 0; }' > ${TMPDIR}/testmsanshared.c
+ $CC $(go env GOGCCFLAGS) -fsanitize=memory -o ${TMPDIR}/testmsanshared ${TMPDIR}/testmsanshared.c ${TMPDIR}/libmsanshared.$libext
+
+ if ! LD_LIBRARY_PATH=. ${TMPDIR}/testmsanshared; then
+ echo "FAIL: msan_shared"
+ status=1
+ fi
+ rm -f ${TMPDIR}/{testmsanshared,testmsanshared.c,libmsanshared.$libext}
+}
+
if test "$msan" = "yes"; then
if ! go build -msan std; then
echo "FAIL: build -msan std"
@@ -108,6 +127,8 @@ if test "$msan" = "yes"; then
echo "FAIL: msan_fail"
status=1
fi
+
+ testmsanshared
fi
if test "$tsan" = "yes"; then
diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go
index af4f91550f..f0766e511e 100644
--- a/misc/cgo/testshared/shared_test.go
+++ b/misc/cgo/testshared/shared_test.go
@@ -815,3 +815,14 @@ func TestImplicitInclusion(t *testing.T) {
goCmd(t, "install", "-linkshared", "implicitcmd")
run(t, "running executable linked against library that contains same package as it", "./bin/implicitcmd")
}
+
+// Tests to make sure that the type fields of empty interfaces and itab
+// fields of nonempty interfaces are unique even across modules,
+// so that interface equality works correctly.
+func TestInterface(t *testing.T) {
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "iface_a")
+ // Note: iface_i gets installed implicitly as a dependency of iface_a.
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "iface_b")
+ goCmd(t, "install", "-linkshared", "iface")
+ run(t, "running type/itab uniqueness tester", "./bin/iface")
+}
diff --git a/misc/cgo/testshared/src/depBase/dep.go b/misc/cgo/testshared/src/depBase/dep.go
index a518b4efe2..9f86710db0 100644
--- a/misc/cgo/testshared/src/depBase/dep.go
+++ b/misc/cgo/testshared/src/depBase/dep.go
@@ -5,6 +5,8 @@ import (
"reflect"
)
+var SlicePtr interface{} = &[]int{}
+
var V int = 1
var HasMask []string = []string{"hi"}
diff --git a/misc/cgo/testshared/src/exe/exe.go b/misc/cgo/testshared/src/exe/exe.go
index 433727112b..84302a811f 100644
--- a/misc/cgo/testshared/src/exe/exe.go
+++ b/misc/cgo/testshared/src/exe/exe.go
@@ -19,6 +19,8 @@ func F() *C {
return nil
}
+var slicePtr interface{} = &[]int{}
+
func main() {
defer depBase.ImplementedInAsm()
// This code below causes various go.itab.* symbols to be generated in
@@ -32,4 +34,11 @@ func main() {
if reflect.TypeOf(F).Out(0) != reflect.TypeOf(c) {
panic("bad reflection results, see golang.org/issue/18252")
}
+
+ sp := reflect.New(reflect.TypeOf(slicePtr).Elem())
+ s := sp.Interface()
+
+ if reflect.TypeOf(s) != reflect.TypeOf(slicePtr) {
+ panic("bad reflection results, see golang.org/issue/18729")
+ }
}
diff --git a/misc/cgo/testshared/src/iface/main.go b/misc/cgo/testshared/src/iface/main.go
new file mode 100644
index 0000000000..3d5b54e73b
--- /dev/null
+++ b/misc/cgo/testshared/src/iface/main.go
@@ -0,0 +1,17 @@
+// Copyright 2017 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 "iface_a"
+import "iface_b"
+
+func main() {
+ if iface_a.F() != iface_b.F() {
+ panic("empty interfaces not equal")
+ }
+ if iface_a.G() != iface_b.G() {
+ panic("non-empty interfaces not equal")
+ }
+}
diff --git a/misc/cgo/testshared/src/iface_a/a.go b/misc/cgo/testshared/src/iface_a/a.go
new file mode 100644
index 0000000000..e11047c166
--- /dev/null
+++ b/misc/cgo/testshared/src/iface_a/a.go
@@ -0,0 +1,17 @@
+// Copyright 2017 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 iface_a
+
+import "iface_i"
+
+//go:noinline
+func F() interface{} {
+ return (*iface_i.T)(nil)
+}
+
+//go:noinline
+func G() iface_i.I {
+ return (*iface_i.T)(nil)
+}
diff --git a/misc/cgo/testshared/src/iface_b/b.go b/misc/cgo/testshared/src/iface_b/b.go
new file mode 100644
index 0000000000..47aee2e77e
--- /dev/null
+++ b/misc/cgo/testshared/src/iface_b/b.go
@@ -0,0 +1,17 @@
+// Copyright 2017 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 iface_b
+
+import "iface_i"
+
+//go:noinline
+func F() interface{} {
+ return (*iface_i.T)(nil)
+}
+
+//go:noinline
+func G() iface_i.I {
+ return (*iface_i.T)(nil)
+}
diff --git a/misc/cgo/testshared/src/iface_i/i.go b/misc/cgo/testshared/src/iface_i/i.go
new file mode 100644
index 0000000000..31c80387c7
--- /dev/null
+++ b/misc/cgo/testshared/src/iface_i/i.go
@@ -0,0 +1,17 @@
+// Copyright 2017 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 iface_i
+
+type I interface {
+ M()
+}
+
+type T struct {
+}
+
+func (t *T) M() {
+}
+
+// *T implements I
diff --git a/misc/ios/go_darwin_arm_exec.go b/misc/ios/go_darwin_arm_exec.go
index 1eeb289c7d..3de341b9c5 100644
--- a/misc/ios/go_darwin_arm_exec.go
+++ b/misc/ios/go_darwin_arm_exec.go
@@ -99,7 +99,7 @@ func main() {
// Approximately 1 in a 100 binaries fail to start. If it happens,
// try again. These failures happen for several reasons beyond
// our control, but all of them are safe to retry as they happen
- // before lldb encounters the initial getwd breakpoint. As we
+ // before lldb encounters the initial SIGUSR2 stop. As we
// know the tests haven't started, we are not hiding flaky tests
// with this retry.
for i := 0; i < 5; i++ {
@@ -204,6 +204,11 @@ func run(bin string, args []string) (err error) {
var opts options
opts, args = parseArgs(args)
+ // Pass the suffix for the current working directory as the
+ // first argument to the test. For iOS, cmd/go generates
+ // special handling of this argument.
+ args = append([]string{"cwdSuffix=" + pkgpath}, args...)
+
// ios-deploy invokes lldb to give us a shell session with the app.
s, err := newSession(appdir, args, opts)
if err != nil {
@@ -224,6 +229,7 @@ func run(bin string, args []string) (err error) {
s.do(`process handle SIGHUP --stop false --pass true --notify false`)
s.do(`process handle SIGPIPE --stop false --pass true --notify false`)
s.do(`process handle SIGUSR1 --stop false --pass true --notify false`)
+ s.do(`process handle SIGUSR2 --stop true --pass false --notify true`) // sent by test harness
s.do(`process handle SIGCONT --stop false --pass true --notify false`)
s.do(`process handle SIGSEGV --stop false --pass true --notify false`) // does not work
s.do(`process handle SIGBUS --stop false --pass true --notify false`) // does not work
@@ -236,20 +242,9 @@ func run(bin string, args []string) (err error) {
return nil
}
- s.do(`breakpoint set -n getwd`) // in runtime/cgo/gcc_darwin_arm.go
-
started = true
- s.doCmd("run", "stop reason = breakpoint", 20*time.Second)
-
- // Move the current working directory into the faux gopath.
- if pkgpath != "src" {
- s.do(`breakpoint delete 1`)
- s.do(`expr char* $mem = (char*)malloc(512)`)
- s.do(`expr $mem = (char*)getwd($mem, 512)`)
- s.do(`expr $mem = (char*)strcat($mem, "/` + pkgpath + `")`)
- s.do(`call (void)chdir($mem)`)
- }
+ s.doCmd("run", "stop reason = signal SIGUSR2", 20*time.Second)
startTestsLen := s.out.Len()
fmt.Fprintln(s.in, `process continue`)
@@ -520,13 +515,11 @@ func copyLocalData(dstbase string) (pkgpath string, err error) {
// Copy timezone file.
//
- // Typical apps have the zoneinfo.zip in the root of their app bundle,
+ // Apps have the zoneinfo.zip in the root of their app bundle,
// read by the time package as the working directory at initialization.
- // As we move the working directory to the GOROOT pkg directory, we
- // install the zoneinfo.zip file in the pkgpath.
if underGoRoot {
err := cp(
- filepath.Join(dstbase, pkgpath),
+ dstbase,
filepath.Join(cwd, "lib", "time", "zoneinfo.zip"),
)
if err != nil {
diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go
index 8113710e39..d4f3d9884e 100644
--- a/src/cmd/compile/internal/gc/alg.go
+++ b/src/cmd/compile/internal/gc/alg.go
@@ -303,7 +303,9 @@ func genhash(sym *Sym, t *Type) {
typecheckslice(fn.Nbody.Slice(), Etop)
Curfn = nil
popdcl()
- testdclstack()
+ if debug_dclstack != 0 {
+ testdclstack()
+ }
// Disable safemode while compiling this code: the code we
// generate internally can refer to unsafe.Pointer.
@@ -493,7 +495,9 @@ func geneq(sym *Sym, t *Type) {
typecheckslice(fn.Nbody.Slice(), Etop)
Curfn = nil
popdcl()
- testdclstack()
+ if debug_dclstack != 0 {
+ testdclstack()
+ }
// Disable safemode while compiling this code: the code we
// generate internally can refer to unsafe.Pointer.
diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go
index 1ee9e76737..752f65be42 100644
--- a/src/cmd/compile/internal/gc/bimport.go
+++ b/src/cmd/compile/internal/gc/bimport.go
@@ -217,7 +217,9 @@ func Import(in *bufio.Reader) {
typecheckok = tcok
resumecheckwidth()
- testdclstack() // debugging only
+ if debug_dclstack != 0 {
+ testdclstack()
+ }
}
func formatErrorf(format string, args ...interface{}) {
diff --git a/src/cmd/compile/internal/gc/builtin.go b/src/cmd/compile/internal/gc/builtin.go
index e02e2feb01..71b323f8a1 100644
--- a/src/cmd/compile/internal/gc/builtin.go
+++ b/src/cmd/compile/internal/gc/builtin.go
@@ -15,6 +15,7 @@ var runtimeDecls = [...]struct {
{"panicwrap", funcTag, 7},
{"gopanic", funcTag, 9},
{"gorecover", funcTag, 12},
+ {"goschedguarded", funcTag, 5},
{"printbool", funcTag, 14},
{"printfloat", funcTag, 16},
{"printint", funcTag, 18},
diff --git a/src/cmd/compile/internal/gc/builtin/runtime.go b/src/cmd/compile/internal/gc/builtin/runtime.go
index 98e25fefb8..69511155f4 100644
--- a/src/cmd/compile/internal/gc/builtin/runtime.go
+++ b/src/cmd/compile/internal/gc/builtin/runtime.go
@@ -21,6 +21,7 @@ func panicwrap(string, string, string)
func gopanic(interface{})
func gorecover(*int32) interface{}
+func goschedguarded()
func printbool(bool)
func printfloat(float64)
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 11f0547d5e..f5fb72bca5 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -30,11 +30,12 @@ var (
)
var (
- Debug_append int
- Debug_closure int
- Debug_panic int
- Debug_slice int
- Debug_wb int
+ Debug_append int
+ Debug_closure int
+ debug_dclstack int
+ Debug_panic int
+ Debug_slice int
+ Debug_wb int
)
// Debug arguments.
@@ -48,6 +49,7 @@ var debugtab = []struct {
{"append", &Debug_append}, // print information about append compilation
{"closure", &Debug_closure}, // print information about closure compilation
{"disablenil", &disable_checknil}, // disable nil checks
+ {"dclstack", &debug_dclstack}, // run internal dclstack checks
{"gcprog", &Debug_gcprog}, // print dump of GC programs
{"nil", &Debug_checknil}, // print information about nil checks
{"panic", &Debug_panic}, // do not hide any compiler panic
@@ -325,7 +327,6 @@ func Main() {
timings.Stop()
timings.AddEvent(int64(lexlineno-lexlineno0), "lines")
- testdclstack()
mkpackage(localpkg.Name) // final import not used checks
finishUniverse()
diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go
index 1d69151cc4..9dbe769fa7 100644
--- a/src/cmd/compile/internal/gc/noder.go
+++ b/src/cmd/compile/internal/gc/noder.go
@@ -34,6 +34,7 @@ func parseFile(filename string) {
}
if nsyntaxerrors == 0 {
+ // Always run testdclstack here, even when debug_dclstack is not set, as a sanity measure.
testdclstack()
}
}
@@ -1051,6 +1052,7 @@ func (p *noder) pragma(pos, line int, text string) syntax.Pragma {
lookup(f[1]).Linkname = f[2]
case strings.HasPrefix(text, "go:cgo_"):
+ lineno = p.baseline + int32(line) - 1 // pragcgo may call yyerror
pragcgobuf += pragcgo(text)
fallthrough // because of //go:cgo_unsafe_args
default:
@@ -1058,6 +1060,7 @@ func (p *noder) pragma(pos, line int, text string) syntax.Pragma {
if i := strings.Index(text, " "); i >= 0 {
verb = verb[:i]
}
+ lineno = p.baseline + int32(line) - 1 // pragmaValue may call yyerror
return syntax.Pragma(pragmaValue(verb))
}
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index 55ee3c01dc..bf483f8416 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -64,6 +64,9 @@ func buildssa(fn *Node) *ssa.Func {
s.config = initssa()
s.f = s.config.NewFunc()
s.f.Name = name
+ if fn.Func.Pragma&Nosplit != 0 {
+ s.f.NoSplit = true
+ }
s.exitCode = fn.Func.Exit
s.panics = map[funcLine]*ssa.Block{}
s.config.DebugTest = s.config.DebugHashMatch("GOSSAHASH", name)
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index 9b9a3f1210..7d008dfa65 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -1833,7 +1833,9 @@ func genwrapper(rcvr *Type, method *Field, newnam *Sym, iface int) {
funcbody(fn)
Curfn = fn
popdcl()
- testdclstack()
+ if debug_dclstack != 0 {
+ testdclstack()
+ }
// wrappers where T is anonymous (struct or interface) can be duplicated.
if rcvr.IsStruct() || rcvr.IsInterface() || rcvr.IsPtr() && rcvr.Elem().IsStruct() {
diff --git a/src/cmd/compile/internal/gc/util.go b/src/cmd/compile/internal/gc/util.go
index bb5cede5a6..c62bd00808 100644
--- a/src/cmd/compile/internal/gc/util.go
+++ b/src/cmd/compile/internal/gc/util.go
@@ -57,8 +57,13 @@ func startProfile() {
Fatalf("%v", err)
}
atExit(func() {
- runtime.GC() // profile all outstanding allocations
- if err := pprof.WriteHeapProfile(f); err != nil {
+ // Profile all outstanding allocations.
+ runtime.GC()
+ // compilebench parses the memory profile to extract memstats,
+ // which are only written in the legacy pprof format.
+ // See golang.org/issue/18641 and runtime/pprof/pprof.go:writeHeap.
+ const writeLegacyFormat = 1
+ if err := pprof.Lookup("heap").WriteTo(f, writeLegacyFormat); err != nil {
Fatalf("%v", err)
}
})
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index efe2016e46..7c2e2ab442 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -3117,12 +3117,12 @@ func walkcompare(n *Node, init *Nodes) *Node {
cmpr = cmpr.Left
}
- if !islvalue(cmpl) || !islvalue(cmpr) {
- Fatalf("arguments of comparison must be lvalues - %v %v", cmpl, cmpr)
- }
-
// Chose not to inline. Call equality function directly.
if !inline {
+ if !islvalue(cmpl) || !islvalue(cmpr) {
+ Fatalf("arguments of comparison must be lvalues - %v %v", cmpl, cmpr)
+ }
+
// eq algs take pointers
pl := temp(ptrto(t))
al := nod(OAS, pl, nod(OADDR, cmpl, nil))
diff --git a/src/cmd/compile/internal/s390x/ssa.go b/src/cmd/compile/internal/s390x/ssa.go
index e2d3c2849b..a9dafa862e 100644
--- a/src/cmd/compile/internal/s390x/ssa.go
+++ b/src/cmd/compile/internal/s390x/ssa.go
@@ -424,7 +424,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
gc.AddAux2(&p.To, v, sc.Off())
- case ssa.OpCopy, ssa.OpS390XMOVDconvert:
+ case ssa.OpCopy, ssa.OpS390XMOVDconvert, ssa.OpS390XMOVDreg:
if v.Type.IsMemory() {
return
}
@@ -433,6 +433,11 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
if x != y {
opregreg(moveByType(v.Type), y, x)
}
+ case ssa.OpS390XMOVDnop:
+ if v.Reg() != v.Args[0].Reg() {
+ v.Fatalf("input[0] and output not in same register %s", v.LongString())
+ }
+ // nothing to do
case ssa.OpLoadReg:
if v.Type.IsFlags() {
v.Fatalf("load flags not implemented: %v", v.LongString())
diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go
index b9ec7eb6b7..5b461bac48 100644
--- a/src/cmd/compile/internal/ssa/compile.go
+++ b/src/cmd/compile/internal/ssa/compile.go
@@ -5,6 +5,7 @@
package ssa
import (
+ "cmd/internal/obj"
"fmt"
"log"
"os"
@@ -349,6 +350,8 @@ var passes = [...]pass{
{name: "writebarrier", fn: writebarrier, required: true}, // expand write barrier ops
{name: "fuse", fn: fuse},
{name: "dse", fn: dse},
+ {name: "insert resched checks", fn: insertLoopReschedChecks,
+ disabled: obj.Preemptibleloops_enabled == 0}, // insert resched checks in loops.
{name: "tighten", fn: tighten}, // move values closer to their uses
{name: "lower", fn: lower, required: true},
{name: "lowered cse", fn: cse},
@@ -378,7 +381,13 @@ type constraint struct {
}
var passOrder = [...]constraint{
- // prove reliese on common-subexpression elimination for maximum benefits.
+ // "insert resched checks" uses mem, better to clean out stores first.
+ {"dse", "insert resched checks"},
+ // insert resched checks adds new blocks containing generic instructions
+ {"insert resched checks", "lower"},
+ {"insert resched checks", "tighten"},
+
+ // prove relies on common-subexpression elimination for maximum benefits.
{"generic cse", "prove"},
// deadcode after prove to eliminate all new dead blocks.
{"prove", "generic deadcode"},
diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go
index 7b2097bcae..df29aa3606 100644
--- a/src/cmd/compile/internal/ssa/func.go
+++ b/src/cmd/compile/internal/ssa/func.go
@@ -24,6 +24,7 @@ type Func struct {
vid idAlloc // value ID allocator
scheduled bool // Values in Blocks are in final order
+ NoSplit bool // true if function is marked as nosplit. Used by schedule check pass.
// when register allocation is done, maps value ids to locations
RegAlloc []Location
diff --git a/src/cmd/compile/internal/ssa/gen/PPC64.rules b/src/cmd/compile/internal/ssa/gen/PPC64.rules
index 0e0f1f9c1e..cad753e591 100644
--- a/src/cmd/compile/internal/ssa/gen/PPC64.rules
+++ b/src/cmd/compile/internal/ssa/gen/PPC64.rules
@@ -338,9 +338,9 @@
(Geq32F x y) -> (FGreaterEqual (FCMPU x y))
(Geq64F x y) -> (FGreaterEqual (FCMPU x y))
-(Geq8U x y) -> (GreaterEqual (CMPU (ZeroExt8to32 x) (ZeroExt8to32 y)))
-(Geq16U x y) -> (GreaterEqual (CMPU (ZeroExt16to32 x) (ZeroExt16to32 y)))
-(Geq32U x y) -> (GreaterEqual (CMPU x y))
+(Geq8U x y) -> (GreaterEqual (CMPWU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Geq16U x y) -> (GreaterEqual (CMPWU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Geq32U x y) -> (GreaterEqual (CMPWU x y))
(Geq64U x y) -> (GreaterEqual (CMPU x y))
// Absorb pseudo-ops into blocks.
diff --git a/src/cmd/compile/internal/ssa/gen/S390X.rules b/src/cmd/compile/internal/ssa/gen/S390X.rules
index 3e0533a951..c26515c569 100644
--- a/src/cmd/compile/internal/ssa/gen/S390X.rules
+++ b/src/cmd/compile/internal/ssa/gen/S390X.rules
@@ -312,9 +312,12 @@
// Lowering loads
(Load <t> ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVDload ptr mem)
-(Load <t> ptr mem) && is32BitInt(t) -> (MOVWZload ptr mem)
-(Load <t> ptr mem) && is16BitInt(t) -> (MOVHZload ptr mem)
-(Load <t> ptr mem) && (t.IsBoolean() || is8BitInt(t)) -> (MOVBZload ptr mem)
+(Load <t> ptr mem) && is32BitInt(t) && isSigned(t) -> (MOVWload ptr mem)
+(Load <t> ptr mem) && is32BitInt(t) && !isSigned(t) -> (MOVWZload ptr mem)
+(Load <t> ptr mem) && is16BitInt(t) && isSigned(t) -> (MOVHload ptr mem)
+(Load <t> ptr mem) && is16BitInt(t) && !isSigned(t) -> (MOVHZload ptr mem)
+(Load <t> ptr mem) && is8BitInt(t) && isSigned(t) -> (MOVBload ptr mem)
+(Load <t> ptr mem) && (t.IsBoolean() || (is8BitInt(t) && !isSigned(t))) -> (MOVBZload ptr mem)
(Load <t> ptr mem) && is32BitFloat(t) -> (FMOVSload ptr mem)
(Load <t> ptr mem) && is64BitFloat(t) -> (FMOVDload ptr mem)
@@ -445,16 +448,20 @@
// ***************************
// TODO: Should the optimizations be a separate pass?
+// if a register move has only 1 use, just use the same register without emitting instruction
+// MOVDnop doesn't emit instruction, only for ensuring the type.
+(MOVDreg x) && x.Uses == 1 -> (MOVDnop x)
+
// Fold sign extensions into conditional moves of constants.
// Designed to remove the MOVBZreg inserted by the If lowering.
-(MOVBZreg x:(MOVDLT (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> x
-(MOVBZreg x:(MOVDLE (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> x
-(MOVBZreg x:(MOVDGT (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> x
-(MOVBZreg x:(MOVDGE (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> x
-(MOVBZreg x:(MOVDEQ (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> x
-(MOVBZreg x:(MOVDNE (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> x
-(MOVBZreg x:(MOVDGTnoinv (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> x
-(MOVBZreg x:(MOVDGEnoinv (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> x
+(MOVBZreg x:(MOVDLT (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> (MOVDreg x)
+(MOVBZreg x:(MOVDLE (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> (MOVDreg x)
+(MOVBZreg x:(MOVDGT (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> (MOVDreg x)
+(MOVBZreg x:(MOVDGE (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> (MOVDreg x)
+(MOVBZreg x:(MOVDEQ (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> (MOVDreg x)
+(MOVBZreg x:(MOVDNE (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> (MOVDreg x)
+(MOVBZreg x:(MOVDGTnoinv (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> (MOVDreg x)
+(MOVBZreg x:(MOVDGEnoinv (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> (MOVDreg x)
// Fold boolean tests into blocks.
(NE (CMPWconst [0] (MOVDLT (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no) -> (LT cmp yes no)
@@ -572,46 +579,46 @@
(MOVDNE x y (InvertFlags cmp)) -> (MOVDNE x y cmp)
// don't extend after proper load
-(MOVBreg x:(MOVBload _ _)) -> x
-(MOVBZreg x:(MOVBZload _ _)) -> x
-(MOVHreg x:(MOVBload _ _)) -> x
-(MOVHreg x:(MOVBZload _ _)) -> x
-(MOVHreg x:(MOVHload _ _)) -> x
-(MOVHZreg x:(MOVBZload _ _)) -> x
-(MOVHZreg x:(MOVHZload _ _)) -> x
-(MOVWreg x:(MOVBload _ _)) -> x
-(MOVWreg x:(MOVBZload _ _)) -> x
-(MOVWreg x:(MOVHload _ _)) -> x
-(MOVWreg x:(MOVHZload _ _)) -> x
-(MOVWreg x:(MOVWload _ _)) -> x
-(MOVWZreg x:(MOVBZload _ _)) -> x
-(MOVWZreg x:(MOVHZload _ _)) -> x
-(MOVWZreg x:(MOVWZload _ _)) -> x
+(MOVBreg x:(MOVBload _ _)) -> (MOVDreg x)
+(MOVBZreg x:(MOVBZload _ _)) -> (MOVDreg x)
+(MOVHreg x:(MOVBload _ _)) -> (MOVDreg x)
+(MOVHreg x:(MOVBZload _ _)) -> (MOVDreg x)
+(MOVHreg x:(MOVHload _ _)) -> (MOVDreg x)
+(MOVHZreg x:(MOVBZload _ _)) -> (MOVDreg x)
+(MOVHZreg x:(MOVHZload _ _)) -> (MOVDreg x)
+(MOVWreg x:(MOVBload _ _)) -> (MOVDreg x)
+(MOVWreg x:(MOVBZload _ _)) -> (MOVDreg x)
+(MOVWreg x:(MOVHload _ _)) -> (MOVDreg x)
+(MOVWreg x:(MOVHZload _ _)) -> (MOVDreg x)
+(MOVWreg x:(MOVWload _ _)) -> (MOVDreg x)
+(MOVWZreg x:(MOVBZload _ _)) -> (MOVDreg x)
+(MOVWZreg x:(MOVHZload _ _)) -> (MOVDreg x)
+(MOVWZreg x:(MOVWZload _ _)) -> (MOVDreg x)
// don't extend if argument is already extended
-(MOVBreg x:(Arg <t>)) && is8BitInt(t) && isSigned(t) -> x
-(MOVBZreg x:(Arg <t>)) && is8BitInt(t) && !isSigned(t) -> x
-(MOVHreg x:(Arg <t>)) && (is8BitInt(t) || is16BitInt(t)) && isSigned(t) -> x
-(MOVHZreg x:(Arg <t>)) && (is8BitInt(t) || is16BitInt(t)) && !isSigned(t) -> x
-(MOVWreg x:(Arg <t>)) && (is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && isSigned(t) -> x
-(MOVWZreg x:(Arg <t>)) && (is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && !isSigned(t) -> x
+(MOVBreg x:(Arg <t>)) && is8BitInt(t) && isSigned(t) -> (MOVDreg x)
+(MOVBZreg x:(Arg <t>)) && is8BitInt(t) && !isSigned(t) -> (MOVDreg x)
+(MOVHreg x:(Arg <t>)) && (is8BitInt(t) || is16BitInt(t)) && isSigned(t) -> (MOVDreg x)
+(MOVHZreg x:(Arg <t>)) && (is8BitInt(t) || is16BitInt(t)) && !isSigned(t) -> (MOVDreg x)
+(MOVWreg x:(Arg <t>)) && (is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && isSigned(t) -> (MOVDreg x)
+(MOVWZreg x:(Arg <t>)) && (is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && !isSigned(t) -> (MOVDreg x)
// fold double extensions
-(MOVBreg x:(MOVBreg _)) -> x
-(MOVBZreg x:(MOVBZreg _)) -> x
-(MOVHreg x:(MOVBreg _)) -> x
-(MOVHreg x:(MOVBZreg _)) -> x
-(MOVHreg x:(MOVHreg _)) -> x
-(MOVHZreg x:(MOVBZreg _)) -> x
-(MOVHZreg x:(MOVHZreg _)) -> x
-(MOVWreg x:(MOVBreg _)) -> x
-(MOVWreg x:(MOVBZreg _)) -> x
-(MOVWreg x:(MOVHreg _)) -> x
-(MOVWreg x:(MOVHreg _)) -> x
-(MOVWreg x:(MOVWreg _)) -> x
-(MOVWZreg x:(MOVBZreg _)) -> x
-(MOVWZreg x:(MOVHZreg _)) -> x
-(MOVWZreg x:(MOVWZreg _)) -> x
+(MOVBreg x:(MOVBreg _)) -> (MOVDreg x)
+(MOVBZreg x:(MOVBZreg _)) -> (MOVDreg x)
+(MOVHreg x:(MOVBreg _)) -> (MOVDreg x)
+(MOVHreg x:(MOVBZreg _)) -> (MOVDreg x)
+(MOVHreg x:(MOVHreg _)) -> (MOVDreg x)
+(MOVHZreg x:(MOVBZreg _)) -> (MOVDreg x)
+(MOVHZreg x:(MOVHZreg _)) -> (MOVDreg x)
+(MOVWreg x:(MOVBreg _)) -> (MOVDreg x)
+(MOVWreg x:(MOVBZreg _)) -> (MOVDreg x)
+(MOVWreg x:(MOVHreg _)) -> (MOVDreg x)
+(MOVWreg x:(MOVHreg _)) -> (MOVDreg x)
+(MOVWreg x:(MOVWreg _)) -> (MOVDreg x)
+(MOVWZreg x:(MOVBZreg _)) -> (MOVDreg x)
+(MOVWZreg x:(MOVHZreg _)) -> (MOVDreg x)
+(MOVWZreg x:(MOVWZreg _)) -> (MOVDreg x)
// fold extensions into constants
(MOVBreg (MOVDconst [c])) -> (MOVDconst [int64(int8(c))])
@@ -641,10 +648,10 @@
(MOVWZreg x:(MOVWZloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWZloadidx <v.Type> [off] {sym} ptr idx mem)
// replace load from same location as preceding store with copy
-(MOVBZload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
-(MOVHZload [off] {sym} ptr (MOVHstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
-(MOVWZload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
-(MOVDload [off] {sym} ptr (MOVDstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+(MOVBZload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVDreg x)
+(MOVHZload [off] {sym} ptr (MOVHstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVDreg x)
+(MOVWZload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVDreg x)
+(MOVDload [off] {sym} ptr (MOVDstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVDreg x)
// Don't extend before storing
(MOVWstore [off] {sym} ptr (MOVWreg x) mem) -> (MOVWstore [off] {sym} ptr x mem)
@@ -885,9 +892,9 @@
(MOVDEQ y _ (FlagLT)) -> y
(MOVDEQ y _ (FlagGT)) -> y
-(MOVDNE _ y (FlagEQ)) -> y
-(MOVDNE x _ (FlagLT)) -> x
-(MOVDNE x _ (FlagGT)) -> x
+(MOVDNE y _ (FlagEQ)) -> y
+(MOVDNE _ x (FlagLT)) -> x
+(MOVDNE _ x (FlagGT)) -> x
(MOVDLT y _ (FlagEQ)) -> y
(MOVDLT _ x (FlagLT)) -> x
diff --git a/src/cmd/compile/internal/ssa/gen/S390XOps.go b/src/cmd/compile/internal/ssa/gen/S390XOps.go
index 7a25c26784..29383f6c10 100644
--- a/src/cmd/compile/internal/ssa/gen/S390XOps.go
+++ b/src/cmd/compile/internal/ssa/gen/S390XOps.go
@@ -311,6 +311,9 @@ func init() {
{name: "MOVHZreg", argLength: 1, reg: gp11sp, asm: "MOVHZ", typ: "UInt64"}, // zero extend arg0 from int16 to int64
{name: "MOVWreg", argLength: 1, reg: gp11sp, asm: "MOVW", typ: "Int64"}, // sign extend arg0 from int32 to int64
{name: "MOVWZreg", argLength: 1, reg: gp11sp, asm: "MOVWZ", typ: "UInt64"}, // zero extend arg0 from int32 to int64
+ {name: "MOVDreg", argLength: 1, reg: gp11sp, asm: "MOVD"}, // move from arg0
+
+ {name: "MOVDnop", argLength: 1, reg: gp11, resultInArg0: true}, // nop, return arg0 in same register
{name: "MOVDconst", reg: gp01, asm: "MOVD", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint
diff --git a/src/cmd/compile/internal/ssa/loopreschedchecks.go b/src/cmd/compile/internal/ssa/loopreschedchecks.go
new file mode 100644
index 0000000000..8f8055e302
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/loopreschedchecks.go
@@ -0,0 +1,517 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ssa
+
+import "fmt"
+
+// an edgeMemCtr records a backedge, together with the memory and
+// counter phi functions at the target of the backedge that must
+// be updated when a rescheduling check replaces the backedge.
+type edgeMemCtr struct {
+ e Edge
+ m *Value // phi for memory at dest of e
+ c *Value // phi for counter at dest of e
+}
+
+// a rewriteTarget is a a value-argindex pair indicating
+// where a rewrite is applied. Note that this is for values,
+// not for block controls, because block controls are not targets
+// for the rewrites performed in inserting rescheduling checks.
+type rewriteTarget struct {
+ v *Value
+ i int
+}
+
+type rewrite struct {
+ before, after *Value // before is the expected value before rewrite, after is the new value installed.
+ rewrites []rewriteTarget // all the targets for this rewrite.
+}
+
+func (r *rewrite) String() string {
+ s := "\n\tbefore=" + r.before.String() + ", after=" + r.after.String()
+ for _, rw := range r.rewrites {
+ s += ", (i=" + fmt.Sprint(rw.i) + ", v=" + rw.v.LongString() + ")"
+ }
+ s += "\n"
+ return s
+}
+
+const initialRescheduleCounterValue = 1021 // Largest 10-bit prime. 97 nSec loop bodies will check every 100 uSec.
+
+// insertLoopReschedChecks inserts rescheduling checks on loop backedges.
+func insertLoopReschedChecks(f *Func) {
+ // TODO: when split information is recorded in export data, insert checks only on backedges that can be reached on a split-call-free path.
+
+ // Loop reschedule checks decrement a per-function counter
+ // shared by all loops, and when the counter becomes non-positive
+ // a call is made to a rescheduling check in the runtime.
+ //
+ // Steps:
+ // 1. locate backedges.
+ // 2. Record memory definitions at block end so that
+ // the SSA graph for mem can be prperly modified.
+ // 3. Define a counter and record its future uses (at backedges)
+ // (Same process as 2, applied to a single definition of the counter.
+ // difference for mem is that there are zero-to-many existing mem
+ // definitions, versus exactly one for the new counter.)
+ // 4. Ensure that phi functions that will-be-needed for mem and counter
+ // are present in the graph, initially with trivial inputs.
+ // 5. Record all to-be-modified uses of mem and counter;
+ // apply modifications (split into two steps to simplify and
+ // avoided nagging order-dependences).
+ // 6. Rewrite backedges to include counter check, reschedule check,
+ // and modify destination phi function appropriately with new
+ // definitions for mem and counter.
+
+ if f.NoSplit { // nosplit functions don't reschedule.
+ return
+ }
+
+ backedges := backedges(f)
+ if len(backedges) == 0 { // no backedges means no rescheduling checks.
+ return
+ }
+
+ lastMems := findLastMems(f)
+
+ idom := f.Idom()
+ sdom := f.sdom()
+
+ if f.pass.debug > 2 {
+ fmt.Printf("before %s = %s\n", f.Name, sdom.treestructure(f.Entry))
+ }
+
+ tofixBackedges := []edgeMemCtr{}
+
+ for _, e := range backedges { // TODO: could filter here by calls in loops, if declared and inferred nosplit are recorded in export data.
+ tofixBackedges = append(tofixBackedges, edgeMemCtr{e, nil, nil})
+ }
+
+ // It's possible that there is no memory state (no global/pointer loads/stores or calls)
+ if lastMems[f.Entry.ID] == nil {
+ lastMems[f.Entry.ID] = f.Entry.NewValue0(f.Entry.Line, OpInitMem, TypeMem)
+ }
+
+ memDefsAtBlockEnds := make([]*Value, f.NumBlocks()) // For each block, the mem def seen at its bottom. Could be from earlier block.
+
+ // Propagate last mem definitions forward through successor blocks.
+ po := f.postorder()
+ for i := len(po) - 1; i >= 0; i-- {
+ b := po[i]
+ mem := lastMems[b.ID]
+ for j := 0; mem == nil; j++ { // if there's no def, then there's no phi, so the visible mem is identical in all predecessors.
+ // loop because there might be backedges that haven't been visited yet.
+ mem = memDefsAtBlockEnds[b.Preds[j].b.ID]
+ }
+ memDefsAtBlockEnds[b.ID] = mem
+ }
+
+ // Set up counter. There are no phis etc pre-existing for it.
+ counter0 := f.Entry.NewValue0I(f.Entry.Line, OpConst32, f.Config.fe.TypeInt32(), initialRescheduleCounterValue)
+ ctrDefsAtBlockEnds := make([]*Value, f.NumBlocks()) // For each block, def visible at its end, if that def will be used.
+
+ // There's a minor difference between memDefsAtBlockEnds and ctrDefsAtBlockEnds;
+ // because the counter only matter for loops and code that reaches them, it is nil for blocks where the ctr is no
+ // longer live. This will avoid creation of dead phi functions. This optimization is ignored for the mem variable
+ // because it is harder and also less likely to be helpful, though dead code elimination ought to clean this out anyhow.
+
+ for _, emc := range tofixBackedges {
+ e := emc.e
+ // set initial uses of counter zero (note available-at-bottom and use are the same thing initially.)
+ // each back-edge will be rewritten to include a reschedule check, and that will use the counter.
+ src := e.b.Preds[e.i].b
+ ctrDefsAtBlockEnds[src.ID] = counter0
+ }
+
+ // Push uses towards root
+ for _, b := range f.postorder() {
+ bd := ctrDefsAtBlockEnds[b.ID]
+ if bd == nil {
+ continue
+ }
+ for _, e := range b.Preds {
+ p := e.b
+ if ctrDefsAtBlockEnds[p.ID] == nil {
+ ctrDefsAtBlockEnds[p.ID] = bd
+ }
+ }
+ }
+
+ // Maps from block to newly-inserted phi function in block.
+ newmemphis := make(map[*Block]rewrite)
+ newctrphis := make(map[*Block]rewrite)
+
+ // Insert phi functions as necessary for future changes to flow graph.
+ for i, emc := range tofixBackedges {
+ e := emc.e
+ h := e.b
+
+ // find the phi function for the memory input at "h", if there is one.
+ var headerMemPhi *Value // look for header mem phi
+
+ for _, v := range h.Values {
+ if v.Op == OpPhi && v.Type.IsMemory() {
+ headerMemPhi = v
+ }
+ }
+
+ if headerMemPhi == nil {
+ // if the header is nil, make a trivial phi from the dominator
+ mem0 := memDefsAtBlockEnds[idom[h.ID].ID]
+ headerMemPhi = newPhiFor(h, mem0)
+ newmemphis[h] = rewrite{before: mem0, after: headerMemPhi}
+ addDFphis(mem0, h, h, f, memDefsAtBlockEnds, newmemphis)
+
+ }
+ tofixBackedges[i].m = headerMemPhi
+
+ var headerCtrPhi *Value
+ rw, ok := newctrphis[h]
+ if !ok {
+ headerCtrPhi = newPhiFor(h, counter0)
+ newctrphis[h] = rewrite{before: counter0, after: headerCtrPhi}
+ addDFphis(counter0, h, h, f, ctrDefsAtBlockEnds, newctrphis)
+ } else {
+ headerCtrPhi = rw.after
+ }
+ tofixBackedges[i].c = headerCtrPhi
+ }
+
+ rewriteNewPhis(f.Entry, f.Entry, f, memDefsAtBlockEnds, newmemphis)
+ rewriteNewPhis(f.Entry, f.Entry, f, ctrDefsAtBlockEnds, newctrphis)
+
+ if f.pass.debug > 0 {
+ for b, r := range newmemphis {
+ fmt.Printf("b=%s, rewrite=%s\n", b, r.String())
+ }
+
+ for b, r := range newctrphis {
+ fmt.Printf("b=%s, rewrite=%s\n", b, r.String())
+ }
+ }
+
+ // Apply collected rewrites.
+ for _, r := range newmemphis {
+ for _, rw := range r.rewrites {
+ rw.v.SetArg(rw.i, r.after)
+ }
+ }
+
+ for _, r := range newctrphis {
+ for _, rw := range r.rewrites {
+ rw.v.SetArg(rw.i, r.after)
+ }
+ }
+
+ zero := f.Entry.NewValue0I(f.Entry.Line, OpConst32, f.Config.fe.TypeInt32(), 0)
+ one := f.Entry.NewValue0I(f.Entry.Line, OpConst32, f.Config.fe.TypeInt32(), 1)
+
+ // Rewrite backedges to include reschedule checks.
+ for _, emc := range tofixBackedges {
+ e := emc.e
+ headerMemPhi := emc.m
+ headerCtrPhi := emc.c
+ h := e.b
+ i := e.i
+ p := h.Preds[i]
+ bb := p.b
+ mem0 := headerMemPhi.Args[i]
+ ctr0 := headerCtrPhi.Args[i]
+ // bb e->p h,
+ // Because we're going to insert a rare-call, make sure the
+ // looping edge still looks likely.
+ likely := BranchLikely
+ if p.i != 0 {
+ likely = BranchUnlikely
+ }
+ bb.Likely = likely
+
+ // rewrite edge to include reschedule check
+ // existing edges:
+ //
+ // bb.Succs[p.i] == Edge{h, i}
+ // h.Preds[i] == p == Edge{bb,p.i}
+ //
+ // new block(s):
+ // test:
+ // ctr1 := ctr0 - 1
+ // if ctr1 <= 0 { goto sched }
+ // goto join
+ // sched:
+ // mem1 := call resched (mem0)
+ // goto join
+ // join:
+ // ctr2 := phi(ctr1, counter0) // counter0 is the constant
+ // mem2 := phi(mem0, mem1)
+ // goto h
+ //
+ // and correct arg i of headerMemPhi and headerCtrPhi
+ //
+ // EXCEPT: block containing only phi functions is bad
+ // for the register allocator. Therefore, there is no
+ // join, and instead branches targeting join instead target
+ // the header, and the other phi functions within header are
+ // adjusted for the additional input.
+
+ test := f.NewBlock(BlockIf)
+ sched := f.NewBlock(BlockPlain)
+
+ test.Line = bb.Line
+ sched.Line = bb.Line
+
+ // ctr1 := ctr0 - 1
+ // if ctr1 <= 0 { goto sched }
+ // goto header
+ ctr1 := test.NewValue2(bb.Line, OpSub32, f.Config.fe.TypeInt32(), ctr0, one)
+ cmp := test.NewValue2(bb.Line, OpLeq32, f.Config.fe.TypeBool(), ctr1, zero)
+ test.SetControl(cmp)
+ test.AddEdgeTo(sched) // if true
+ // if false -- rewrite edge to header.
+ // do NOT remove+add, because that will perturb all the other phi functions
+ // as well as messing up other edges to the header.
+ test.Succs = append(test.Succs, Edge{h, i})
+ h.Preds[i] = Edge{test, 1}
+ headerMemPhi.SetArg(i, mem0)
+ headerCtrPhi.SetArg(i, ctr1)
+
+ test.Likely = BranchUnlikely
+
+ // sched:
+ // mem1 := call resched (mem0)
+ // goto header
+ resched := f.Config.fe.Syslook("goschedguarded")
+ mem1 := sched.NewValue1A(bb.Line, OpStaticCall, TypeMem, resched, mem0)
+ sched.AddEdgeTo(h)
+ headerMemPhi.AddArg(mem1)
+ headerCtrPhi.AddArg(counter0)
+
+ bb.Succs[p.i] = Edge{test, 0}
+ test.Preds = append(test.Preds, Edge{bb, p.i})
+
+ // Must correct all the other phi functions in the header for new incoming edge.
+ // Except for mem and counter phis, it will be the same value seen on the original
+ // backedge at index i.
+ for _, v := range h.Values {
+ if v.Op == OpPhi && v != headerMemPhi && v != headerCtrPhi {
+ v.AddArg(v.Args[i])
+ }
+ }
+ }
+
+ f.invalidateCFG()
+
+ if f.pass.debug > 2 {
+ sdom = newSparseTree(f, f.Idom())
+ fmt.Printf("after %s = %s\n", f.Name, sdom.treestructure(f.Entry))
+ }
+
+ return
+}
+
+// newPhiFor inserts a new Phi function into b,
+// with all inputs set to v.
+func newPhiFor(b *Block, v *Value) *Value {
+ phiV := b.NewValue0(b.Line, OpPhi, v.Type)
+
+ for range b.Preds {
+ phiV.AddArg(v)
+ }
+ return phiV
+}
+
+// rewriteNewPhis updates newphis[h] to record all places where the new phi function inserted
+// in block h will replace a previous definition. Block b is the block currently being processed;
+// if b has its own phi definition then it takes the place of h.
+// defsForUses provides information about other definitions of the variable that are present
+// (and if nil, indicates that the variable is no longer live)
+func rewriteNewPhis(h, b *Block, f *Func, defsForUses []*Value, newphis map[*Block]rewrite) {
+ // If b is a block with a new phi, then a new rewrite applies below it in the dominator tree.
+ if _, ok := newphis[b]; ok {
+ h = b
+ }
+ change := newphis[h]
+ x := change.before
+ y := change.after
+
+ // Apply rewrites to this block
+ if x != nil { // don't waste time on the common case of no definition.
+ p := &change.rewrites
+ for _, v := range b.Values {
+ if v == y { // don't rewrite self -- phi inputs are handled below.
+ continue
+ }
+ for i, w := range v.Args {
+ if w != x {
+ continue
+ }
+ *p = append(*p, rewriteTarget{v, i})
+ }
+ }
+
+ // Rewrite appropriate inputs of phis reached in successors
+ // in dominance frontier, self, and dominated.
+ // If the variable def reaching uses in b is itself defined in b, then the new phi function
+ // does not reach the successors of b. (This assumes a bit about the structure of the
+ // phi use-def graph, but it's true for memory and the inserted counter.)
+ if dfu := defsForUses[b.ID]; dfu != nil && dfu.Block != b {
+ for _, e := range b.Succs {
+ s := e.b
+ if sphi, ok := newphis[s]; ok { // saves time to find the phi this way.
+ *p = append(*p, rewriteTarget{sphi.after, e.i})
+ continue
+ }
+ for _, v := range s.Values {
+ if v.Op == OpPhi && v.Args[e.i] == x {
+ *p = append(*p, rewriteTarget{v, e.i})
+ break
+ }
+ }
+ }
+ }
+ newphis[h] = change
+ }
+
+ sdom := f.sdom()
+
+ for c := sdom[b.ID].child; c != nil; c = sdom[c.ID].sibling {
+ rewriteNewPhis(h, c, f, defsForUses, newphis) // TODO: convert to explicit stack from recursion.
+ }
+}
+
+// addDFphis creates new trivial phis that are necessary to correctly reflect (within SSA)
+// a new definition for variable "x" inserted at h (usually but not necessarily a phi).
+// These new phis can only occur at the dominance frontier of h; block s is in the dominance
+// frontier of h if h does not strictly dominate s and if s is a successor of a block b where
+// either b = h or h strictly dominates b.
+// These newly created phis are themselves new definitions that may require addition of their
+// own trivial phi functions in their own dominance frontier, and this is handled recursively.
+func addDFphis(x *Value, h, b *Block, f *Func, defForUses []*Value, newphis map[*Block]rewrite) {
+ oldv := defForUses[b.ID]
+ if oldv != x { // either a new definition replacing x, or nil if it is proven that there are no uses reachable from b
+ return
+ }
+ sdom := f.sdom()
+ idom := f.Idom()
+outer:
+ for _, e := range b.Succs {
+ s := e.b
+ // check phi functions in the dominance frontier
+ if sdom.isAncestor(h, s) {
+ continue // h dominates s, successor of b, therefore s is not in the frontier.
+ }
+ if _, ok := newphis[s]; ok {
+ continue // successor s of b already has a new phi function, so there is no need to add another.
+ }
+ if x != nil {
+ for _, v := range s.Values {
+ if v.Op == OpPhi && v.Args[e.i] == x {
+ continue outer // successor s of b has an old phi function, so there is no need to add another.
+ }
+ }
+ }
+
+ old := defForUses[idom[s.ID].ID] // new phi function is correct-but-redundant, combining value "old" on all inputs.
+ headerPhi := newPhiFor(s, old)
+ // the new phi will replace "old" in block s and all blocks dominated by s.
+ newphis[s] = rewrite{before: old, after: headerPhi} // record new phi, to have inputs labeled "old" rewritten to "headerPhi"
+ addDFphis(old, s, s, f, defForUses, newphis) // the new definition may also create new phi functions.
+ }
+ for c := sdom[b.ID].child; c != nil; c = sdom[c.ID].sibling {
+ addDFphis(x, h, c, f, defForUses, newphis) // TODO: convert to explicit stack from recursion.
+ }
+}
+
+// findLastMems maps block ids to last memory-output op in a block, if any
+func findLastMems(f *Func) []*Value {
+
+ var stores []*Value
+ lastMems := make([]*Value, f.NumBlocks())
+ storeUse := f.newSparseSet(f.NumValues())
+ defer f.retSparseSet(storeUse)
+ for _, b := range f.Blocks {
+ // Find all the stores in this block. Categorize their uses:
+ // storeUse contains stores which are used by a subsequent store.
+ storeUse.clear()
+ stores = stores[:0]
+ var memPhi *Value
+ for _, v := range b.Values {
+ if v.Op == OpPhi {
+ if v.Type.IsMemory() {
+ memPhi = v
+ }
+ continue
+ }
+ if v.Type.IsMemory() {
+ stores = append(stores, v)
+ if v.Op == OpSelect1 {
+ // Use the arg of the tuple-generating op.
+ v = v.Args[0]
+ }
+ for _, a := range v.Args {
+ if a.Block == b && a.Type.IsMemory() {
+ storeUse.add(a.ID)
+ }
+ }
+ }
+ }
+ if len(stores) == 0 {
+ lastMems[b.ID] = memPhi
+ continue
+ }
+
+ // find last store in the block
+ var last *Value
+ for _, v := range stores {
+ if storeUse.contains(v.ID) {
+ continue
+ }
+ if last != nil {
+ b.Fatalf("two final stores - simultaneous live stores %s %s", last, v)
+ }
+ last = v
+ }
+ if last == nil {
+ b.Fatalf("no last store found - cycle?")
+ }
+ lastMems[b.ID] = last
+ }
+ return lastMems
+}
+
+type backedgesState struct {
+ b *Block
+ i int
+}
+
+// backedges returns a slice of successor edges that are back
+// edges. For reducible loops, edge.b is the header.
+func backedges(f *Func) []Edge {
+ edges := []Edge{}
+ mark := make([]markKind, f.NumBlocks())
+ stack := []backedgesState{}
+
+ mark[f.Entry.ID] = notExplored
+ stack = append(stack, backedgesState{f.Entry, 0})
+
+ for len(stack) > 0 {
+ l := len(stack)
+ x := stack[l-1]
+ if x.i < len(x.b.Succs) {
+ e := x.b.Succs[x.i]
+ stack[l-1].i++
+ s := e.b
+ if mark[s.ID] == notFound {
+ mark[s.ID] = notExplored
+ stack = append(stack, backedgesState{s, 0})
+ } else if mark[s.ID] == notExplored {
+ edges = append(edges, e)
+ }
+ } else {
+ mark[x.b.ID] = done
+ stack = stack[0 : l-1]
+ }
+ }
+ return edges
+}
diff --git a/src/cmd/compile/internal/ssa/nilcheck.go b/src/cmd/compile/internal/ssa/nilcheck.go
index 9f58db664b..0a34cd1ae6 100644
--- a/src/cmd/compile/internal/ssa/nilcheck.go
+++ b/src/cmd/compile/internal/ssa/nilcheck.go
@@ -82,7 +82,7 @@ func nilcheckelim(f *Func) {
}
}
- // Next, process values in the block.
+ // Next, eliminate any redundant nil checks in this block.
i := 0
for _, v := range b.Values {
b.Values[i] = v
@@ -105,13 +105,10 @@ func nilcheckelim(f *Func) {
f.Config.Warnl(v.Line, "removed nil check")
}
v.reset(OpUnknown)
+ // TODO: f.freeValue(v)
i--
continue
}
- // Record the fact that we know ptr is non nil, and remember to
- // undo that information when this dominator subtree is done.
- nonNilValues[ptr.ID] = true
- work = append(work, bp{op: ClearPtr, ptr: ptr})
}
}
for j := i; j < len(b.Values); j++ {
@@ -119,6 +116,21 @@ func nilcheckelim(f *Func) {
}
b.Values = b.Values[:i]
+ // Finally, find redundant nil checks for subsequent blocks.
+ // Note that we can't add these until the loop above is done, as the
+ // values in the block are not ordered in any way when this pass runs.
+ // This was the cause of issue #18725.
+ for _, v := range b.Values {
+ if v.Op != OpNilCheck {
+ continue
+ }
+ ptr := v.Args[0]
+ // Record the fact that we know ptr is non nil, and remember to
+ // undo that information when this dominator subtree is done.
+ nonNilValues[ptr.ID] = true
+ work = append(work, bp{op: ClearPtr, ptr: ptr})
+ }
+
// Add all dominated blocks to the work list.
for w := sdom[node.block.ID].child; w != nil; w = sdom[w.ID].sibling {
work = append(work, bp{op: Work, block: w})
diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go
index a63c5b9953..9d11d03793 100644
--- a/src/cmd/compile/internal/ssa/opGen.go
+++ b/src/cmd/compile/internal/ssa/opGen.go
@@ -1473,6 +1473,8 @@ const (
OpS390XMOVHZreg
OpS390XMOVWreg
OpS390XMOVWZreg
+ OpS390XMOVDreg
+ OpS390XMOVDnop
OpS390XMOVDconst
OpS390XCFDBRA
OpS390XCGDBRA
@@ -18571,6 +18573,32 @@ var opcodeTable = [...]opInfo{
},
},
{
+ name: "MOVDreg",
+ argLen: 1,
+ asm: s390x.AMOVD,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+ },
+ outputs: []outputInfo{
+ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+ },
+ },
+ },
+ {
+ name: "MOVDnop",
+ argLen: 1,
+ resultInArg0: true,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+ },
+ outputs: []outputInfo{
+ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+ },
+ },
+ },
+ {
name: "MOVDconst",
auxType: auxInt64,
argLen: 0,
diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go
index 8c8373b8aa..031459c1ff 100644
--- a/src/cmd/compile/internal/ssa/rewritePPC64.go
+++ b/src/cmd/compile/internal/ssa/rewritePPC64.go
@@ -1543,12 +1543,12 @@ func rewriteValuePPC64_OpGeq16U(v *Value, config *Config) bool {
_ = b
// match: (Geq16U x y)
// cond:
- // result: (GreaterEqual (CMPU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+ // result: (GreaterEqual (CMPWU (ZeroExt16to32 x) (ZeroExt16to32 y)))
for {
x := v.Args[0]
y := v.Args[1]
v.reset(OpPPC64GreaterEqual)
- v0 := b.NewValue0(v.Line, OpPPC64CMPU, TypeFlags)
+ v0 := b.NewValue0(v.Line, OpPPC64CMPWU, TypeFlags)
v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
v1.AddArg(x)
v0.AddArg(v1)
@@ -1598,12 +1598,12 @@ func rewriteValuePPC64_OpGeq32U(v *Value, config *Config) bool {
_ = b
// match: (Geq32U x y)
// cond:
- // result: (GreaterEqual (CMPU x y))
+ // result: (GreaterEqual (CMPWU x y))
for {
x := v.Args[0]
y := v.Args[1]
v.reset(OpPPC64GreaterEqual)
- v0 := b.NewValue0(v.Line, OpPPC64CMPU, TypeFlags)
+ v0 := b.NewValue0(v.Line, OpPPC64CMPWU, TypeFlags)
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
@@ -1687,12 +1687,12 @@ func rewriteValuePPC64_OpGeq8U(v *Value, config *Config) bool {
_ = b
// match: (Geq8U x y)
// cond:
- // result: (GreaterEqual (CMPU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+ // result: (GreaterEqual (CMPWU (ZeroExt8to32 x) (ZeroExt8to32 y)))
for {
x := v.Args[0]
y := v.Args[1]
v.reset(OpPPC64GreaterEqual)
- v0 := b.NewValue0(v.Line, OpPPC64CMPU, TypeFlags)
+ v0 := b.NewValue0(v.Line, OpPPC64CMPWU, TypeFlags)
v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
v1.AddArg(x)
v0.AddArg(v1)
diff --git a/src/cmd/compile/internal/ssa/rewriteS390X.go b/src/cmd/compile/internal/ssa/rewriteS390X.go
index 7d023bcf8b..0425ced330 100644
--- a/src/cmd/compile/internal/ssa/rewriteS390X.go
+++ b/src/cmd/compile/internal/ssa/rewriteS390X.go
@@ -524,6 +524,8 @@ func rewriteValueS390X(v *Value, config *Config) bool {
return rewriteValueS390X_OpS390XMOVDload(v, config)
case OpS390XMOVDloadidx:
return rewriteValueS390X_OpS390XMOVDloadidx(v, config)
+ case OpS390XMOVDreg:
+ return rewriteValueS390X_OpS390XMOVDreg(v, config)
case OpS390XMOVDstore:
return rewriteValueS390X_OpS390XMOVDstore(v, config)
case OpS390XMOVDstoreconst:
@@ -3236,13 +3238,28 @@ func rewriteValueS390X_OpLoad(v *Value, config *Config) bool {
return true
}
// match: (Load <t> ptr mem)
- // cond: is32BitInt(t)
+ // cond: is32BitInt(t) && isSigned(t)
+ // result: (MOVWload ptr mem)
+ for {
+ t := v.Type
+ ptr := v.Args[0]
+ mem := v.Args[1]
+ if !(is32BitInt(t) && isSigned(t)) {
+ break
+ }
+ v.reset(OpS390XMOVWload)
+ v.AddArg(ptr)
+ v.AddArg(mem)
+ return true
+ }
+ // match: (Load <t> ptr mem)
+ // cond: is32BitInt(t) && !isSigned(t)
// result: (MOVWZload ptr mem)
for {
t := v.Type
ptr := v.Args[0]
mem := v.Args[1]
- if !(is32BitInt(t)) {
+ if !(is32BitInt(t) && !isSigned(t)) {
break
}
v.reset(OpS390XMOVWZload)
@@ -3251,13 +3268,28 @@ func rewriteValueS390X_OpLoad(v *Value, config *Config) bool {
return true
}
// match: (Load <t> ptr mem)
- // cond: is16BitInt(t)
+ // cond: is16BitInt(t) && isSigned(t)
+ // result: (MOVHload ptr mem)
+ for {
+ t := v.Type
+ ptr := v.Args[0]
+ mem := v.Args[1]
+ if !(is16BitInt(t) && isSigned(t)) {
+ break
+ }
+ v.reset(OpS390XMOVHload)
+ v.AddArg(ptr)
+ v.AddArg(mem)
+ return true
+ }
+ // match: (Load <t> ptr mem)
+ // cond: is16BitInt(t) && !isSigned(t)
// result: (MOVHZload ptr mem)
for {
t := v.Type
ptr := v.Args[0]
mem := v.Args[1]
- if !(is16BitInt(t)) {
+ if !(is16BitInt(t) && !isSigned(t)) {
break
}
v.reset(OpS390XMOVHZload)
@@ -3266,13 +3298,28 @@ func rewriteValueS390X_OpLoad(v *Value, config *Config) bool {
return true
}
// match: (Load <t> ptr mem)
- // cond: (t.IsBoolean() || is8BitInt(t))
+ // cond: is8BitInt(t) && isSigned(t)
+ // result: (MOVBload ptr mem)
+ for {
+ t := v.Type
+ ptr := v.Args[0]
+ mem := v.Args[1]
+ if !(is8BitInt(t) && isSigned(t)) {
+ break
+ }
+ v.reset(OpS390XMOVBload)
+ v.AddArg(ptr)
+ v.AddArg(mem)
+ return true
+ }
+ // match: (Load <t> ptr mem)
+ // cond: (t.IsBoolean() || (is8BitInt(t) && !isSigned(t)))
// result: (MOVBZload ptr mem)
for {
t := v.Type
ptr := v.Args[0]
mem := v.Args[1]
- if !(t.IsBoolean() || is8BitInt(t)) {
+ if !(t.IsBoolean() || (is8BitInt(t) && !isSigned(t))) {
break
}
v.reset(OpS390XMOVBZload)
@@ -7802,7 +7849,7 @@ func rewriteValueS390X_OpS390XMOVBZload(v *Value, config *Config) bool {
_ = b
// match: (MOVBZload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _))
// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
- // result: x
+ // result: (MOVDreg x)
for {
off := v.AuxInt
sym := v.Aux
@@ -7818,8 +7865,7 @@ func rewriteValueS390X_OpS390XMOVBZload(v *Value, config *Config) bool {
if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
@@ -7976,7 +8022,7 @@ func rewriteValueS390X_OpS390XMOVBZreg(v *Value, config *Config) bool {
_ = b
// match: (MOVBZreg x:(MOVDLT (MOVDconst [c]) (MOVDconst [d]) _))
// cond: int64(uint8(c)) == c && int64(uint8(d)) == d
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVDLT {
@@ -7995,14 +8041,13 @@ func rewriteValueS390X_OpS390XMOVBZreg(v *Value, config *Config) bool {
if !(int64(uint8(c)) == c && int64(uint8(d)) == d) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVBZreg x:(MOVDLE (MOVDconst [c]) (MOVDconst [d]) _))
// cond: int64(uint8(c)) == c && int64(uint8(d)) == d
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVDLE {
@@ -8021,14 +8066,13 @@ func rewriteValueS390X_OpS390XMOVBZreg(v *Value, config *Config) bool {
if !(int64(uint8(c)) == c && int64(uint8(d)) == d) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVBZreg x:(MOVDGT (MOVDconst [c]) (MOVDconst [d]) _))
// cond: int64(uint8(c)) == c && int64(uint8(d)) == d
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVDGT {
@@ -8047,14 +8091,13 @@ func rewriteValueS390X_OpS390XMOVBZreg(v *Value, config *Config) bool {
if !(int64(uint8(c)) == c && int64(uint8(d)) == d) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVBZreg x:(MOVDGE (MOVDconst [c]) (MOVDconst [d]) _))
// cond: int64(uint8(c)) == c && int64(uint8(d)) == d
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVDGE {
@@ -8073,14 +8116,13 @@ func rewriteValueS390X_OpS390XMOVBZreg(v *Value, config *Config) bool {
if !(int64(uint8(c)) == c && int64(uint8(d)) == d) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVBZreg x:(MOVDEQ (MOVDconst [c]) (MOVDconst [d]) _))
// cond: int64(uint8(c)) == c && int64(uint8(d)) == d
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVDEQ {
@@ -8099,14 +8141,13 @@ func rewriteValueS390X_OpS390XMOVBZreg(v *Value, config *Config) bool {
if !(int64(uint8(c)) == c && int64(uint8(d)) == d) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVBZreg x:(MOVDNE (MOVDconst [c]) (MOVDconst [d]) _))
// cond: int64(uint8(c)) == c && int64(uint8(d)) == d
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVDNE {
@@ -8125,14 +8166,13 @@ func rewriteValueS390X_OpS390XMOVBZreg(v *Value, config *Config) bool {
if !(int64(uint8(c)) == c && int64(uint8(d)) == d) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVBZreg x:(MOVDGTnoinv (MOVDconst [c]) (MOVDconst [d]) _))
// cond: int64(uint8(c)) == c && int64(uint8(d)) == d
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVDGTnoinv {
@@ -8151,14 +8191,13 @@ func rewriteValueS390X_OpS390XMOVBZreg(v *Value, config *Config) bool {
if !(int64(uint8(c)) == c && int64(uint8(d)) == d) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVBZreg x:(MOVDGEnoinv (MOVDconst [c]) (MOVDconst [d]) _))
// cond: int64(uint8(c)) == c && int64(uint8(d)) == d
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVDGEnoinv {
@@ -8177,27 +8216,25 @@ func rewriteValueS390X_OpS390XMOVBZreg(v *Value, config *Config) bool {
if !(int64(uint8(c)) == c && int64(uint8(d)) == d) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVBZreg x:(MOVBZload _ _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVBZload {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVBZreg x:(Arg <t>))
// cond: is8BitInt(t) && !isSigned(t)
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpArg {
@@ -8207,21 +8244,19 @@ func rewriteValueS390X_OpS390XMOVBZreg(v *Value, config *Config) bool {
if !(is8BitInt(t) && !isSigned(t)) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVBZreg x:(MOVBZreg _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVBZreg {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
@@ -8349,20 +8384,19 @@ func rewriteValueS390X_OpS390XMOVBreg(v *Value, config *Config) bool {
_ = b
// match: (MOVBreg x:(MOVBload _ _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVBload {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVBreg x:(Arg <t>))
// cond: is8BitInt(t) && isSigned(t)
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpArg {
@@ -8372,21 +8406,19 @@ func rewriteValueS390X_OpS390XMOVBreg(v *Value, config *Config) bool {
if !(is8BitInt(t) && isSigned(t)) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVBreg x:(MOVBreg _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVBreg {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
@@ -9847,11 +9879,11 @@ func rewriteValueS390X_OpS390XMOVDNE(v *Value, config *Config) bool {
v.AddArg(cmp)
return true
}
- // match: (MOVDNE _ y (FlagEQ))
+ // match: (MOVDNE y _ (FlagEQ))
// cond:
// result: y
for {
- y := v.Args[1]
+ y := v.Args[0]
v_2 := v.Args[2]
if v_2.Op != OpS390XFlagEQ {
break
@@ -9861,11 +9893,11 @@ func rewriteValueS390X_OpS390XMOVDNE(v *Value, config *Config) bool {
v.AddArg(y)
return true
}
- // match: (MOVDNE x _ (FlagLT))
+ // match: (MOVDNE _ x (FlagLT))
// cond:
// result: x
for {
- x := v.Args[0]
+ x := v.Args[1]
v_2 := v.Args[2]
if v_2.Op != OpS390XFlagLT {
break
@@ -9875,11 +9907,11 @@ func rewriteValueS390X_OpS390XMOVDNE(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
- // match: (MOVDNE x _ (FlagGT))
+ // match: (MOVDNE _ x (FlagGT))
// cond:
// result: x
for {
- x := v.Args[0]
+ x := v.Args[1]
v_2 := v.Args[2]
if v_2.Op != OpS390XFlagGT {
break
@@ -9995,7 +10027,7 @@ func rewriteValueS390X_OpS390XMOVDload(v *Value, config *Config) bool {
_ = b
// match: (MOVDload [off] {sym} ptr (MOVDstore [off2] {sym2} ptr2 x _))
// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
- // result: x
+ // result: (MOVDreg x)
for {
off := v.AuxInt
sym := v.Aux
@@ -10011,8 +10043,7 @@ func rewriteValueS390X_OpS390XMOVDload(v *Value, config *Config) bool {
if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
@@ -10164,6 +10195,23 @@ func rewriteValueS390X_OpS390XMOVDloadidx(v *Value, config *Config) bool {
}
return false
}
+func rewriteValueS390X_OpS390XMOVDreg(v *Value, config *Config) bool {
+ b := v.Block
+ _ = b
+ // match: (MOVDreg x)
+ // cond: x.Uses == 1
+ // result: (MOVDnop x)
+ for {
+ x := v.Args[0]
+ if !(x.Uses == 1) {
+ break
+ }
+ v.reset(OpS390XMOVDnop)
+ v.AddArg(x)
+ return true
+ }
+ return false
+}
func rewriteValueS390X_OpS390XMOVDstore(v *Value, config *Config) bool {
b := v.Block
_ = b
@@ -10912,7 +10960,7 @@ func rewriteValueS390X_OpS390XMOVHZload(v *Value, config *Config) bool {
_ = b
// match: (MOVHZload [off] {sym} ptr (MOVHstore [off2] {sym2} ptr2 x _))
// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
- // result: x
+ // result: (MOVDreg x)
for {
off := v.AuxInt
sym := v.Aux
@@ -10928,8 +10976,7 @@ func rewriteValueS390X_OpS390XMOVHZload(v *Value, config *Config) bool {
if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
@@ -11086,33 +11133,31 @@ func rewriteValueS390X_OpS390XMOVHZreg(v *Value, config *Config) bool {
_ = b
// match: (MOVHZreg x:(MOVBZload _ _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVBZload {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVHZreg x:(MOVHZload _ _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVHZload {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVHZreg x:(Arg <t>))
// cond: (is8BitInt(t) || is16BitInt(t)) && !isSigned(t)
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpArg {
@@ -11122,34 +11167,31 @@ func rewriteValueS390X_OpS390XMOVHZreg(v *Value, config *Config) bool {
if !((is8BitInt(t) || is16BitInt(t)) && !isSigned(t)) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVHZreg x:(MOVBZreg _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVBZreg {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVHZreg x:(MOVHZreg _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVHZreg {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
@@ -11277,46 +11319,43 @@ func rewriteValueS390X_OpS390XMOVHreg(v *Value, config *Config) bool {
_ = b
// match: (MOVHreg x:(MOVBload _ _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVBload {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVHreg x:(MOVBZload _ _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVBZload {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVHreg x:(MOVHload _ _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVHload {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVHreg x:(Arg <t>))
// cond: (is8BitInt(t) || is16BitInt(t)) && isSigned(t)
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpArg {
@@ -11326,47 +11365,43 @@ func rewriteValueS390X_OpS390XMOVHreg(v *Value, config *Config) bool {
if !((is8BitInt(t) || is16BitInt(t)) && isSigned(t)) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVHreg x:(MOVBreg _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVBreg {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVHreg x:(MOVBZreg _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVBZreg {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVHreg x:(MOVHreg _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVHreg {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
@@ -12310,7 +12345,7 @@ func rewriteValueS390X_OpS390XMOVWZload(v *Value, config *Config) bool {
_ = b
// match: (MOVWZload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _))
// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
- // result: x
+ // result: (MOVDreg x)
for {
off := v.AuxInt
sym := v.Aux
@@ -12326,8 +12361,7 @@ func rewriteValueS390X_OpS390XMOVWZload(v *Value, config *Config) bool {
if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
@@ -12484,46 +12518,43 @@ func rewriteValueS390X_OpS390XMOVWZreg(v *Value, config *Config) bool {
_ = b
// match: (MOVWZreg x:(MOVBZload _ _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVBZload {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVWZreg x:(MOVHZload _ _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVHZload {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVWZreg x:(MOVWZload _ _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVWZload {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVWZreg x:(Arg <t>))
// cond: (is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && !isSigned(t)
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpArg {
@@ -12533,47 +12564,43 @@ func rewriteValueS390X_OpS390XMOVWZreg(v *Value, config *Config) bool {
if !((is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && !isSigned(t)) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVWZreg x:(MOVBZreg _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVBZreg {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVWZreg x:(MOVHZreg _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVHZreg {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVWZreg x:(MOVWZreg _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVWZreg {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
@@ -12701,72 +12728,67 @@ func rewriteValueS390X_OpS390XMOVWreg(v *Value, config *Config) bool {
_ = b
// match: (MOVWreg x:(MOVBload _ _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVBload {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVWreg x:(MOVBZload _ _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVBZload {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVWreg x:(MOVHload _ _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVHload {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVWreg x:(MOVHZload _ _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVHZload {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVWreg x:(MOVWload _ _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVWload {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVWreg x:(Arg <t>))
// cond: (is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && isSigned(t)
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpArg {
@@ -12776,73 +12798,67 @@ func rewriteValueS390X_OpS390XMOVWreg(v *Value, config *Config) bool {
if !((is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && isSigned(t)) {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVWreg x:(MOVBreg _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVBreg {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVWreg x:(MOVBZreg _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVBZreg {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVWreg x:(MOVHreg _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVHreg {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVWreg x:(MOVHreg _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVHreg {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
// match: (MOVWreg x:(MOVWreg _))
// cond:
- // result: x
+ // result: (MOVDreg x)
for {
x := v.Args[0]
if x.Op != OpS390XMOVWreg {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpS390XMOVDreg)
v.AddArg(x)
return true
}
diff --git a/src/cmd/compile/internal/ssa/sparsetree.go b/src/cmd/compile/internal/ssa/sparsetree.go
index 7c82a60d0f..8e5b9f3e5b 100644
--- a/src/cmd/compile/internal/ssa/sparsetree.go
+++ b/src/cmd/compile/internal/ssa/sparsetree.go
@@ -4,7 +4,10 @@
package ssa
-import "fmt"
+import (
+ "fmt"
+ "strings"
+)
type SparseTreeNode struct {
child *Block
@@ -67,6 +70,34 @@ func newSparseTree(f *Func, parentOf []*Block) SparseTree {
return t
}
+// treestructure provides a string description of the dominator
+// tree and flow structure of block b and all blocks that it
+// dominates.
+func (t SparseTree) treestructure(b *Block) string {
+ return t.treestructure1(b, 0)
+}
+func (t SparseTree) treestructure1(b *Block, i int) string {
+ s := "\n" + strings.Repeat("\t", i) + b.String() + "->["
+ for i, e := range b.Succs {
+ if i > 0 {
+ s = s + ","
+ }
+ s = s + e.b.String()
+ }
+ s += "]"
+ if c0 := t[b.ID].child; c0 != nil {
+ s += "("
+ for c := c0; c != nil; c = t[c.ID].sibling {
+ if c != c0 {
+ s += " "
+ }
+ s += t.treestructure1(c, i+1)
+ }
+ s += ")"
+ }
+ return s
+}
+
// numberBlock assigns entry and exit numbers for b and b's
// children in an in-order walk from a gappy sequence, where n
// is the first number not yet assigned or reserved. N should
diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go
index 1eb4d7bb1a..054ba1f85c 100644
--- a/src/cmd/compile/internal/ssa/writebarrier.go
+++ b/src/cmd/compile/internal/ssa/writebarrier.go
@@ -35,7 +35,7 @@ func writebarrier(f *Func) {
valueLoop:
for i, v := range b.Values {
switch v.Op {
- case OpStoreWB, OpMoveWB, OpMoveWBVolatile:
+ case OpStoreWB, OpMoveWB, OpMoveWBVolatile, OpZeroWB:
if IsStackAddr(v.Args[0]) {
switch v.Op {
case OpStoreWB:
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index 7d5f79f339..c51dcead2b 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -15,7 +15,6 @@ import (
"os/exec"
"path/filepath"
"regexp"
- "runtime"
"strconv"
"strings"
"sync"
@@ -354,7 +353,7 @@ func (t *tester) registerTests() {
// This test needs its stdout/stderr to be terminals, so we don't run it from cmd/go's tests.
// See issue 18153.
- if runtime.GOOS == "linux" {
+ if t.goos == "linux" {
t.tests = append(t.tests, distTest{
name: "cmd_go_test_terminal",
heading: "cmd/go terminal test",
@@ -568,7 +567,7 @@ func (t *tester) registerTests() {
if t.gohostos == "linux" && t.goarch == "amd64" {
t.registerTest("testasan", "../misc/cgo/testasan", "go", "run", "main.go")
}
- if t.gohostos == "linux" && t.goarch == "amd64" {
+ if t.goos == "linux" && t.goarch == "amd64" {
t.registerTest("testsanitizers", "../misc/cgo/testsanitizers", "./test.bash")
}
if t.hasBash() && t.goos != "android" && !t.iOS() && t.gohostos != "windows" {
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index e93fd6ebed..3d5dd2b397 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -17,7 +17,7 @@
// clean remove object files
// doc show documentation for package or symbol
// env print Go environment information
-// bug print information for bug reports
+// bug start a bug report
// fix run go tool fix on packages
// fmt run gofmt on package sources
// generate generate Go files by processing source
@@ -324,15 +324,14 @@
// each named variable on its own line.
//
//
-// Print information for bug reports
+// Start a bug report
//
// Usage:
//
// go bug
//
-// Bug prints information that helps file effective bug reports.
-//
-// Bugs may be reported at https://golang.org/issue/new.
+// Bug opens the default browser and starts a new bug report.
+// The report includes useful system information.
//
//
// Run go tool fix on packages
diff --git a/src/cmd/go/bug.go b/src/cmd/go/bug.go
index cbd258b80b..658f6dabd9 100644
--- a/src/cmd/go/bug.go
+++ b/src/cmd/go/bug.go
@@ -20,11 +20,10 @@ import (
var cmdBug = &Command{
Run: runBug,
UsageLine: "bug",
- Short: "print information for bug reports",
+ Short: "start a bug report",
Long: `
-Bug prints information that helps file effective bug reports.
-
-Bugs may be reported at https://golang.org/issue/new.
+Bug opens the default browser and starts a new bug report.
+The report includes useful system information.
`,
}
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index 5727eb094e..ef5348bba4 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -3744,6 +3744,13 @@ func TestMatchesOnlySubtestParallelIsOK(t *testing.T) {
tg.grepBoth(okPattern, "go test did not say ok")
}
+// Issue 18845
+func TestBenchTimeout(t *testing.T) {
+ tg := testgo(t)
+ defer tg.cleanup()
+ tg.run("test", "-bench", ".", "-timeout", "750ms", "testdata/timeoutbench_test.go")
+}
+
func TestLinkXImportPathEscape(t *testing.T) {
// golang.org/issue/16710
tg := testgo(t)
@@ -3787,3 +3794,26 @@ GLOBL ·constants<>(SB),8,$8
tg.setenv("GOPATH", tg.path("go"))
tg.run("build", "p")
}
+
+// Issue 18778.
+func TestDotDotDotOutsideGOPATH(t *testing.T) {
+ tg := testgo(t)
+ defer tg.cleanup()
+
+ tg.tempFile("pkgs/a.go", `package x`)
+ tg.tempFile("pkgs/a_test.go", `package x_test
+import "testing"
+func TestX(t *testing.T) {}`)
+
+ tg.tempFile("pkgs/a/a.go", `package a`)
+ tg.tempFile("pkgs/a/a_test.go", `package a_test
+import "testing"
+func TestA(t *testing.T) {}`)
+
+ tg.cd(tg.path("pkgs"))
+ tg.run("build", "./...")
+ tg.run("test", "./...")
+ tg.run("list", "./...")
+ tg.grepStdout("pkgs$", "expected package not listed")
+ tg.grepStdout("pkgs/a", "expected package not listed")
+}
diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index d69fa5118f..e40f9420c7 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -429,7 +429,7 @@ func setErrorPos(p *Package, importPos []token.Position) *Package {
func cleanImport(path string) string {
orig := path
path = pathpkg.Clean(path)
- if strings.HasPrefix(orig, "./") && path != ".." && path != "." && !strings.HasPrefix(path, "../") {
+ if strings.HasPrefix(orig, "./") && path != ".." && !strings.HasPrefix(path, "../") {
path = "./" + path
}
return path
diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go
index cdb167de75..6482f0fd32 100644
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -894,9 +894,13 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
if buildContext.GOOS == "darwin" {
if buildContext.GOARCH == "arm" || buildContext.GOARCH == "arm64" {
- t.NeedCgo = true
+ t.IsIOS = true
+ t.NeedOS = true
}
}
+ if t.TestMain == nil {
+ t.NeedOS = true
+ }
for _, cp := range pmain.imports {
if len(cp.coverVars) > 0 {
@@ -1343,7 +1347,8 @@ type testFuncs struct {
NeedTest bool
ImportXtest bool
NeedXtest bool
- NeedCgo bool
+ NeedOS bool
+ IsIOS bool
Cover []coverInfo
}
@@ -1444,7 +1449,7 @@ var testmainTmpl = template.Must(template.New("main").Parse(`
package main
import (
-{{if not .TestMain}}
+{{if .NeedOS}}
"os"
{{end}}
"testing"
@@ -1460,8 +1465,10 @@ import (
_cover{{$i}} {{$p.Package.ImportPath | printf "%q"}}
{{end}}
-{{if .NeedCgo}}
+{{if .IsIOS}}
+ "os/signal"
_ "runtime/cgo"
+ "syscall"
{{end}}
)
@@ -1523,6 +1530,32 @@ func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts
{{end}}
func main() {
+{{if .IsIOS}}
+ // Send a SIGUSR2, which will be intercepted by LLDB to
+ // tell the test harness that installation was successful.
+ // See misc/ios/go_darwin_arm_exec.go.
+ signal.Notify(make(chan os.Signal), syscall.SIGUSR2)
+ syscall.Kill(0, syscall.SIGUSR2)
+ signal.Reset(syscall.SIGUSR2)
+
+ // The first argument supplied to an iOS test is an offset
+ // suffix for the current working directory.
+ // Process it here, and remove it from os.Args.
+ const hdr = "cwdSuffix="
+ if len(os.Args) < 2 || len(os.Args[1]) <= len(hdr) || os.Args[1][:len(hdr)] != hdr {
+ panic("iOS test not passed a working directory suffix")
+ }
+ suffix := os.Args[1][len(hdr):]
+ dir, err := os.Getwd()
+ if err != nil {
+ panic(err)
+ }
+ if err := os.Chdir(dir + "/" + suffix); err != nil {
+ panic(err)
+ }
+ os.Args = append([]string{os.Args[0]}, os.Args[2:]...)
+{{end}}
+
{{if .CoverEnabled}}
testing.RegisterCover(testing.Cover{
Mode: {{printf "%q" .CoverMode}},
diff --git a/src/cmd/go/testdata/timeoutbench_test.go b/src/cmd/go/testdata/timeoutbench_test.go
new file mode 100644
index 0000000000..57a8888299
--- /dev/null
+++ b/src/cmd/go/testdata/timeoutbench_test.go
@@ -0,0 +1,10 @@
+package timeoutbench_test
+
+import (
+ "testing"
+ "time"
+)
+
+func BenchmarkSleep1s(b *testing.B) {
+ time.Sleep(1 * time.Second)
+}
diff --git a/src/cmd/internal/obj/go.go b/src/cmd/internal/obj/go.go
index 1852dc74f6..732ce19634 100644
--- a/src/cmd/internal/obj/go.go
+++ b/src/cmd/internal/obj/go.go
@@ -13,8 +13,9 @@ import (
// go-specific code shared across loaders (5l, 6l, 8l).
var (
- framepointer_enabled int
- Fieldtrack_enabled int
+ framepointer_enabled int
+ Fieldtrack_enabled int
+ Preemptibleloops_enabled int
)
// Toolchain experiments.
@@ -27,6 +28,7 @@ var exper = []struct {
}{
{"fieldtrack", &Fieldtrack_enabled},
{"framepointer", &framepointer_enabled},
+ {"preemptibleloops", &Preemptibleloops_enabled},
}
func addexp(s string) {
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index 61d3e4fb72..22d2c548c3 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -1080,7 +1080,7 @@ func writelines(ctxt *Link, syms []*Symbol) ([]*Symbol, []*Symbol) {
epcs = s
dsym := ctxt.Syms.Lookup(dwarf.InfoPrefix+s.Name, int(s.Version))
- dsym.Attr |= AttrHidden
+ dsym.Attr |= AttrHidden | AttrReachable
dsym.Type = obj.SDWARFINFO
for _, r := range dsym.R {
if r.Type == obj.R_DWARFREF && r.Sym.Size == 0 {
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index 1ebd7de662..479425f211 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -168,7 +168,7 @@ func container(s *Symbol) int {
if s == nil {
return 0
}
- if Buildmode == BuildmodePlugin && onlycsymbol(s) {
+ if Buildmode == BuildmodePlugin && Headtype == obj.Hdarwin && onlycsymbol(s) {
return 1
}
// We want to generate func table entries only for the "lowest level" symbols,
diff --git a/src/compress/bzip2/bzip2_test.go b/src/compress/bzip2/bzip2_test.go
index a6c3080db3..95fb189585 100644
--- a/src/compress/bzip2/bzip2_test.go
+++ b/src/compress/bzip2/bzip2_test.go
@@ -204,12 +204,6 @@ func TestMTF(t *testing.T) {
}
}
-var (
- digits = mustLoadFile("testdata/e.txt.bz2")
- twain = mustLoadFile("testdata/Mark.Twain-Tom.Sawyer.txt.bz2")
- random = mustLoadFile("testdata/random.data.bz2")
-)
-
func benchmarkDecode(b *testing.B, compressed []byte) {
// Determine the uncompressed size of testfile.
uncompressedSize, err := io.Copy(ioutil.Discard, NewReader(bytes.NewReader(compressed)))
@@ -227,6 +221,18 @@ func benchmarkDecode(b *testing.B, compressed []byte) {
}
}
-func BenchmarkDecodeDigits(b *testing.B) { benchmarkDecode(b, digits) }
-func BenchmarkDecodeTwain(b *testing.B) { benchmarkDecode(b, twain) }
-func BenchmarkDecodeRand(b *testing.B) { benchmarkDecode(b, random) }
+func BenchmarkDecodeDigits(b *testing.B) {
+ digits := mustLoadFile("testdata/e.txt.bz2")
+ b.ResetTimer()
+ benchmarkDecode(b, digits)
+}
+func BenchmarkDecodeTwain(b *testing.B) {
+ twain := mustLoadFile("testdata/Mark.Twain-Tom.Sawyer.txt.bz2")
+ b.ResetTimer()
+ benchmarkDecode(b, twain)
+}
+func BenchmarkDecodeRand(b *testing.B) {
+ random := mustLoadFile("testdata/random.data.bz2")
+ b.ResetTimer()
+ benchmarkDecode(b, random)
+}
diff --git a/src/compress/flate/deflate.go b/src/compress/flate/deflate.go
index 97265b3ca2..4d6a5357d8 100644
--- a/src/compress/flate/deflate.go
+++ b/src/compress/flate/deflate.go
@@ -136,14 +136,17 @@ func (d *compressor) fillDeflate(b []byte) int {
delta := d.hashOffset - 1
d.hashOffset -= delta
d.chainHead -= delta
- for i, v := range d.hashPrev {
+
+ // Iterate over slices instead of arrays to avoid copying
+ // the entire table onto the stack (Issue #18625).
+ for i, v := range d.hashPrev[:] {
if int(v) > delta {
d.hashPrev[i] = uint32(int(v) - delta)
} else {
d.hashPrev[i] = 0
}
}
- for i, v := range d.hashHead {
+ for i, v := range d.hashHead[:] {
if int(v) > delta {
d.hashHead[i] = uint32(int(v) - delta)
} else {
diff --git a/src/compress/flate/deflate_test.go b/src/compress/flate/deflate_test.go
index 521a260365..fbea761721 100644
--- a/src/compress/flate/deflate_test.go
+++ b/src/compress/flate/deflate_test.go
@@ -12,6 +12,7 @@ import (
"io"
"io/ioutil"
"reflect"
+ "runtime/debug"
"sync"
"testing"
)
@@ -864,3 +865,33 @@ func TestBestSpeedMaxMatchOffset(t *testing.T) {
}
}
}
+
+func TestMaxStackSize(t *testing.T) {
+ // This test must not run in parallel with other tests as debug.SetMaxStack
+ // affects all goroutines.
+ n := debug.SetMaxStack(1 << 16)
+ defer debug.SetMaxStack(n)
+
+ var wg sync.WaitGroup
+ defer wg.Wait()
+
+ b := make([]byte, 1<<20)
+ for level := HuffmanOnly; level <= BestCompression; level++ {
+ // Run in separate goroutine to increase probability of stack regrowth.
+ wg.Add(1)
+ go func(level int) {
+ defer wg.Done()
+ zw, err := NewWriter(ioutil.Discard, level)
+ if err != nil {
+ t.Errorf("level %d, NewWriter() = %v, want nil", level, err)
+ }
+ if n, err := zw.Write(b); n != len(b) || err != nil {
+ t.Errorf("level %d, Write() = (%d, %v), want (%d, nil)", level, n, err, len(b))
+ }
+ if err := zw.Close(); err != nil {
+ t.Errorf("level %d, Close() = %v, want nil", level, err)
+ }
+ zw.Reset(ioutil.Discard)
+ }(level)
+ }
+}
diff --git a/src/compress/flate/deflatefast.go b/src/compress/flate/deflatefast.go
index a1636a37d6..08298b76bb 100644
--- a/src/compress/flate/deflatefast.go
+++ b/src/compress/flate/deflatefast.go
@@ -60,7 +60,7 @@ func newDeflateFast() *deflateFast {
func (e *deflateFast) encode(dst []token, src []byte) []token {
// Ensure that e.cur doesn't wrap.
if e.cur > 1<<30 {
- *e = deflateFast{cur: maxStoreBlockSize, prev: e.prev[:0]}
+ e.resetAll()
}
// This check isn't in the Snappy implementation, but there, the caller
@@ -265,6 +265,21 @@ func (e *deflateFast) reset() {
// Protect against e.cur wraparound.
if e.cur > 1<<30 {
- *e = deflateFast{cur: maxStoreBlockSize, prev: e.prev[:0]}
+ e.resetAll()
+ }
+}
+
+// resetAll resets the deflateFast struct and is only called in rare
+// situations to prevent integer overflow. It manually resets each field
+// to avoid causing large stack growth.
+//
+// See https://golang.org/issue/18636.
+func (e *deflateFast) resetAll() {
+ // This is equivalent to:
+ // *e = deflateFast{cur: maxStoreBlockSize, prev: e.prev[:0]}
+ e.cur = maxStoreBlockSize
+ e.prev = e.prev[:0]
+ for i := range e.table {
+ e.table[i] = tableEntry{}
}
}
diff --git a/src/compress/gzip/issue14937_test.go b/src/compress/gzip/issue14937_test.go
index e76d47cc4e..30c1390dfd 100644
--- a/src/compress/gzip/issue14937_test.go
+++ b/src/compress/gzip/issue14937_test.go
@@ -9,11 +9,17 @@ import (
"testing"
)
-// Per golang.org/issue/14937, check that every .gz file
-// in the tree has a zero mtime.
+// TestGZIPFilesHaveZeroMTimes checks that every .gz file in the tree
+// has a zero MTIME. This is a requirement for the Debian maintainers
+// to be able to have deterministic packages.
+//
+// See https://golang.org/issue/14937.
func TestGZIPFilesHaveZeroMTimes(t *testing.T) {
- if testing.Short() && testenv.Builder() == "" {
- t.Skip("skipping in short mode")
+ // To avoid spurious false positives due to untracked GZIP files that
+ // may be in the user's GOROOT (Issue 18604), we only run this test on
+ // the builders, which should have a clean checkout of the tree.
+ if testenv.Builder() == "" {
+ t.Skip("skipping test on non-builder")
}
goroot, err := filepath.EvalSymlinks(runtime.GOROOT())
if err != nil {
diff --git a/src/crypto/dsa/dsa_test.go b/src/crypto/dsa/dsa_test.go
index b89aeaebea..8600059f03 100644
--- a/src/crypto/dsa/dsa_test.go
+++ b/src/crypto/dsa/dsa_test.go
@@ -95,7 +95,7 @@ func TestSignAndVerify(t *testing.T) {
func TestSigningWithDegenerateKeys(t *testing.T) {
// Signing with degenerate private keys should not cause an infinite
// loop.
- badKeys := []struct{
+ badKeys := []struct {
p, q, g, y, x string
}{
{"00", "01", "00", "00", "00"},
@@ -105,7 +105,7 @@ func TestSigningWithDegenerateKeys(t *testing.T) {
for i, test := range badKeys {
priv := PrivateKey{
PublicKey: PublicKey{
- Parameters: Parameters {
+ Parameters: Parameters{
P: fromHex(test.p),
Q: fromHex(test.q),
G: fromHex(test.g),
diff --git a/src/crypto/tls/cipher_suites.go b/src/crypto/tls/cipher_suites.go
index 05146743ac..beb0f1926d 100644
--- a/src/crypto/tls/cipher_suites.go
+++ b/src/crypto/tls/cipher_suites.go
@@ -84,15 +84,15 @@ var cipherSuites = []*cipherSuite{
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM},
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
- {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12, cipherAES, macSHA256, nil},
+ {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteDefaultOff, cipherAES, macSHA256, nil},
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
- {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, cipherAES, macSHA256, nil},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteDefaultOff, cipherAES, macSHA256, nil},
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
{TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM},
{TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
- {TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12, cipherAES, macSHA256, nil},
+ {TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12 | suiteDefaultOff, cipherAES, macSHA256, nil},
{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
{TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
diff --git a/src/crypto/tls/tls.go b/src/crypto/tls/tls.go
index f2e5aea2bc..615d1e5576 100644
--- a/src/crypto/tls/tls.go
+++ b/src/crypto/tls/tls.go
@@ -6,8 +6,8 @@
package tls
// BUG(agl): The crypto/tls package only implements some countermeasures
-// against Lucky13 attacks on CBC-mode encryption. See
-// http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and
+// against Lucky13 attacks on CBC-mode encryption, and only on SHA1
+// variants. See http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and
// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
import (
diff --git a/src/crypto/x509/cert_pool.go b/src/crypto/x509/cert_pool.go
index fea33df379..71ffbdf0e0 100644
--- a/src/crypto/x509/cert_pool.go
+++ b/src/crypto/x509/cert_pool.go
@@ -4,7 +4,11 @@
package x509
-import "encoding/pem"
+import (
+ "encoding/pem"
+ "errors"
+ "runtime"
+)
// CertPool is a set of certificates.
type CertPool struct {
@@ -26,6 +30,11 @@ func NewCertPool() *CertPool {
// Any mutations to the returned pool are not written to disk and do
// not affect any other pool.
func SystemCertPool() (*CertPool, error) {
+ if runtime.GOOS == "windows" {
+ // Issue 16736, 18609:
+ return nil, errors.New("crypto/x509: system root pool is not available on Windows")
+ }
+
return loadSystemRoots()
}
diff --git a/src/crypto/x509/root_windows.go b/src/crypto/x509/root_windows.go
index ca2fba5cb4..a936fec7d8 100644
--- a/src/crypto/x509/root_windows.go
+++ b/src/crypto/x509/root_windows.go
@@ -226,6 +226,11 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
}
func loadSystemRoots() (*CertPool, error) {
+ // TODO: restore this functionality on Windows. We tried to do
+ // it in Go 1.8 but had to revert it. See Issue 18609.
+ // Returning (nil, nil) was the old behavior, prior to CL 30578.
+ return nil, nil
+
const CRYPT_E_NOT_FOUND = 0x80092004
store, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("ROOT"))
diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go
index aa30d85b7d..b085dad90f 100644
--- a/src/crypto/x509/x509_test.go
+++ b/src/crypto/x509/x509_test.go
@@ -24,6 +24,7 @@ import (
"net"
"os/exec"
"reflect"
+ "runtime"
"strings"
"testing"
"time"
@@ -1477,6 +1478,9 @@ func TestMultipleRDN(t *testing.T) {
}
func TestSystemCertPool(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("not implemented on Windows; Issue 16736, 18609")
+ }
_, err := SystemCertPool()
if err != nil {
t.Fatal(err)
diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go
index 0fa7c34a13..feb91223a9 100644
--- a/src/database/sql/sql.go
+++ b/src/database/sql/sql.go
@@ -1357,16 +1357,7 @@ func (db *DB) begin(ctx context.Context, opts *TxOptions, strategy connReuseStra
cancel: cancel,
ctx: ctx,
}
- go func(tx *Tx) {
- select {
- case <-tx.ctx.Done():
- if !tx.isDone() {
- // Discard and close the connection used to ensure the transaction
- // is closed and the resources are released.
- tx.rollback(true)
- }
- }
- }(tx)
+ go tx.awaitDone()
return tx, nil
}
@@ -1388,6 +1379,11 @@ func (db *DB) Driver() driver.Driver {
type Tx struct {
db *DB
+ // closemu prevents the transaction from closing while there
+ // is an active query. It is held for read during queries
+ // and exclusively during close.
+ closemu sync.RWMutex
+
// dc is owned exclusively until Commit or Rollback, at which point
// it's returned with putConn.
dc *driverConn
@@ -1413,6 +1409,20 @@ type Tx struct {
ctx context.Context
}
+// awaitDone blocks until the context in Tx is canceled and rolls back
+// the transaction if it's not already done.
+func (tx *Tx) awaitDone() {
+ // Wait for either the transaction to be committed or rolled
+ // back, or for the associated context to be closed.
+ <-tx.ctx.Done()
+
+ // Discard and close the connection used to ensure the
+ // transaction is closed and the resources are released. This
+ // rollback does nothing if the transaction has already been
+ // committed or rolled back.
+ tx.rollback(true)
+}
+
func (tx *Tx) isDone() bool {
return atomic.LoadInt32(&tx.done) != 0
}
@@ -1424,16 +1434,31 @@ var ErrTxDone = errors.New("sql: Transaction has already been committed or rolle
// close returns the connection to the pool and
// must only be called by Tx.rollback or Tx.Commit.
func (tx *Tx) close(err error) {
+ tx.closemu.Lock()
+ defer tx.closemu.Unlock()
+
tx.db.putConn(tx.dc, err)
tx.cancel()
tx.dc = nil
tx.txi = nil
}
+// hookTxGrabConn specifies an optional hook to be called on
+// a successful call to (*Tx).grabConn. For tests.
+var hookTxGrabConn func()
+
func (tx *Tx) grabConn(ctx context.Context) (*driverConn, error) {
+ select {
+ default:
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ }
if tx.isDone() {
return nil, ErrTxDone
}
+ if hookTxGrabConn != nil { // test hook
+ hookTxGrabConn()
+ }
return tx.dc, nil
}
@@ -1503,6 +1528,9 @@ func (tx *Tx) Rollback() error {
// for the execution of the returned statement. The returned statement
// will run in the transaction context.
func (tx *Tx) PrepareContext(ctx context.Context, query string) (*Stmt, error) {
+ tx.closemu.RLock()
+ defer tx.closemu.RUnlock()
+
// TODO(bradfitz): We could be more efficient here and either
// provide a method to take an existing Stmt (created on
// perhaps a different Conn), and re-create it on this Conn if
@@ -1567,6 +1595,9 @@ func (tx *Tx) Prepare(query string) (*Stmt, error) {
// The returned statement operates within the transaction and will be closed
// when the transaction has been committed or rolled back.
func (tx *Tx) StmtContext(ctx context.Context, stmt *Stmt) *Stmt {
+ tx.closemu.RLock()
+ defer tx.closemu.RUnlock()
+
// TODO(bradfitz): optimize this. Currently this re-prepares
// each time. This is fine for now to illustrate the API but
// we should really cache already-prepared statements
@@ -1618,6 +1649,9 @@ func (tx *Tx) Stmt(stmt *Stmt) *Stmt {
// ExecContext executes a query that doesn't return rows.
// For example: an INSERT and UPDATE.
func (tx *Tx) ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error) {
+ tx.closemu.RLock()
+ defer tx.closemu.RUnlock()
+
dc, err := tx.grabConn(ctx)
if err != nil {
return nil, err
@@ -1661,6 +1695,9 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
// QueryContext executes a query that returns rows, typically a SELECT.
func (tx *Tx) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) {
+ tx.closemu.RLock()
+ defer tx.closemu.RUnlock()
+
dc, err := tx.grabConn(ctx)
if err != nil {
return nil, err
@@ -2038,25 +2075,21 @@ type Rows struct {
// closed value is 1 when the Rows is closed.
// Use atomic operations on value when checking value.
closed int32
- ctxClose chan struct{} // closed when Rows is closed, may be null.
+ cancel func() // called when Rows is closed, may be nil.
lastcols []driver.Value
lasterr error // non-nil only if closed is true
closeStmt *driverStmt // if non-nil, statement to Close on close
}
func (rs *Rows) initContextClose(ctx context.Context) {
- if ctx.Done() == context.Background().Done() {
- return
- }
+ ctx, rs.cancel = context.WithCancel(ctx)
+ go rs.awaitDone(ctx)
+}
- rs.ctxClose = make(chan struct{})
- go func() {
- select {
- case <-ctx.Done():
- rs.Close()
- case <-rs.ctxClose:
- }
- }()
+// awaitDone blocks until the rows are closed or the context canceled.
+func (rs *Rows) awaitDone(ctx context.Context) {
+ <-ctx.Done()
+ rs.Close()
}
// Next prepares the next result row for reading with the Scan method. It
@@ -2314,7 +2347,9 @@ func (rs *Rows) Scan(dest ...interface{}) error {
return nil
}
-var rowsCloseHook func(*Rows, *error)
+// rowsCloseHook returns a function so tests may install the
+// hook throug a test only mutex.
+var rowsCloseHook = func() func(*Rows, *error) { return nil }
func (rs *Rows) isClosed() bool {
return atomic.LoadInt32(&rs.closed) != 0
@@ -2328,13 +2363,15 @@ func (rs *Rows) Close() error {
if !atomic.CompareAndSwapInt32(&rs.closed, 0, 1) {
return nil
}
- if rs.ctxClose != nil {
- close(rs.ctxClose)
- }
+
err := rs.rowsi.Close()
- if fn := rowsCloseHook; fn != nil {
+ if fn := rowsCloseHook(); fn != nil {
fn(rs, &err)
}
+ if rs.cancel != nil {
+ rs.cancel()
+ }
+
if rs.closeStmt != nil {
rs.closeStmt.Close()
}
diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go
index 63e1292cb1..898df3b455 100644
--- a/src/database/sql/sql_test.go
+++ b/src/database/sql/sql_test.go
@@ -14,6 +14,7 @@ import (
"runtime"
"strings"
"sync"
+ "sync/atomic"
"testing"
"time"
)
@@ -326,9 +327,7 @@ func TestQueryContext(t *testing.T) {
// And verify that the final rows.Next() call, which hit EOF,
// also closed the rows connection.
- if n := db.numFreeConns(); n != 1 {
- t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
- }
+ waitForFree(t, db, 5*time.Second, 1)
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
t.Errorf("executed %d Prepare statements; want 1", prepares)
}
@@ -345,6 +344,18 @@ func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool {
return false
}
+// waitForFree checks db.numFreeConns until either it equals want or
+// the maxWait time elapses.
+func waitForFree(t *testing.T, db *DB, maxWait time.Duration, want int) {
+ var numFree int
+ if !waitCondition(maxWait, 5*time.Millisecond, func() bool {
+ numFree = db.numFreeConns()
+ return numFree == want
+ }) {
+ t.Fatalf("free conns after hitting EOF = %d; want %d", numFree, want)
+ }
+}
+
func TestQueryContextWait(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
@@ -361,9 +372,7 @@ func TestQueryContextWait(t *testing.T) {
}
// Verify closed rows connection after error condition.
- if n := db.numFreeConns(); n != 1 {
- t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
- }
+ waitForFree(t, db, 5*time.Second, 1)
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
t.Errorf("executed %d Prepare statements; want 1", prepares)
}
@@ -388,13 +397,7 @@ func TestTxContextWait(t *testing.T) {
t.Fatalf("expected QueryContext to error with context deadline exceeded but returned %v", err)
}
- var numFree int
- if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
- numFree = db.numFreeConns()
- return numFree == 0
- }) {
- t.Fatalf("free conns after hitting EOF = %d; want 0", numFree)
- }
+ waitForFree(t, db, 5*time.Second, 0)
// Ensure the dropped connection allows more connections to be made.
// Checked on DB Close.
@@ -471,9 +474,7 @@ func TestMultiResultSetQuery(t *testing.T) {
// And verify that the final rows.Next() call, which hit EOF,
// also closed the rows connection.
- if n := db.numFreeConns(); n != 1 {
- t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
- }
+ waitForFree(t, db, 5*time.Second, 1)
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
t.Errorf("executed %d Prepare statements; want 1", prepares)
}
@@ -1135,6 +1136,24 @@ func TestQueryRowClosingStmt(t *testing.T) {
}
}
+var atomicRowsCloseHook atomic.Value // of func(*Rows, *error)
+
+func init() {
+ rowsCloseHook = func() func(*Rows, *error) {
+ fn, _ := atomicRowsCloseHook.Load().(func(*Rows, *error))
+ return fn
+ }
+}
+
+func setRowsCloseHook(fn func(*Rows, *error)) {
+ if fn == nil {
+ // Can't change an atomic.Value back to nil, so set it to this
+ // no-op func instead.
+ fn = func(*Rows, *error) {}
+ }
+ atomicRowsCloseHook.Store(fn)
+}
+
// Test issue 6651
func TestIssue6651(t *testing.T) {
db := newTestDB(t, "people")
@@ -1147,6 +1166,7 @@ func TestIssue6651(t *testing.T) {
return fmt.Errorf(want)
}
defer func() { rowsCursorNextHook = nil }()
+
err := db.QueryRow("SELECT|people|name|").Scan(&v)
if err == nil || err.Error() != want {
t.Errorf("error = %q; want %q", err, want)
@@ -1154,10 +1174,10 @@ func TestIssue6651(t *testing.T) {
rowsCursorNextHook = nil
want = "error in rows.Close"
- rowsCloseHook = func(rows *Rows, err *error) {
+ setRowsCloseHook(func(rows *Rows, err *error) {
*err = fmt.Errorf(want)
- }
- defer func() { rowsCloseHook = nil }()
+ })
+ defer setRowsCloseHook(nil)
err = db.QueryRow("SELECT|people|name|").Scan(&v)
if err == nil || err.Error() != want {
t.Errorf("error = %q; want %q", err, want)
@@ -1830,7 +1850,9 @@ func TestStmtCloseDeps(t *testing.T) {
db.dumpDeps(t)
}
- if len(stmt.css) > nquery {
+ if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
+ return len(stmt.css) <= nquery
+ }) {
t.Errorf("len(stmt.css) = %d; want <= %d", len(stmt.css), nquery)
}
@@ -2576,10 +2598,10 @@ func TestIssue6081(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- rowsCloseHook = func(rows *Rows, err *error) {
+ setRowsCloseHook(func(rows *Rows, err *error) {
*err = driver.ErrBadConn
- }
- defer func() { rowsCloseHook = nil }()
+ })
+ defer setRowsCloseHook(nil)
for i := 0; i < 10; i++ {
rows, err := stmt.Query()
if err != nil {
@@ -2642,7 +2664,10 @@ func TestIssue18429(t *testing.T) {
if err != nil {
return
}
- rows, err := tx.QueryContext(ctx, "WAIT|"+qwait+"|SELECT|people|name|")
+ // This is expected to give a cancel error many, but not all the time.
+ // Test failure will happen with a panic or other race condition being
+ // reported.
+ rows, _ := tx.QueryContext(ctx, "WAIT|"+qwait+"|SELECT|people|name|")
if rows != nil {
rows.Close()
}
@@ -2655,6 +2680,56 @@ func TestIssue18429(t *testing.T) {
time.Sleep(milliWait * 3 * time.Millisecond)
}
+// TestIssue18719 closes the context right before use. The sql.driverConn
+// will nil out the ci on close in a lock, but if another process uses it right after
+// it will panic with on the nil ref.
+//
+// See https://golang.org/cl/35550 .
+func TestIssue18719(t *testing.T) {
+ db := newTestDB(t, "people")
+ defer closeDB(t, db)
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ tx, err := db.BeginTx(ctx, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ hookTxGrabConn = func() {
+ cancel()
+
+ // Wait for the context to cancel and tx to rollback.
+ for tx.isDone() == false {
+ time.Sleep(time.Millisecond * 3)
+ }
+ }
+ defer func() { hookTxGrabConn = nil }()
+
+ // This call will grab the connection and cancel the context
+ // after it has done so. Code after must deal with the canceled state.
+ rows, err := tx.QueryContext(ctx, "SELECT|people|name|")
+ if err != nil {
+ rows.Close()
+ t.Fatalf("expected error %v but got %v", nil, err)
+ }
+
+ // Rows may be ignored because it will be closed when the context is canceled.
+
+ // Do not explicitly rollback. The rollback will happen from the
+ // canceled context.
+
+ // Wait for connections to return to pool.
+ var numOpen int
+ if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
+ numOpen = db.numOpenConns()
+ return numOpen == 0
+ }) {
+ t.Fatalf("open conns after hitting EOF = %d; want 0", numOpen)
+ }
+}
+
func TestConcurrency(t *testing.T) {
doConcurrentTest(t, new(concurrentDBQueryTest))
doConcurrentTest(t, new(concurrentDBExecTest))
diff --git a/src/go/ast/scope.go b/src/go/ast/scope.go
index 1ce5e2e84b..a400c7152a 100644
--- a/src/go/ast/scope.go
+++ b/src/go/ast/scope.go
@@ -70,10 +70,8 @@ func (s *Scope) String() string {
// The Data fields contains object-specific data:
//
// Kind Data type Data value
-// Pkg *types.Package package scope
+// Pkg *Scope package scope
// Con int iota for the respective declaration
-// Con != nil constant value
-// Typ *Scope (used as method scope during type checking - transient)
//
type Object struct {
Kind ObjKind
diff --git a/src/go/doc/doc_test.go b/src/go/doc/doc_test.go
index ad8ba5378f..82e63100d4 100644
--- a/src/go/doc/doc_test.go
+++ b/src/go/doc/doc_test.go
@@ -25,7 +25,7 @@ var files = flag.String("files", "", "consider only Go test files matching this
const dataDir = "testdata"
-var templateTxt = readTemplate("template.txt")
+var templateTxt *template.Template
func readTemplate(filename string) *template.Template {
t := template.New(filename)
@@ -96,6 +96,9 @@ func test(t *testing.T, mode Mode) {
if err != nil {
t.Fatal(err)
}
+ if templateTxt == nil {
+ templateTxt = readTemplate("template.txt")
+ }
// test packages
for _, pkg := range pkgs {
diff --git a/src/go/parser/performance_test.go b/src/go/parser/performance_test.go
index f2732c0e2b..b2e1c11e9d 100644
--- a/src/go/parser/performance_test.go
+++ b/src/go/parser/performance_test.go
@@ -10,17 +10,12 @@ import (
"testing"
)
-var src = readFile("parser.go")
-
-func readFile(filename string) []byte {
- data, err := ioutil.ReadFile(filename)
+func BenchmarkParse(b *testing.B) {
+ src, err := ioutil.ReadFile("parser.go")
if err != nil {
- panic(err)
+ b.Fatal(err)
}
- return data
-}
-
-func BenchmarkParse(b *testing.B) {
+ b.ResetTimer()
b.SetBytes(int64(len(src)))
for i := 0; i < b.N; i++ {
if _, err := ParseFile(token.NewFileSet(), "", src, ParseComments); err != nil {
diff --git a/src/go/printer/nodes.go b/src/go/printer/nodes.go
index 5a408cd571..08b8711c2d 100644
--- a/src/go/printer/nodes.go
+++ b/src/go/printer/nodes.go
@@ -733,7 +733,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
case *ast.FuncLit:
p.expr(x.Type)
- p.adjBlock(p.distanceFrom(x.Type.Pos()), blank, x.Body)
+ p.funcBody(p.distanceFrom(x.Type.Pos()), blank, x.Body)
case *ast.ParenExpr:
if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
@@ -825,6 +825,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
if x.Type != nil {
p.expr1(x.Type, token.HighestPrec, depth)
}
+ p.level++
p.print(x.Lbrace, token.LBRACE)
p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace)
// do not insert extra line break following a /*-style comment
@@ -837,6 +838,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
mode |= noExtraBlank
}
p.print(mode, x.Rbrace, token.RBRACE, mode)
+ p.level--
case *ast.Ellipsis:
p.print(token.ELLIPSIS)
@@ -1560,18 +1562,23 @@ func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int {
return bodySize
}
-// adjBlock prints an "adjacent" block (e.g., a for-loop or function body) following
-// a header (e.g., a for-loop control clause or function signature) of given headerSize.
+// funcBody prints a function body following a function header of given headerSize.
// If the header's and block's size are "small enough" and the block is "simple enough",
// the block is printed on the current line, without line breaks, spaced from the header
// by sep. Otherwise the block's opening "{" is printed on the current line, followed by
// lines for the block's statements and its closing "}".
//
-func (p *printer) adjBlock(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
+func (p *printer) funcBody(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
if b == nil {
return
}
+ // save/restore composite literal nesting level
+ defer func(level int) {
+ p.level = level
+ }(p.level)
+ p.level = 0
+
const maxSize = 100
if headerSize+p.bodySize(b, maxSize) <= maxSize {
p.print(sep, b.Lbrace, token.LBRACE)
@@ -1616,7 +1623,7 @@ func (p *printer) funcDecl(d *ast.FuncDecl) {
}
p.expr(d.Name)
p.signature(d.Type.Params, d.Type.Results)
- p.adjBlock(p.distanceFrom(d.Pos()), vtab, d.Body)
+ p.funcBody(p.distanceFrom(d.Pos()), vtab, d.Body)
}
func (p *printer) decl(decl ast.Decl) {
diff --git a/src/go/printer/printer.go b/src/go/printer/printer.go
index eabf23e8b2..be61dad590 100644
--- a/src/go/printer/printer.go
+++ b/src/go/printer/printer.go
@@ -58,6 +58,7 @@ type printer struct {
// Current state
output []byte // raw printer result
indent int // current indentation
+ level int // level == 0: outside composite literal; level > 0: inside composite literal
mode pmode // current printer mode
impliedSemi bool // if set, a linebreak implies a semicolon
lastTok token.Token // last token printed (token.ILLEGAL if it's whitespace)
@@ -744,15 +745,19 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro
// follows on the same line but is not a comma, and not a "closing"
// token immediately following its corresponding "opening" token,
// add an extra separator unless explicitly disabled. Use a blank
- // as separator unless we have pending linebreaks and they are not
- // disabled, in which case we want a linebreak (issue 15137).
+ // as separator unless we have pending linebreaks, they are not
+ // disabled, and we are outside a composite literal, in which case
+ // we want a linebreak (issue 15137).
+ // TODO(gri) This has become overly complicated. We should be able
+ // to track whether we're inside an expression or statement and
+ // use that information to decide more directly.
needsLinebreak := false
if p.mode&noExtraBlank == 0 &&
last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line &&
tok != token.COMMA &&
(tok != token.RPAREN || p.prevOpen == token.LPAREN) &&
(tok != token.RBRACK || p.prevOpen == token.LBRACK) {
- if p.containsLinebreak() && p.mode&noExtraLinebreak == 0 {
+ if p.containsLinebreak() && p.mode&noExtraLinebreak == 0 && p.level == 0 {
needsLinebreak = true
} else {
p.writeByte(' ', 1)
diff --git a/src/go/printer/testdata/comments2.golden b/src/go/printer/testdata/comments2.golden
index 7676a26c12..8b3a94ddcd 100644
--- a/src/go/printer/testdata/comments2.golden
+++ b/src/go/printer/testdata/comments2.golden
@@ -103,3 +103,62 @@ label:
mask := uint64(1)<<c - 1 // Allocation mask
used := atomic.LoadUint64(&h.used) // Current allocations
}
+
+// Test cases for issue 18782
+var _ = [][]int{
+ /* a, b, c, d, e */
+ /* a */ {0, 0, 0, 0, 0},
+ /* b */ {0, 5, 4, 4, 4},
+ /* c */ {0, 4, 5, 4, 4},
+ /* d */ {0, 4, 4, 5, 4},
+ /* e */ {0, 4, 4, 4, 5},
+}
+
+var _ = T{ /* a */ 0}
+
+var _ = T{ /* a */ /* b */ 0}
+
+var _ = T{ /* a */ /* b */
+ /* c */ 0,
+}
+
+var _ = T{ /* a */ /* b */
+ /* c */
+ /* d */ 0,
+}
+
+var _ = T{
+ /* a */
+ /* b */ 0,
+}
+
+var _ = T{ /* a */ {}}
+
+var _ = T{ /* a */ /* b */ {}}
+
+var _ = T{ /* a */ /* b */
+ /* c */ {},
+}
+
+var _ = T{ /* a */ /* b */
+ /* c */
+ /* d */ {},
+}
+
+var _ = T{
+ /* a */
+ /* b */ {},
+}
+
+var _ = []T{
+ func() {
+ var _ = [][]int{
+ /* a, b, c, d, e */
+ /* a */ {0, 0, 0, 0, 0},
+ /* b */ {0, 5, 4, 4, 4},
+ /* c */ {0, 4, 5, 4, 4},
+ /* d */ {0, 4, 4, 5, 4},
+ /* e */ {0, 4, 4, 4, 5},
+ }
+ },
+}
diff --git a/src/go/printer/testdata/comments2.input b/src/go/printer/testdata/comments2.input
index 4a055c8277..8d38c4194b 100644
--- a/src/go/printer/testdata/comments2.input
+++ b/src/go/printer/testdata/comments2.input
@@ -103,3 +103,66 @@ label:
mask := uint64(1)<<c - 1 // Allocation mask
used := atomic.LoadUint64(&h.used) // Current allocations
}
+
+// Test cases for issue 18782
+var _ = [][]int{
+ /* a, b, c, d, e */
+ /* a */ {0, 0, 0, 0, 0},
+ /* b */ {0, 5, 4, 4, 4},
+ /* c */ {0, 4, 5, 4, 4},
+ /* d */ {0, 4, 4, 5, 4},
+ /* e */ {0, 4, 4, 4, 5},
+}
+
+var _ = T{ /* a */ 0,
+}
+
+var _ = T{ /* a */ /* b */ 0,
+}
+
+var _ = T{ /* a */ /* b */
+ /* c */ 0,
+}
+
+var _ = T{ /* a */ /* b */
+ /* c */
+ /* d */ 0,
+}
+
+var _ = T{
+ /* a */
+ /* b */ 0,
+}
+
+var _ = T{ /* a */ {},
+}
+
+var _ = T{ /* a */ /* b */ {},
+}
+
+var _ = T{ /* a */ /* b */
+ /* c */ {},
+}
+
+var _ = T{ /* a */ /* b */
+ /* c */
+ /* d */ {},
+}
+
+var _ = T{
+ /* a */
+ /* b */ {},
+}
+
+var _ = []T{
+ func() {
+ var _ = [][]int{
+ /* a, b, c, d, e */
+ /* a */ {0, 0, 0, 0, 0},
+ /* b */ {0, 5, 4, 4, 4},
+ /* c */ {0, 4, 5, 4, 4},
+ /* d */ {0, 4, 4, 5, 4},
+ /* e */ {0, 4, 4, 4, 5},
+ }
+ },
+}
diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go
index 1c6d7b5299..06d2c93dda 100644
--- a/src/go/types/stdlib_test.go
+++ b/src/go/types/stdlib_test.go
@@ -157,6 +157,7 @@ func TestStdFixed(t *testing.T) {
"issue11362.go", // canonical import path check
"issue15002.go", // uses Mmap; testTestDir should consult build tags
"issue16369.go", // go/types handles this correctly - not an issue
+ "issue18459.go", // go/types doesn't check validity of //go:xxx directives
)
}
diff --git a/src/net/http/client.go b/src/net/http/client.go
index 7eb87c6d10..0005538e70 100644
--- a/src/net/http/client.go
+++ b/src/net/http/client.go
@@ -413,19 +413,28 @@ func (c *Client) checkRedirect(req *Request, via []*Request) error {
// redirectBehavior describes what should happen when the
// client encounters a 3xx status code from the server
-func redirectBehavior(reqMethod string, resp *Response, via []*Request) (redirectMethod string, shouldRedirect bool) {
+func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirectMethod string, shouldRedirect, includeBody bool) {
switch resp.StatusCode {
case 301, 302, 303:
- redirectMethod = "GET"
+ redirectMethod = reqMethod
shouldRedirect = true
+ includeBody = false
+
+ // RFC 2616 allowed automatic redirection only with GET and
+ // HEAD requests. RFC 7231 lifts this restriction, but we still
+ // restrict other methods to GET to maintain compatibility.
+ // See Issue 18570.
+ if reqMethod != "GET" && reqMethod != "HEAD" {
+ redirectMethod = "GET"
+ }
case 307, 308:
redirectMethod = reqMethod
shouldRedirect = true
+ includeBody = true
// Treat 307 and 308 specially, since they're new in
// Go 1.8, and they also require re-sending the request body.
- loc := resp.Header.Get("Location")
- if loc == "" {
+ if resp.Header.Get("Location") == "" {
// 308s have been observed in the wild being served
// without Location headers. Since Go 1.7 and earlier
// didn't follow these codes, just stop here instead
@@ -434,7 +443,6 @@ func redirectBehavior(reqMethod string, resp *Response, via []*Request) (redirec
shouldRedirect = false
break
}
- ireq := via[0]
if ireq.GetBody == nil && ireq.outgoingLength() != 0 {
// We had a request body, and 307/308 require
// re-sending it, but GetBody is not defined. So just
@@ -443,8 +451,7 @@ func redirectBehavior(reqMethod string, resp *Response, via []*Request) (redirec
shouldRedirect = false
}
}
-
- return redirectMethod, shouldRedirect
+ return redirectMethod, shouldRedirect, includeBody
}
// Do sends an HTTP request and returns an HTTP response, following
@@ -474,7 +481,8 @@ func redirectBehavior(reqMethod string, resp *Response, via []*Request) (redirec
// If the server replies with a redirect, the Client first uses the
// CheckRedirect function to determine whether the redirect should be
// followed. If permitted, a 301, 302, or 303 redirect causes
-// subsequent requests to use HTTP method "GET", with no body.
+// subsequent requests to use HTTP method GET
+// (or HEAD if the original request was HEAD), with no body.
// A 307 or 308 redirect preserves the original HTTP method and body,
// provided that the Request.GetBody function is defined.
// The NewRequest function automatically sets GetBody for common
@@ -486,11 +494,14 @@ func (c *Client) Do(req *Request) (*Response, error) {
}
var (
- deadline = c.deadline()
- reqs []*Request
- resp *Response
- copyHeaders = c.makeHeadersCopier(req)
+ deadline = c.deadline()
+ reqs []*Request
+ resp *Response
+ copyHeaders = c.makeHeadersCopier(req)
+
+ // Redirect behavior:
redirectMethod string
+ includeBody bool
)
uerr := func(err error) error {
req.closeBody()
@@ -528,7 +539,7 @@ func (c *Client) Do(req *Request) (*Response, error) {
Cancel: ireq.Cancel,
ctx: ireq.ctx,
}
- if ireq.GetBody != nil {
+ if includeBody && ireq.GetBody != nil {
req.Body, err = ireq.GetBody()
if err != nil {
return nil, uerr(err)
@@ -592,7 +603,7 @@ func (c *Client) Do(req *Request) (*Response, error) {
}
var shouldRedirect bool
- redirectMethod, shouldRedirect = redirectBehavior(req.Method, resp, reqs)
+ redirectMethod, shouldRedirect, includeBody = redirectBehavior(req.Method, resp, reqs[0])
if !shouldRedirect {
return resp, nil
}
diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go
index ca6e9180f1..4f674dd8d6 100644
--- a/src/net/http/client_test.go
+++ b/src/net/http/client_test.go
@@ -360,25 +360,25 @@ func TestPostRedirects(t *testing.T) {
wantSegments := []string{
`POST / "first"`,
`POST /?code=301&next=302 "c301"`,
- `GET /?code=302 "c301"`,
- `GET / "c301"`,
+ `GET /?code=302 ""`,
+ `GET / ""`,
`POST /?code=302&next=302 "c302"`,
- `GET /?code=302 "c302"`,
- `GET / "c302"`,
+ `GET /?code=302 ""`,
+ `GET / ""`,
`POST /?code=303&next=301 "c303wc301"`,
- `GET /?code=301 "c303wc301"`,
- `GET / "c303wc301"`,
+ `GET /?code=301 ""`,
+ `GET / ""`,
`POST /?code=304 "c304"`,
`POST /?code=305 "c305"`,
`POST /?code=307&next=303,308,302 "c307"`,
`POST /?code=303&next=308,302 "c307"`,
- `GET /?code=308&next=302 "c307"`,
+ `GET /?code=308&next=302 ""`,
`GET /?code=302 "c307"`,
- `GET / "c307"`,
+ `GET / ""`,
`POST /?code=308&next=302,301 "c308"`,
`POST /?code=302&next=301 "c308"`,
- `GET /?code=301 "c308"`,
- `GET / "c308"`,
+ `GET /?code=301 ""`,
+ `GET / ""`,
`POST /?code=404 "c404"`,
}
want := strings.Join(wantSegments, "\n")
@@ -399,20 +399,20 @@ func TestDeleteRedirects(t *testing.T) {
wantSegments := []string{
`DELETE / "first"`,
`DELETE /?code=301&next=302,308 "c301"`,
- `GET /?code=302&next=308 "c301"`,
- `GET /?code=308 "c301"`,
+ `GET /?code=302&next=308 ""`,
+ `GET /?code=308 ""`,
`GET / "c301"`,
`DELETE /?code=302&next=302 "c302"`,
- `GET /?code=302 "c302"`,
- `GET / "c302"`,
+ `GET /?code=302 ""`,
+ `GET / ""`,
`DELETE /?code=303 "c303"`,
- `GET / "c303"`,
+ `GET / ""`,
`DELETE /?code=307&next=301,308,303,302,304 "c307"`,
`DELETE /?code=301&next=308,303,302,304 "c307"`,
- `GET /?code=308&next=303,302,304 "c307"`,
+ `GET /?code=308&next=303,302,304 ""`,
`GET /?code=303&next=302,304 "c307"`,
- `GET /?code=302&next=304 "c307"`,
- `GET /?code=304 "c307"`,
+ `GET /?code=302&next=304 ""`,
+ `GET /?code=304 ""`,
`DELETE /?code=308&next=307 "c308"`,
`DELETE /?code=307 "c308"`,
`DELETE / "c308"`,
@@ -432,7 +432,11 @@ func testRedirectsByMethod(t *testing.T, method string, table []redirectTest, wa
ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
log.Lock()
slurp, _ := ioutil.ReadAll(r.Body)
- fmt.Fprintf(&log.Buffer, "%s %s %q\n", r.Method, r.RequestURI, slurp)
+ fmt.Fprintf(&log.Buffer, "%s %s %q", r.Method, r.RequestURI, slurp)
+ if cl := r.Header.Get("Content-Length"); r.Method == "GET" && len(slurp) == 0 && (r.ContentLength != 0 || cl != "") {
+ fmt.Fprintf(&log.Buffer, " (but with body=%T, content-length = %v, %q)", r.Body, r.ContentLength, cl)
+ }
+ log.WriteByte('\n')
log.Unlock()
urlQuery := r.URL.Query()
if v := urlQuery.Get("code"); v != "" {
@@ -475,7 +479,24 @@ func testRedirectsByMethod(t *testing.T, method string, table []redirectTest, wa
want = strings.TrimSpace(want)
if got != want {
- t.Errorf("Log differs.\n Got:\n%s\nWant:\n%s\n", got, want)
+ got, want, lines := removeCommonLines(got, want)
+ t.Errorf("Log differs after %d common lines.\n\nGot:\n%s\n\nWant:\n%s\n", lines, got, want)
+ }
+}
+
+func removeCommonLines(a, b string) (asuffix, bsuffix string, commonLines int) {
+ for {
+ nl := strings.IndexByte(a, '\n')
+ if nl < 0 {
+ return a, b, commonLines
+ }
+ line := a[:nl+1]
+ if !strings.HasPrefix(b, line) {
+ return a, b, commonLines
+ }
+ commonLines++
+ a = a[len(line):]
+ b = b[len(line):]
}
}
@@ -1665,9 +1686,9 @@ func TestClientRedirectTypes(t *testing.T) {
3: {method: "POST", serverStatus: 307, wantMethod: "POST"},
4: {method: "POST", serverStatus: 308, wantMethod: "POST"},
- 5: {method: "HEAD", serverStatus: 301, wantMethod: "GET"},
- 6: {method: "HEAD", serverStatus: 302, wantMethod: "GET"},
- 7: {method: "HEAD", serverStatus: 303, wantMethod: "GET"},
+ 5: {method: "HEAD", serverStatus: 301, wantMethod: "HEAD"},
+ 6: {method: "HEAD", serverStatus: 302, wantMethod: "HEAD"},
+ 7: {method: "HEAD", serverStatus: 303, wantMethod: "HEAD"},
8: {method: "HEAD", serverStatus: 307, wantMethod: "HEAD"},
9: {method: "HEAD", serverStatus: 308, wantMethod: "HEAD"},
diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go
index 072da2552b..73dd56e8c4 100644
--- a/src/net/http/serve_test.go
+++ b/src/net/http/serve_test.go
@@ -5173,3 +5173,142 @@ func TestServerDuplicateBackgroundRead(t *testing.T) {
}
wg.Wait()
}
+
+// Test that the bufio.Reader returned by Hijack includes any buffered
+// byte (from the Server's backgroundRead) in its buffer. We want the
+// Handler code to be able to tell that a byte is available via
+// bufio.Reader.Buffered(), without resorting to Reading it
+// (potentially blocking) to get at it.
+func TestServerHijackGetsBackgroundByte(t *testing.T) {
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping test; see https://golang.org/issue/18657")
+ }
+ setParallel(t)
+ defer afterTest(t)
+ done := make(chan struct{})
+ inHandler := make(chan bool, 1)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ defer close(done)
+
+ // Tell the client to send more data after the GET request.
+ inHandler <- true
+
+ // Wait until the HTTP server sees the extra data
+ // after the GET request. The HTTP server fires the
+ // close notifier here, assuming it's a pipelined
+ // request, as documented.
+ select {
+ case <-w.(CloseNotifier).CloseNotify():
+ case <-time.After(5 * time.Second):
+ t.Error("timeout")
+ return
+ }
+
+ conn, buf, err := w.(Hijacker).Hijack()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ defer conn.Close()
+ n := buf.Reader.Buffered()
+ if n != 1 {
+ t.Errorf("buffered data = %d; want 1", n)
+ }
+ peek, err := buf.Reader.Peek(3)
+ if string(peek) != "foo" || err != nil {
+ t.Errorf("Peek = %q, %v; want foo, nil", peek, err)
+ }
+ }))
+ defer ts.Close()
+
+ cn, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer cn.Close()
+ if _, err := cn.Write([]byte("GET / HTTP/1.1\r\nHost: e.com\r\n\r\n")); err != nil {
+ t.Fatal(err)
+ }
+ <-inHandler
+ if _, err := cn.Write([]byte("foo")); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := cn.(*net.TCPConn).CloseWrite(); err != nil {
+ t.Fatal(err)
+ }
+ select {
+ case <-done:
+ case <-time.After(2 * time.Second):
+ t.Error("timeout")
+ }
+}
+
+// Like TestServerHijackGetsBackgroundByte above but sending a
+// immediate 1MB of data to the server to fill up the server's 4KB
+// buffer.
+func TestServerHijackGetsBackgroundByte_big(t *testing.T) {
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping test; see https://golang.org/issue/18657")
+ }
+ setParallel(t)
+ defer afterTest(t)
+ done := make(chan struct{})
+ const size = 8 << 10
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ defer close(done)
+
+ // Wait until the HTTP server sees the extra data
+ // after the GET request. The HTTP server fires the
+ // close notifier here, assuming it's a pipelined
+ // request, as documented.
+ select {
+ case <-w.(CloseNotifier).CloseNotify():
+ case <-time.After(5 * time.Second):
+ t.Error("timeout")
+ return
+ }
+
+ conn, buf, err := w.(Hijacker).Hijack()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ defer conn.Close()
+ slurp, err := ioutil.ReadAll(buf.Reader)
+ if err != nil {
+ t.Errorf("Copy: %v", err)
+ }
+ allX := true
+ for _, v := range slurp {
+ if v != 'x' {
+ allX = false
+ }
+ }
+ if len(slurp) != size {
+ t.Errorf("read %d; want %d", len(slurp), size)
+ } else if !allX {
+ t.Errorf("read %q; want %d 'x'", slurp, size)
+ }
+ }))
+ defer ts.Close()
+
+ cn, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer cn.Close()
+ if _, err := fmt.Fprintf(cn, "GET / HTTP/1.1\r\nHost: e.com\r\n\r\n%s",
+ strings.Repeat("x", size)); err != nil {
+ t.Fatal(err)
+ }
+ if err := cn.(*net.TCPConn).CloseWrite(); err != nil {
+ t.Fatal(err)
+ }
+
+ select {
+ case <-done:
+ case <-time.After(2 * time.Second):
+ t.Error("timeout")
+ }
+}
diff --git a/src/net/http/server.go b/src/net/http/server.go
index 96236489bd..df70a15193 100644
--- a/src/net/http/server.go
+++ b/src/net/http/server.go
@@ -164,7 +164,7 @@ type Flusher interface {
// should always test for this ability at runtime.
type Hijacker interface {
// Hijack lets the caller take over the connection.
- // After a call to Hijack(), the HTTP server library
+ // After a call to Hijack the HTTP server library
// will not do anything else with the connection.
//
// It becomes the caller's responsibility to manage
@@ -174,6 +174,9 @@ type Hijacker interface {
// already set, depending on the configuration of the
// Server. It is the caller's responsibility to set
// or clear those deadlines as needed.
+ //
+ // The returned bufio.Reader may contain unprocessed buffered
+ // data from the client.
Hijack() (net.Conn, *bufio.ReadWriter, error)
}
@@ -293,6 +296,11 @@ func (c *conn) hijackLocked() (rwc net.Conn, buf *bufio.ReadWriter, err error) {
rwc.SetDeadline(time.Time{})
buf = bufio.NewReadWriter(c.bufr, bufio.NewWriter(rwc))
+ if c.r.hasByte {
+ if _, err := c.bufr.Peek(c.bufr.Buffered() + 1); err != nil {
+ return nil, nil, fmt.Errorf("unexpected Peek failure reading buffered byte: %v", err)
+ }
+ }
c.setState(rwc, StateHijacked)
return
}
diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go
index d5ddf6a123..a58b1839cc 100644
--- a/src/net/http/transport_test.go
+++ b/src/net/http/transport_test.go
@@ -36,6 +36,7 @@ import (
"strconv"
"strings"
"sync"
+ "sync/atomic"
"testing"
"time"
)
@@ -2545,6 +2546,13 @@ type closerFunc func() error
func (f closerFunc) Close() error { return f() }
+type writerFuncConn struct {
+ net.Conn
+ write func(p []byte) (n int, err error)
+}
+
+func (c writerFuncConn) Write(p []byte) (n int, err error) { return c.write(p) }
+
// Issue 4677. If we try to reuse a connection that the server is in the
// process of closing, we may end up successfully writing out our request (or a
// portion of our request) only to find a connection error when we try to read
@@ -2557,66 +2565,78 @@ func (f closerFunc) Close() error { return f() }
func TestRetryIdempotentRequestsOnError(t *testing.T) {
defer afterTest(t)
+ var (
+ mu sync.Mutex
+ logbuf bytes.Buffer
+ )
+ logf := func(format string, args ...interface{}) {
+ mu.Lock()
+ defer mu.Unlock()
+ fmt.Fprintf(&logbuf, format, args...)
+ logbuf.WriteByte('\n')
+ }
+
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ logf("Handler")
+ w.Header().Set("X-Status", "ok")
}))
defer ts.Close()
- tr := &Transport{}
+ var writeNumAtomic int32
+ tr := &Transport{
+ Dial: func(network, addr string) (net.Conn, error) {
+ logf("Dial")
+ c, err := net.Dial(network, ts.Listener.Addr().String())
+ if err != nil {
+ logf("Dial error: %v", err)
+ return nil, err
+ }
+ return &writerFuncConn{
+ Conn: c,
+ write: func(p []byte) (n int, err error) {
+ if atomic.AddInt32(&writeNumAtomic, 1) == 2 {
+ logf("intentional write failure")
+ return 0, errors.New("second write fails")
+ }
+ logf("Write(%q)", p)
+ return c.Write(p)
+ },
+ }, nil
+ },
+ }
+ defer tr.CloseIdleConnections()
c := &Client{Transport: tr}
- const N = 2
- retryc := make(chan struct{}, N)
SetRoundTripRetried(func() {
- retryc <- struct{}{}
+ logf("Retried.")
})
defer SetRoundTripRetried(nil)
- for n := 0; n < 100; n++ {
- // open 2 conns
- errc := make(chan error, N)
- for i := 0; i < N; i++ {
- // start goroutines, send on errc
- go func() {
- res, err := c.Get(ts.URL)
- if err == nil {
- res.Body.Close()
- }
- errc <- err
- }()
- }
- for i := 0; i < N; i++ {
- if err := <-errc; err != nil {
- t.Fatal(err)
- }
- }
-
- ts.CloseClientConnections()
- for i := 0; i < N; i++ {
- go func() {
- res, err := c.Get(ts.URL)
- if err == nil {
- res.Body.Close()
- }
- errc <- err
- }()
+ for i := 0; i < 3; i++ {
+ res, err := c.Get("http://fake.golang/")
+ if err != nil {
+ t.Fatalf("i=%d: Get = %v", i, err)
}
+ res.Body.Close()
+ }
- for i := 0; i < N; i++ {
- if err := <-errc; err != nil {
- t.Fatal(err)
- }
- }
- for i := 0; i < N; i++ {
- select {
- case <-retryc:
- // we triggered a retry, test was successful
- t.Logf("finished after %d runs\n", n)
- return
- default:
- }
- }
+ mu.Lock()
+ got := logbuf.String()
+ mu.Unlock()
+ const want = `Dial
+Write("GET / HTTP/1.1\r\nHost: fake.golang\r\nUser-Agent: Go-http-client/1.1\r\nAccept-Encoding: gzip\r\n\r\n")
+Handler
+intentional write failure
+Retried.
+Dial
+Write("GET / HTTP/1.1\r\nHost: fake.golang\r\nUser-Agent: Go-http-client/1.1\r\nAccept-Encoding: gzip\r\n\r\n")
+Handler
+Write("GET / HTTP/1.1\r\nHost: fake.golang\r\nUser-Agent: Go-http-client/1.1\r\nAccept-Encoding: gzip\r\n\r\n")
+Handler
+`
+ if got != want {
+ t.Errorf("Log of events differs. Got:\n%s\nWant:\n%s", got, want)
}
- t.Fatal("did not trigger any retries")
}
// Issue 6981
diff --git a/src/os/os_test.go b/src/os/os_test.go
index b7300cd38c..7ad9aac70e 100644
--- a/src/os/os_test.go
+++ b/src/os/os_test.go
@@ -54,12 +54,15 @@ var sysdir = func() *sysDir {
case "darwin":
switch runtime.GOARCH {
case "arm", "arm64":
+ /// At this point the test harness has not had a chance
+ // to move us into the ./src/os directory, so the
+ // current working directory is the root of the app.
wd, err := syscall.Getwd()
if err != nil {
wd = err.Error()
}
return &sysDir{
- filepath.Join(wd, "..", ".."),
+ wd,
[]string{
"ResourceRules.plist",
"Info.plist",
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index 1fed972eea..0be306dc54 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -26,6 +26,8 @@ import (
"unsafe"
)
+var sink interface{}
+
func TestBool(t *testing.T) {
v := ValueOf(true)
if v.Bool() != true {
@@ -5338,6 +5340,72 @@ func TestCallGC(t *testing.T) {
f2("four", "five5", "six666", "seven77", "eight888")
}
+// Issue 18635 (function version).
+func TestKeepFuncLive(t *testing.T) {
+ // Test that we keep makeFuncImpl live as long as it is
+ // referenced on the stack.
+ typ := TypeOf(func(i int) {})
+ var f, g func(in []Value) []Value
+ f = func(in []Value) []Value {
+ clobber()
+ i := int(in[0].Int())
+ if i > 0 {
+ // We can't use Value.Call here because
+ // runtime.call* will keep the makeFuncImpl
+ // alive. However, by converting it to an
+ // interface value and calling that,
+ // reflect.callReflect is the only thing that
+ // can keep the makeFuncImpl live.
+ //
+ // Alternate between f and g so that if we do
+ // reuse the memory prematurely it's more
+ // likely to get obviously corrupted.
+ MakeFunc(typ, g).Interface().(func(i int))(i - 1)
+ }
+ return nil
+ }
+ g = func(in []Value) []Value {
+ clobber()
+ i := int(in[0].Int())
+ MakeFunc(typ, f).Interface().(func(i int))(i)
+ return nil
+ }
+ MakeFunc(typ, f).Call([]Value{ValueOf(10)})
+}
+
+// Issue 18635 (method version).
+type KeepMethodLive struct{}
+
+func (k KeepMethodLive) Method1(i int) {
+ clobber()
+ if i > 0 {
+ ValueOf(k).MethodByName("Method2").Interface().(func(i int))(i - 1)
+ }
+}
+
+func (k KeepMethodLive) Method2(i int) {
+ clobber()
+ ValueOf(k).MethodByName("Method1").Interface().(func(i int))(i)
+}
+
+func TestKeepMethodLive(t *testing.T) {
+ // Test that we keep methodValue live as long as it is
+ // referenced on the stack.
+ KeepMethodLive{}.Method1(10)
+}
+
+// clobber tries to clobber unreachable memory.
+func clobber() {
+ runtime.GC()
+ for i := 1; i < 32; i++ {
+ for j := 0; j < 10; j++ {
+ obj := make([]*byte, i)
+ sink = obj
+ }
+ }
+ runtime.GC()
+}
+
type funcLayoutTest struct {
rcvr, t Type
size, argsize, retOffset uintptr
diff --git a/src/reflect/value.go b/src/reflect/value.go
index a1bfb6d489..1abfbe6f65 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -538,6 +538,11 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) {
off += typ.size
}
}
+
+ // runtime.getArgInfo expects to be able to find ctxt on the
+ // stack when it finds our caller, makeFuncStub. Make sure it
+ // doesn't get garbage collected.
+ runtime.KeepAlive(ctxt)
}
// methodReceiver returns information about the receiver
@@ -650,6 +655,9 @@ func callMethod(ctxt *methodValue, frame unsafe.Pointer) {
// though it's a heap object.
memclrNoHeapPointers(args, frametype.size)
framePool.Put(args)
+
+ // See the comment in callReflect.
+ runtime.KeepAlive(ctxt)
}
// funcName returns the name of f, for use in error messages.
diff --git a/src/runtime/memmove_amd64.s b/src/runtime/memmove_amd64.s
index 464f5fdc1b..c2286d3edd 100644
--- a/src/runtime/memmove_amd64.s
+++ b/src/runtime/memmove_amd64.s
@@ -146,10 +146,16 @@ move_1or2:
move_0:
RET
move_3or4:
+ CMPQ BX, $4
+ JB move_3
+ MOVL (SI), AX
+ MOVL AX, (DI)
+ RET
+move_3:
MOVW (SI), AX
- MOVW -2(SI)(BX*1), CX
+ MOVB 2(SI), CX
MOVW AX, (DI)
- MOVW CX, -2(DI)(BX*1)
+ MOVB CX, 2(DI)
RET
move_5through7:
MOVL (SI), AX
diff --git a/src/runtime/memmove_test.go b/src/runtime/memmove_test.go
index dbfa284c28..74b8753b5f 100644
--- a/src/runtime/memmove_test.go
+++ b/src/runtime/memmove_test.go
@@ -6,6 +6,7 @@ package runtime_test
import (
"crypto/rand"
+ "encoding/binary"
"fmt"
"internal/race"
. "runtime"
@@ -447,3 +448,22 @@ func BenchmarkCopyFat1024(b *testing.B) {
_ = y
}
}
+
+func BenchmarkIssue18740(b *testing.B) {
+ // This tests that memmove uses one 4-byte load/store to move 4 bytes.
+ // It used to do 2 2-byte load/stores, which leads to a pipeline stall
+ // when we try to read the result with one 4-byte load.
+ var buf [4]byte
+ for j := 0; j < b.N; j++ {
+ s := uint32(0)
+ for i := 0; i < 4096; i += 4 {
+ copy(buf[:], g[i:])
+ s += binary.LittleEndian.Uint32(buf[:])
+ }
+ sink = uint64(s)
+ }
+}
+
+// TODO: 2 byte and 8 byte benchmarks also.
+
+var g [4096]byte
diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go
index 64a2f3abef..0b996d8950 100644
--- a/src/runtime/mgc.go
+++ b/src/runtime/mgc.go
@@ -1129,8 +1129,6 @@ top:
// sitting in the per-P work caches.
// Flush and disable work caches.
- gcMarkRootCheck()
-
// Disallow caching workbufs and indicate that we're in mark 2.
gcBlackenPromptly = true
@@ -1153,6 +1151,16 @@ top:
})
})
+ // Check that roots are marked. We should be able to
+ // do this before the forEachP, but based on issue
+ // #16083 there may be a (harmless) race where we can
+ // enter mark 2 while some workers are still scanning
+ // stacks. The forEachP ensures these scans are done.
+ //
+ // TODO(austin): Figure out the race and fix this
+ // properly.
+ gcMarkRootCheck()
+
// Now we can start up mark 2 workers.
atomic.Xaddint64(&gcController.dedicatedMarkWorkersNeeded, 0xffffffff)
atomic.Xaddint64(&gcController.fractionalMarkWorkersNeeded, 0xffffffff)
diff --git a/src/runtime/mgcsweep.go b/src/runtime/mgcsweep.go
index e74a451d0d..fb5c488ffc 100644
--- a/src/runtime/mgcsweep.go
+++ b/src/runtime/mgcsweep.go
@@ -405,7 +405,10 @@ func reimburseSweepCredit(unusableBytes uintptr) {
// Nobody cares about the credit. Avoid the atomic.
return
}
- if int64(atomic.Xadd64(&mheap_.spanBytesAlloc, -int64(unusableBytes))) < 0 {
+ nval := atomic.Xadd64(&mheap_.spanBytesAlloc, -int64(unusableBytes))
+ if int64(nval) < 0 {
+ // Debugging for #18043.
+ print("runtime: bad spanBytesAlloc=", nval, " (was ", nval+uint64(unusableBytes), ") unusableBytes=", unusableBytes, " sweepPagesPerByte=", mheap_.sweepPagesPerByte, "\n")
throw("spanBytesAlloc underflow")
}
}
diff --git a/src/runtime/msan.go b/src/runtime/msan.go
index 7177c8e611..c0f3957e28 100644
--- a/src/runtime/msan.go
+++ b/src/runtime/msan.go
@@ -28,9 +28,11 @@ const msanenabled = true
// the runtime, but operations like a slice copy can call msanread
// anyhow for values on the stack. Just ignore msanread when running
// on the system stack. The other msan functions are fine.
+//
+//go:nosplit
func msanread(addr unsafe.Pointer, sz uintptr) {
g := getg()
- if g == g.m.g0 || g == g.m.gsignal {
+ if g == nil || g.m == nil || g == g.m.g0 || g == g.m.gsignal {
return
}
domsanread(addr, sz)
diff --git a/src/runtime/plugin.go b/src/runtime/plugin.go
index 80869e1b1c..8edb29c9fe 100644
--- a/src/runtime/plugin.go
+++ b/src/runtime/plugin.go
@@ -56,7 +56,9 @@ func plugin_lastmoduleinit() (path string, syms map[string]interface{}, mismatch
lock(&ifaceLock)
for _, i := range md.itablinks {
- additab(i, true, false)
+ if i.inhash == 0 {
+ additab(i, true, false)
+ }
}
unlock(&ifaceLock)
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index 756ce63c24..f41672de73 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -240,6 +240,16 @@ func Gosched() {
mcall(gosched_m)
}
+var alwaysFalse bool
+
+// goschedguarded does nothing, but is written in a way that guarantees a preemption check in its prologue.
+// Calls to this function are inserted by the compiler in otherwise uninterruptible loops (see insertLoopReschedChecks).
+func goschedguarded() {
+ if alwaysFalse {
+ goschedguarded()
+ }
+}
+
// Puts the current goroutine into a waiting state and calls unlockf.
// If unlockf returns false, the goroutine is resumed.
// unlockf must not access this G's stack, as it may be moved between
diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go
index c2844375f7..f886961d6a 100644
--- a/src/runtime/runtime-gdb_test.go
+++ b/src/runtime/runtime-gdb_test.go
@@ -7,6 +7,7 @@ package runtime_test
import (
"bytes"
"fmt"
+ "go/build"
"internal/testenv"
"io/ioutil"
"os"
@@ -67,7 +68,6 @@ func checkGdbPython(t *testing.T) {
}
const helloSource = `
-package main
import "fmt"
var gslice []string
func main() {
@@ -85,9 +85,20 @@ func main() {
`
func TestGdbPython(t *testing.T) {
+ testGdbPython(t, false)
+}
+
+func TestGdbPythonCgo(t *testing.T) {
+ testGdbPython(t, true)
+}
+
+func testGdbPython(t *testing.T, cgo bool) {
if runtime.GOARCH == "mips64" {
testenv.SkipFlaky(t, 18173)
}
+ if cgo && !build.Default.CgoEnabled {
+ t.Skip("skipping because cgo is not enabled")
+ }
t.Parallel()
checkGdbEnvironment(t)
@@ -100,8 +111,15 @@ func TestGdbPython(t *testing.T) {
}
defer os.RemoveAll(dir)
+ var buf bytes.Buffer
+ buf.WriteString("package main\n")
+ if cgo {
+ buf.WriteString(`import "C"` + "\n")
+ }
+ buf.WriteString(helloSource)
+
src := filepath.Join(dir, "main.go")
- err = ioutil.WriteFile(src, []byte(helloSource), 0644)
+ err = ioutil.WriteFile(src, buf.Bytes(), 0644)
if err != nil {
t.Fatalf("failed to create file: %v", err)
}
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index f52190661c..ed82783ca9 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -285,6 +285,25 @@ func modulesinit() {
md.gcbssmask = progToPointerMask((*byte)(unsafe.Pointer(md.gcbss)), md.ebss-md.bss)
}
}
+
+ // Modules appear in the moduledata linked list in the order they are
+ // loaded by the dynamic loader, with one exception: the
+ // firstmoduledata itself the module that contains the runtime. This
+ // is not always the first module (when using -buildmode=shared, it
+ // is typically libstd.so, the second module). The order matters for
+ // typelinksinit, so we swap the first module with whatever module
+ // contains the main function.
+ //
+ // See Issue #18729.
+ mainText := funcPC(main_main)
+ for i, md := range *modules {
+ if md.text <= mainText && mainText <= md.etext {
+ (*modules)[0] = md
+ (*modules)[i] = &firstmoduledata
+ break
+ }
+ }
+
atomicstorep(unsafe.Pointer(&modulesSlice), unsafe.Pointer(modules))
}
diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s
index 839df16406..6ddcb30ae2 100644
--- a/src/runtime/sys_linux_amd64.s
+++ b/src/runtime/sys_linux_amd64.s
@@ -330,9 +330,9 @@ sigtrampnog:
// Lock sigprofCallersUse.
MOVL $0, AX
MOVL $1, CX
- MOVQ $runtime·sigprofCallersUse(SB), BX
+ MOVQ $runtime·sigprofCallersUse(SB), R11
LOCK
- CMPXCHGL CX, 0(BX)
+ CMPXCHGL CX, 0(R11)
JNZ sigtramp // Skip stack trace if already locked.
// Jump to the traceback function in runtime/cgo.
diff --git a/src/runtime/testdata/testprogcgo/threadpprof.go b/src/runtime/testdata/testprogcgo/threadpprof.go
index 44afb91d96..3da82961b9 100644
--- a/src/runtime/testdata/testprogcgo/threadpprof.go
+++ b/src/runtime/testdata/testprogcgo/threadpprof.go
@@ -61,7 +61,7 @@ static void* cpuHogDriver(void* arg __attribute__ ((unused))) {
return 0;
}
-void runCPUHogThread() {
+void runCPUHogThread(void) {
pthread_t tid;
pthread_create(&tid, 0, cpuHogDriver, 0);
}
diff --git a/src/runtime/testdata/testprogcgo/traceback.go b/src/runtime/testdata/testprogcgo/traceback.go
index e8b0a04556..2a023f66ca 100644
--- a/src/runtime/testdata/testprogcgo/traceback.go
+++ b/src/runtime/testdata/testprogcgo/traceback.go
@@ -15,16 +15,16 @@ package main
char *p;
-static int f3() {
+static int f3(void) {
*p = 0;
return 0;
}
-static int f2() {
+static int f2(void) {
return f3();
}
-static int f1() {
+static int f1(void) {
return f2();
}
diff --git a/src/syscall/mkpost.go b/src/syscall/mkpost.go
index 26aeec843f..e75ba1502a 100644
--- a/src/syscall/mkpost.go
+++ b/src/syscall/mkpost.go
@@ -18,6 +18,7 @@ import (
"log"
"os"
"regexp"
+ "strings"
)
func main() {
@@ -38,10 +39,16 @@ func main() {
re = regexp.MustCompile("Pad_cgo[A-Za-z0-9_]*")
s = re.ReplaceAllString(s, "_")
+ // We want to keep X__val in Fsid. Hide it and restore it later.
+ s = strings.Replace(s, "X__val", "MKPOSTFSIDVAL", 1)
+
// Replace other unwanted fields with blank identifiers.
re = regexp.MustCompile("X_[A-Za-z0-9_]*")
s = re.ReplaceAllString(s, "_")
+ // Restore X__val in Fsid.
+ s = strings.Replace(s, "MKPOSTFSIDVAL", "X__val", 1)
+
// Force the type of RawSockaddr.Data to [14]int8 to match
// the existing gccgo API.
re = regexp.MustCompile("(Data\\s+\\[14\\])uint8")
diff --git a/src/syscall/ztypes_linux_s390x.go b/src/syscall/ztypes_linux_s390x.go
index cdde47863f..63c4a83b19 100644
--- a/src/syscall/ztypes_linux_s390x.go
+++ b/src/syscall/ztypes_linux_s390x.go
@@ -140,7 +140,7 @@ type Dirent struct {
}
type Fsid struct {
- _ [2]int32
+ X__val [2]int32
}
type Flock_t struct {
diff --git a/src/testing/benchmark.go b/src/testing/benchmark.go
index c033ce5fec..bcebb418c4 100644
--- a/src/testing/benchmark.go
+++ b/src/testing/benchmark.go
@@ -219,7 +219,7 @@ func (b *B) run1() bool {
}
// Only print the output if we know we are not going to proceed.
// Otherwise it is printed in processBench.
- if b.hasSub || b.finished {
+ if atomic.LoadInt32(&b.hasSub) != 0 || b.finished {
tag := "BENCH"
if b.skipped {
tag = "SKIP"
@@ -460,10 +460,13 @@ func (ctx *benchContext) processBench(b *B) {
//
// A subbenchmark is like any other benchmark. A benchmark that calls Run at
// least once will not be measured itself and will be called once with N=1.
+//
+// Run may be called simultaneously from multiple goroutines, but all such
+// calls must happen before the outer benchmark function for b returns.
func (b *B) Run(name string, f func(b *B)) bool {
// Since b has subbenchmarks, we will no longer run it as a benchmark itself.
// Release the lock and acquire it on exit to ensure locks stay paired.
- b.hasSub = true
+ atomic.StoreInt32(&b.hasSub, 1)
benchmarkLock.Unlock()
defer benchmarkLock.Lock()
diff --git a/src/testing/sub_test.go b/src/testing/sub_test.go
index 8d5d9206f0..bb7b3e0925 100644
--- a/src/testing/sub_test.go
+++ b/src/testing/sub_test.go
@@ -6,6 +6,7 @@ package testing
import (
"bytes"
+ "fmt"
"regexp"
"strings"
"sync/atomic"
@@ -515,3 +516,19 @@ func TestBenchmarkOutput(t *T) {
Benchmark(func(b *B) { b.Error("do not print this output") })
Benchmark(func(b *B) {})
}
+
+func TestParallelSub(t *T) {
+ c := make(chan int)
+ block := make(chan int)
+ for i := 0; i < 10; i++ {
+ go func(i int) {
+ <-block
+ t.Run(fmt.Sprint(i), func(t *T) {})
+ c <- 1
+ }(i)
+ }
+ close(block)
+ for i := 0; i < 10; i++ {
+ <-c
+ }
+}
diff --git a/src/testing/testing.go b/src/testing/testing.go
index c972b2737f..bd19a31c27 100644
--- a/src/testing/testing.go
+++ b/src/testing/testing.go
@@ -216,6 +216,7 @@ import (
"strconv"
"strings"
"sync"
+ "sync/atomic"
"time"
)
@@ -267,8 +268,8 @@ type common struct {
skipped bool // Test of benchmark has been skipped.
finished bool // Test function has completed.
done bool // Test is finished and all subtests have completed.
- hasSub bool
- raceErrors int // number of races detected during test
+ hasSub int32 // written atomically
+ raceErrors int // number of races detected during test
parent *common
level int // Nesting depth of test or benchmark.
@@ -645,7 +646,7 @@ func tRunner(t *T, fn func(t *T)) {
// Do not lock t.done to allow race detector to detect race in case
// the user does not appropriately synchronizes a goroutine.
t.done = true
- if t.parent != nil && !t.hasSub {
+ if t.parent != nil && atomic.LoadInt32(&t.hasSub) == 0 {
t.setRan()
}
t.signal <- true
@@ -659,8 +660,11 @@ func tRunner(t *T, fn func(t *T)) {
// Run runs f as a subtest of t called name. It reports whether f succeeded.
// Run will block until all its parallel subtests have completed.
+//
+// Run may be called simultaneously from multiple goroutines, but all such
+// calls must happen before the outer test function for t returns.
func (t *T) Run(name string, f func(t *T)) bool {
- t.hasSub = true
+ atomic.StoreInt32(&t.hasSub, 1)
testName, ok := t.context.match.fullName(&t.common, name)
if !ok {
return true
@@ -817,6 +821,7 @@ func (m *M) Run() int {
haveExamples = len(m.examples) > 0
testRan, testOk := runTests(m.deps.MatchString, m.tests)
exampleRan, exampleOk := runExamples(m.deps.MatchString, m.examples)
+ stopAlarm()
if !testRan && !exampleRan && *matchBenchmarks == "" {
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
}
diff --git a/src/vendor/golang_org/x/crypto/poly1305/sum_amd64.s b/src/vendor/golang_org/x/crypto/poly1305/sum_amd64.s
index bc75c61afc..2edae63828 100644
--- a/src/vendor/golang_org/x/crypto/poly1305/sum_amd64.s
+++ b/src/vendor/golang_org/x/crypto/poly1305/sum_amd64.s
@@ -54,9 +54,9 @@
ADCQ t3, h1; \
ADCQ $0, h2
-DATA poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF
-DATA poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC
-GLOBL poly1305Mask<>(SB), RODATA, $16
+DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF
+DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC
+GLOBL ·poly1305Mask<>(SB), RODATA, $16
// func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]key)
TEXT ·poly1305(SB), $0-32
@@ -67,8 +67,8 @@ TEXT ·poly1305(SB), $0-32
MOVQ 0(AX), R11
MOVQ 8(AX), R12
- ANDQ poly1305Mask<>(SB), R11 // r0
- ANDQ poly1305Mask<>+8(SB), R12 // r1
+ ANDQ ·poly1305Mask<>(SB), R11 // r0
+ ANDQ ·poly1305Mask<>+8(SB), R12 // r1
XORQ R8, R8 // h0
XORQ R9, R9 // h1
XORQ R10, R10 // h2
diff --git a/src/vendor/golang_org/x/crypto/poly1305/sum_arm.s b/src/vendor/golang_org/x/crypto/poly1305/sum_arm.s
index 93167b2712..f70b4ac484 100644
--- a/src/vendor/golang_org/x/crypto/poly1305/sum_arm.s
+++ b/src/vendor/golang_org/x/crypto/poly1305/sum_arm.s
@@ -9,12 +9,12 @@
// This code was translated into a form compatible with 5a from the public
// domain source by Andrew Moon: github.com/floodyberry/poly1305-opt/blob/master/app/extensions/poly1305.
-DATA poly1305_init_constants_armv6<>+0x00(SB)/4, $0x3ffffff
-DATA poly1305_init_constants_armv6<>+0x04(SB)/4, $0x3ffff03
-DATA poly1305_init_constants_armv6<>+0x08(SB)/4, $0x3ffc0ff
-DATA poly1305_init_constants_armv6<>+0x0c(SB)/4, $0x3f03fff
-DATA poly1305_init_constants_armv6<>+0x10(SB)/4, $0x00fffff
-GLOBL poly1305_init_constants_armv6<>(SB), 8, $20
+DATA ·poly1305_init_constants_armv6<>+0x00(SB)/4, $0x3ffffff
+DATA ·poly1305_init_constants_armv6<>+0x04(SB)/4, $0x3ffff03
+DATA ·poly1305_init_constants_armv6<>+0x08(SB)/4, $0x3ffc0ff
+DATA ·poly1305_init_constants_armv6<>+0x0c(SB)/4, $0x3f03fff
+DATA ·poly1305_init_constants_armv6<>+0x10(SB)/4, $0x00fffff
+GLOBL ·poly1305_init_constants_armv6<>(SB), 8, $20
// Warning: the linker may use R11 to synthesize certain instructions. Please
// take care and verify that no synthetic instructions use it.
@@ -27,7 +27,7 @@ TEXT poly1305_init_ext_armv6<>(SB), NOSPLIT, $0
ADD $4, R13, R8
MOVM.IB [R4-R7], (R8)
MOVM.IA.W (R1), [R2-R5]
- MOVW $poly1305_init_constants_armv6<>(SB), R7
+ MOVW $·poly1305_init_constants_armv6<>(SB), R7
MOVW R2, R8
MOVW R2>>26, R9
MOVW R3>>20, g
diff --git a/test/fixedbugs/issue10958.go b/test/fixedbugs/issue10958.go
new file mode 100644
index 0000000000..86d2057622
--- /dev/null
+++ b/test/fixedbugs/issue10958.go
@@ -0,0 +1,95 @@
+// +build !nacl,disabled
+// buildrun -t 10 -gcflags=-d=ssa/insert_resched_checks/on,ssa/check/on
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test is disabled because it flakes when run in all.bash
+// on some platforms, but is useful standalone to verify
+// that rescheduling checks are working (and we may wish
+// to investigate the flake, since it suggests that the
+// loop rescheduling check may not work right on those
+// platforms).
+
+// This checks to see that call-free infinite loops do not
+// block garbage collection. IF YOU RUN IT STANDALONE without
+// -gcflags=-d=ssa/insert_resched_checks/on in a not-experimental
+// build, it should hang.
+
+package main
+
+import (
+ "runtime"
+)
+
+var someglobal1 int
+var someglobal2 int
+var someglobal3 int
+
+//go:noinline
+func f() {}
+
+func standinacorner1() {
+ for someglobal1&1 == 0 {
+ someglobal1++
+ someglobal1++
+ }
+}
+
+func standinacorner2(i int) {
+ // contains an irreducible loop containing changes to memory
+ if i != 0 {
+ goto midloop
+ }
+
+loop:
+ if someglobal2&1 != 0 {
+ goto done
+ }
+ someglobal2++
+midloop:
+ someglobal2++
+ goto loop
+
+done:
+ return
+}
+
+func standinacorner3() {
+ for someglobal3&1 == 0 {
+ if someglobal3&2 != 0 {
+ for someglobal3&3 == 2 {
+ someglobal3++
+ someglobal3++
+ someglobal3++
+ someglobal3++
+ }
+ }
+ someglobal3++
+ someglobal3++
+ someglobal3++
+ someglobal3++
+ }
+}
+
+func main() {
+ go standinacorner1()
+ go standinacorner2(0)
+ go standinacorner3()
+ // println("About to stand in a corner1")
+ for someglobal1 == 0 {
+ runtime.Gosched()
+ }
+ // println("About to stand in a corner2")
+ for someglobal2 == 0 {
+ runtime.Gosched()
+ }
+ // println("About to stand in a corner3")
+ for someglobal3 == 0 {
+ runtime.Gosched()
+ }
+ // println("About to GC")
+ runtime.GC()
+ // println("Success")
+}
diff --git a/test/fixedbugs/issue18459.go b/test/fixedbugs/issue18459.go
new file mode 100644
index 0000000000..ac07661d63
--- /dev/null
+++ b/test/fixedbugs/issue18459.go
@@ -0,0 +1,13 @@
+// errorcheck
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Verify that we have a line number for this error.
+
+package main
+
+//go:nowritebarrier // ERROR "go:nowritebarrier only allowed in runtime"
+func main() {
+}
diff --git a/test/fixedbugs/issue18661.go b/test/fixedbugs/issue18661.go
new file mode 100644
index 0000000000..8c83775200
--- /dev/null
+++ b/test/fixedbugs/issue18661.go
@@ -0,0 +1,18 @@
+// compile
+
+// Copyright 2017 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 p
+
+var (
+ e interface{}
+ s = struct{ a *int }{}
+ b = e == s
+)
+
+func test(obj interface{}) {
+ if obj != struct{ a *string }{} {
+ }
+}
diff --git a/test/fixedbugs/issue18725.go b/test/fixedbugs/issue18725.go
new file mode 100644
index 0000000000..c632dbad63
--- /dev/null
+++ b/test/fixedbugs/issue18725.go
@@ -0,0 +1,24 @@
+// run
+
+// Copyright 2017 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 "os"
+
+func panicWhenNot(cond bool) {
+ if cond {
+ os.Exit(0)
+ } else {
+ panic("nilcheck elim failed")
+ }
+}
+
+func main() {
+ e := (*string)(nil)
+ panicWhenNot(e == e)
+ // Should never reach this line.
+ panicWhenNot(*e == *e)
+}
diff --git a/test/fixedbugs/issue18808.go b/test/fixedbugs/issue18808.go
new file mode 100644
index 0000000000..c98386ee78
--- /dev/null
+++ b/test/fixedbugs/issue18808.go
@@ -0,0 +1,63 @@
+// run
+
+// Copyright 2017 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 lim = 0x80000000
+
+//go:noinline
+func eq(x uint32) {
+ if x == lim {
+ return
+ }
+ panic("x == lim returned false")
+}
+
+//go:noinline
+func neq(x uint32) {
+ if x != lim {
+ panic("x != lim returned true")
+ }
+}
+
+//go:noinline
+func gt(x uint32) {
+ if x > lim {
+ return
+ }
+ panic("x > lim returned false")
+}
+
+//go:noinline
+func gte(x uint32) {
+ if x >= lim {
+ return
+ }
+ panic("x >= lim returned false")
+}
+
+//go:noinline
+func lt(x uint32) {
+ if x < lim {
+ panic("x < lim returned true")
+ }
+}
+
+//go:noinline
+func lte(x uint32) {
+ if x <= lim {
+ panic("x <= lim returned true")
+ }
+}
+
+func main() {
+ eq(lim)
+ neq(lim)
+ gt(lim+1)
+ gte(lim+1)
+ lt(lim+1)
+ lte(lim+1)
+}
diff --git a/test/fixedbugs/issue18906.go b/test/fixedbugs/issue18906.go
new file mode 100644
index 0000000000..544400be25
--- /dev/null
+++ b/test/fixedbugs/issue18906.go
@@ -0,0 +1,36 @@
+// run
+
+// Copyright 2017 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
+
+//go:noinline
+func f(x int) {
+}
+
+//go:noinline
+func val() int8 {
+ return -1
+}
+
+var (
+ array = [257]int{}
+ slice = array[1:]
+)
+
+func init() {
+ for i := range array {
+ array[i] = i - 1
+ }
+}
+
+func main() {
+ x := val()
+ y := int(uint8(x))
+ f(y) // try and force y to be calculated and spilled
+ if slice[y] != 255 {
+ panic("incorrect value")
+ }
+}
diff --git a/test/live.go b/test/live.go
index 4fb231cfef..b23e1509e0 100644
--- a/test/live.go
+++ b/test/live.go
@@ -1,6 +1,7 @@
-// errorcheckwithauto -0 -l -live -wb=0
+// errorcheckwithauto -0 -l -live -wb=0 -d=ssa/insert_resched_checks/off
// +build !ppc64,!ppc64le
// ppc64 needs a better tighten pass to make f18 pass
+// rescheduling checks need to be turned off because there are some live variables across the inserted check call
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
diff --git a/test/nilptr3.go b/test/nilptr3.go
index 8fdae8c075..c681cba50c 100644
--- a/test/nilptr3.go
+++ b/test/nilptr3.go
@@ -40,23 +40,23 @@ var (
)
func f1() {
- _ = *intp // ERROR "generated nil check"
+ _ = *intp // ERROR "removed nil check"
// This one should be removed but the block copy needs
// to be turned into its own pseudo-op in order to see
// the indirect.
- _ = *arrayp // ERROR "generated nil check"
+ _ = *arrayp // ERROR "removed nil check"
// 0-byte indirect doesn't suffice.
// we don't registerize globals, so there are no removed.* nil checks.
- _ = *array0p // ERROR "generated nil check"
_ = *array0p // ERROR "removed nil check"
+ _ = *array0p // ERROR "generated nil check"
- _ = *intp // ERROR "removed nil check"
+ _ = *intp // ERROR "generated nil check"
_ = *arrayp // ERROR "removed nil check"
_ = *structp // ERROR "generated nil check"
_ = *emptyp // ERROR "generated nil check"
- _ = *arrayp // ERROR "removed nil check"
+ _ = *arrayp // ERROR "generated nil check"
}
func f2() {
@@ -71,15 +71,15 @@ func f2() {
empty1p *Empty1
)
- _ = *intp // ERROR "generated nil check"
- _ = *arrayp // ERROR "generated nil check"
- _ = *array0p // ERROR "generated nil check"
- _ = *array0p // ERROR "removed.* nil check"
_ = *intp // ERROR "removed.* nil check"
_ = *arrayp // ERROR "removed.* nil check"
+ _ = *array0p // ERROR "removed.* nil check"
+ _ = *array0p // ERROR "generated nil check"
+ _ = *intp // ERROR "generated nil check"
+ _ = *arrayp // ERROR "removed.* nil check"
_ = *structp // ERROR "generated nil check"
_ = *emptyp // ERROR "generated nil check"
- _ = *arrayp // ERROR "removed.* nil check"
+ _ = *arrayp // ERROR "generated nil check"
_ = *bigarrayp // ERROR "generated nil check" ARM removed nil check before indirect!!
_ = *bigstructp // ERROR "generated nil check"
_ = *empty1p // ERROR "generated nil check"
@@ -122,16 +122,16 @@ func f3(x *[10000]int) {
// x wasn't going to change across the function call.
// But it's a little complex to do and in practice doesn't
// matter enough.
- _ = x[9999] // ERROR "removed nil check"
+ _ = x[9999] // ERROR "generated nil check" // TODO: fix
}
func f3a() {
x := fx10k()
y := fx10k()
z := fx10k()
- _ = &x[9] // ERROR "generated nil check"
- y = z
_ = &x[9] // ERROR "removed.* nil check"
+ y = z
+ _ = &x[9] // ERROR "generated nil check"
x = y
_ = &x[9] // ERROR "generated nil check"
}
@@ -139,11 +139,11 @@ func f3a() {
func f3b() {
x := fx10k()
y := fx10k()
- _ = &x[9] // ERROR "generated nil check"
+ _ = &x[9] // ERROR "removed.* nil check"
y = x
_ = &x[9] // ERROR "removed.* nil check"
x = y
- _ = &x[9] // ERROR "removed.* nil check"
+ _ = &x[9] // ERROR "generated nil check"
}
func fx10() *[10]int
@@ -179,15 +179,15 @@ func f4(x *[10]int) {
_ = x[9] // ERROR "generated nil check" // bug would like to remove before indirect
fx10()
- _ = x[9] // ERROR "removed nil check"
+ _ = x[9] // ERROR "generated nil check" // TODO: fix
x = fx10()
y := fx10()
- _ = &x[9] // ERROR "generated nil check"
+ _ = &x[9] // ERROR "removed[a-z ]* nil check"
y = x
_ = &x[9] // ERROR "removed[a-z ]* nil check"
x = y
- _ = &x[9] // ERROR "removed[a-z ]* nil check"
+ _ = &x[9] // ERROR "generated nil check"
}
func f5(p *float32, q *float64, r *float32, s *float64) float64 {
diff --git a/test/opt_branchlikely.go b/test/opt_branchlikely.go
index 5781253e3e..84de32179f 100644
--- a/test/opt_branchlikely.go
+++ b/test/opt_branchlikely.go
@@ -1,5 +1,6 @@
// +build amd64
-// errorcheck -0 -d=ssa/likelyadjust/debug=1
+// errorcheck -0 -d=ssa/likelyadjust/debug=1,ssa/insert_resched_checks/off
+// rescheduling check insertion is turend off because the inserted conditional branches perturb the errorcheck
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
diff --git a/test/run.go b/test/run.go
index 0dee6b5caa..19ca328765 100644
--- a/test/run.go
+++ b/test/run.go
@@ -463,6 +463,7 @@ func (t *test) run() {
}
var args, flags []string
+ var tim int
wantError := false
wantAuto := false
singlefilepkgs := false
@@ -478,7 +479,7 @@ func (t *test) run() {
action = "rundir"
case "cmpout":
action = "run" // the run case already looks for <dir>/<test>.out files
- case "compile", "compiledir", "build", "run", "runoutput", "rundir":
+ case "compile", "compiledir", "build", "run", "buildrun", "runoutput", "rundir":
// nothing to do
case "errorcheckandrundir":
wantError = false // should be no error if also will run
@@ -505,6 +506,14 @@ func (t *test) run() {
wantError = false
case "-s":
singlefilepkgs = true
+ case "-t": // timeout in seconds
+ args = args[1:]
+ var err error
+ tim, err = strconv.Atoi(args[0])
+ if err != nil {
+ t.err = fmt.Errorf("need number of seconds for -t timeout, got %s instead", args[0])
+ }
+
default:
flags = append(flags, args[0])
}
@@ -539,7 +548,31 @@ func (t *test) run() {
} else {
cmd.Env = os.Environ()
}
- err := cmd.Run()
+
+ var err error
+
+ if tim != 0 {
+ err = cmd.Start()
+ // This command-timeout code adapted from cmd/go/test.go
+ if err == nil {
+ tick := time.NewTimer(time.Duration(tim) * time.Second)
+ done := make(chan error)
+ go func() {
+ done <- cmd.Wait()
+ }()
+ select {
+ case err = <-done:
+ // ok
+ case <-tick.C:
+ cmd.Process.Kill()
+ err = <-done
+ // err = errors.New("Test timeout")
+ }
+ tick.Stop()
+ }
+ } else {
+ err = cmd.Run()
+ }
if err != nil {
err = fmt.Errorf("%s\n%s", err, buf.Bytes())
}
@@ -671,6 +704,32 @@ func (t *test) run() {
t.err = err
}
+ case "buildrun": // build binary, then run binary, instead of go run. Useful for timeout tests where failure mode is infinite loop.
+ // TODO: not supported on NaCl
+ useTmp = true
+ cmd := []string{"go", "build", "-o", "a.exe"}
+ if *linkshared {
+ cmd = append(cmd, "-linkshared")
+ }
+ longdirgofile := filepath.Join(filepath.Join(cwd, t.dir), t.gofile)
+ cmd = append(cmd, flags...)
+ cmd = append(cmd, longdirgofile)
+ out, err := runcmd(cmd...)
+ if err != nil {
+ t.err = err
+ return
+ }
+ cmd = []string{"./a.exe"}
+ out, err = runcmd(append(cmd, args...)...)
+ if err != nil {
+ t.err = err
+ return
+ }
+
+ if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() {
+ t.err = fmt.Errorf("incorrect output\n%s", out)
+ }
+
case "run":
useTmp = false
cmd := []string{"go", "run"}