mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.link] all: merge branch 'master' into dev.link
NOT apply CL 238779, which is for sym.Symbols. Clean merge other than that. Change-Id: I535e9580fcf7d6f382bd684c3d53f11f90d0b6ed
This commit is contained in:
commit
5e526e67e7
70 changed files with 2089 additions and 2929 deletions
3
AUTHORS
3
AUTHORS
|
|
@ -934,7 +934,7 @@ Maya Rashish <maya@netbsd.org>
|
||||||
Mayank Kumar <krmayankk@gmail.com>
|
Mayank Kumar <krmayankk@gmail.com>
|
||||||
MediaMath, Inc
|
MediaMath, Inc
|
||||||
Meir Fischer <meirfischer@gmail.com>
|
Meir Fischer <meirfischer@gmail.com>
|
||||||
Meng Zhuo <mengzhuo1203@gmail.com>
|
Meng Zhuo <mengzhuo1203@gmail.com> <mzh@golangcn.org>
|
||||||
Meteor Development Group
|
Meteor Development Group
|
||||||
Mhd Sulhan <m.shulhan@gmail.com>
|
Mhd Sulhan <m.shulhan@gmail.com>
|
||||||
Micah Stetson <micah.stetson@gmail.com>
|
Micah Stetson <micah.stetson@gmail.com>
|
||||||
|
|
@ -1044,6 +1044,7 @@ Niels Widger <niels.widger@gmail.com>
|
||||||
Nigel Kerr <nigel.kerr@gmail.com>
|
Nigel Kerr <nigel.kerr@gmail.com>
|
||||||
Nik Nyby <nnyby@columbia.edu>
|
Nik Nyby <nnyby@columbia.edu>
|
||||||
Nikhil Benesch <nikhil.benesch@gmail.com>
|
Nikhil Benesch <nikhil.benesch@gmail.com>
|
||||||
|
Nikita Gillmann <nikita@n0.is> <ng0@n0.is>
|
||||||
Niklas Schnelle <niklas.schnelle@gmail.com>
|
Niklas Schnelle <niklas.schnelle@gmail.com>
|
||||||
Niko Dziemba <niko@dziemba.com>
|
Niko Dziemba <niko@dziemba.com>
|
||||||
Nikolay Turpitko <nikolay@turpitko.com>
|
Nikolay Turpitko <nikolay@turpitko.com>
|
||||||
|
|
|
||||||
|
|
@ -1501,7 +1501,7 @@ Maxwell Krohn <themax@gmail.com>
|
||||||
Maya Rashish <maya@NetBSD.org>
|
Maya Rashish <maya@NetBSD.org>
|
||||||
Mayank Kumar <krmayankk@gmail.com>
|
Mayank Kumar <krmayankk@gmail.com>
|
||||||
Meir Fischer <meirfischer@gmail.com>
|
Meir Fischer <meirfischer@gmail.com>
|
||||||
Meng Zhuo <mengzhuo1203@gmail.com>
|
Meng Zhuo <mengzhuo1203@gmail.com> <mzh@golangcn.org>
|
||||||
Mhd Sulhan <m.shulhan@gmail.com>
|
Mhd Sulhan <m.shulhan@gmail.com>
|
||||||
Micah Stetson <micah.stetson@gmail.com>
|
Micah Stetson <micah.stetson@gmail.com>
|
||||||
Michael Anthony Knyszek <mknyszek@google.com>
|
Michael Anthony Knyszek <mknyszek@google.com>
|
||||||
|
|
@ -1654,6 +1654,7 @@ Nigel Kerr <nigel.kerr@gmail.com>
|
||||||
Nigel Tao <nigeltao@golang.org>
|
Nigel Tao <nigeltao@golang.org>
|
||||||
Nik Nyby <nnyby@columbia.edu>
|
Nik Nyby <nnyby@columbia.edu>
|
||||||
Nikhil Benesch <nikhil.benesch@gmail.com>
|
Nikhil Benesch <nikhil.benesch@gmail.com>
|
||||||
|
Nikita Gillmann <nikita@n0.is> <ng0@n0.is>
|
||||||
Nikita Kryuchkov <nkryuchkov10@gmail.com>
|
Nikita Kryuchkov <nkryuchkov10@gmail.com>
|
||||||
Nikita Vanyasin <nikita.vanyasin@gmail.com>
|
Nikita Vanyasin <nikita.vanyasin@gmail.com>
|
||||||
Niklas Schnelle <niklas.schnelle@gmail.com>
|
Niklas Schnelle <niklas.schnelle@gmail.com>
|
||||||
|
|
|
||||||
|
|
@ -79,15 +79,13 @@ release.
|
||||||
<h2 id="Source_code">Source code</h2>
|
<h2 id="Source_code">Source code</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
If you cannot use a release, or prefer to build gccgo for
|
If you cannot use a release, or prefer to build gccgo for yourself, the
|
||||||
yourself,
|
gccgo source code is accessible via Git. The GCC web site has
|
||||||
the gccgo source code is accessible via Subversion. The
|
<a href="https://gcc.gnu.org/git.html">instructions for getting the GCC
|
||||||
GCC web site
|
source code</a>. The gccgo source code is included. As a convenience, a
|
||||||
has <a href="https://gcc.gnu.org/svn.html">instructions for getting the
|
stable version of the Go support is available in the
|
||||||
GCC source code</a>. The gccgo source code is included. As a
|
<code>devel/gccgo</code> branch of the main GCC code repository:
|
||||||
convenience, a stable version of the Go support is available in
|
<code>git://gcc.gnu.org/git/gcc.git</code>.
|
||||||
a branch of the main GCC code
|
|
||||||
repository: <code>svn://gcc.gnu.org/svn/gcc/branches/gccgo</code>.
|
|
||||||
This branch is periodically updated with stable Go compiler sources.
|
This branch is periodically updated with stable Go compiler sources.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
@ -139,13 +137,10 @@ which you have write access):
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
cvs -z 9 -d :pserver:anoncvs@sourceware.org:/cvs/src login
|
git clone git://sourceware.org/git/binutils-gdb.git
|
||||||
[password is "anoncvs"]
|
|
||||||
[The next command will create a directory named src, not binutils]
|
|
||||||
cvs -z 9 -d :pserver:anoncvs@sourceware.org:/cvs/src co binutils
|
|
||||||
mkdir binutils-objdir
|
mkdir binutils-objdir
|
||||||
cd binutils-objdir
|
cd binutils-objdir
|
||||||
../src/configure --enable-gold=default --prefix=/opt/gold
|
../binutils-gdb/configure --enable-gold=default --prefix=/opt/gold
|
||||||
make
|
make
|
||||||
make install
|
make install
|
||||||
</pre>
|
</pre>
|
||||||
|
|
@ -176,7 +171,7 @@ described above):
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
svn checkout svn://gcc.gnu.org/svn/gcc/branches/gccgo gccgo
|
git clone --branch devel/gccgo git://gcc.gnu.org/git/gcc.git gccgo
|
||||||
mkdir objdir
|
mkdir objdir
|
||||||
cd objdir
|
cd objdir
|
||||||
../gccgo/configure --prefix=/opt/gccgo --enable-languages=c,c++,go --with-ld=/opt/gold/bin/ld
|
../gccgo/configure --prefix=/opt/gccgo --enable-languages=c,c++,go --with-ld=/opt/gold/bin/ld
|
||||||
|
|
|
||||||
100
doc/go1.15.html
100
doc/go1.15.html
|
|
@ -50,8 +50,8 @@ Do not send CLs removing the interior tags from such phrases.
|
||||||
<h3 id="windows">Windows</h3>
|
<h3 id="windows">Windows</h3>
|
||||||
|
|
||||||
<p> <!-- CL 214397 and CL 230217 -->
|
<p> <!-- CL 214397 and CL 230217 -->
|
||||||
Go 1.15 now generates Windows ASLR executables when -buildmode=pie
|
Go 1.15 now generates Windows ASLR executables when <code>-buildmode=pie</code>
|
||||||
cmd/link flag is provided. Go command uses -buildmode=pie by default
|
cmd/link flag is provided. Go command uses <code>-buildmode=pie</code> by default
|
||||||
on Windows.
|
on Windows.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
@ -145,7 +145,7 @@ Do not send CLs removing the interior tags from such phrases.
|
||||||
<a href="https://golang.org/issue/36568">issue #36568</a>). The workaround is
|
<a href="https://golang.org/issue/36568">issue #36568</a>). The workaround is
|
||||||
not enabled by default because it is not safe to use when Go versions lower
|
not enabled by default because it is not safe to use when Go versions lower
|
||||||
than 1.14.2 and 1.13.10 are running concurrently with the same module cache.
|
than 1.14.2 and 1.13.10 are running concurrently with the same module cache.
|
||||||
It can be enabled by explictly setting the environment variable
|
It can be enabled by explicitly setting the environment variable
|
||||||
<code>GODEBUG=modcacheunzipinplace=1</code>.
|
<code>GODEBUG=modcacheunzipinplace=1</code>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
@ -313,7 +313,8 @@ Do not send CLs removing the interior tags from such phrases.
|
||||||
<p>
|
<p>
|
||||||
For a representative set of large Go programs, linking is 20% faster
|
For a representative set of large Go programs, linking is 20% faster
|
||||||
and requires 30% less memory on average, for <code>ELF</code>-based
|
and requires 30% less memory on average, for <code>ELF</code>-based
|
||||||
OSes running on <code>amd64</code> architectures, with more modest
|
OSes (Linux, FreeBSD, NetBSD, OpenBSD, Dragonfly, and Solaris)
|
||||||
|
running on <code>amd64</code> architectures, with more modest
|
||||||
improvements for other architecture/OS combinations.
|
improvements for other architecture/OS combinations.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
@ -359,11 +360,11 @@ Do not send CLs removing the interior tags from such phrases.
|
||||||
<h3 id="cgo">Cgo</h3>
|
<h3 id="cgo">Cgo</h3>
|
||||||
|
|
||||||
<p><!-- CL 235817 -->
|
<p><!-- CL 235817 -->
|
||||||
Go 1.15 will translate the C type <code>EGLConfig</code> to the
|
Go 1.15 will translate the C type <code>EGLConfig</code> to the
|
||||||
Go type <code>uintptr</code>. This change is similar to how Go
|
Go type <code>uintptr</code>. This change is similar to how Go
|
||||||
1.12 and newer treats <code>EGLDisplay</code>, Darwin's CoreFoundation and
|
1.12 and newer treats <code>EGLDisplay</code>, Darwin's CoreFoundation and
|
||||||
Java's JNI types. See the <a href="/cmd/cgo/#hdr-Special_cases">cgo
|
Java's JNI types. See the <a href="/cmd/cgo/#hdr-Special_cases">cgo
|
||||||
documentation</a> for more information.
|
documentation</a> for more information.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3 id="minor_library_changes">Minor changes to the library</h3>
|
<h3 id="minor_library_changes">Minor changes to the library</h3>
|
||||||
|
|
@ -374,24 +375,27 @@ Do not send CLs removing the interior tags from such phrases.
|
||||||
in mind.
|
in mind.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<dl id="debug/pe"><dt><a href="/pkg/debug/pe/">debug/pe</a></dt>
|
<dl id="bufio"><dt><a href="/pkg/bufio/">bufio</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<p><!-- CL 222637 -->
|
<p><!-- CL 225357, CL 225557 -->
|
||||||
The package now defines the
|
When a <a href="/pkg/bufio/#Scanner"><code>Scanner</code></a> is
|
||||||
<code>IMAGE_FILE</code>, <code>IMAGE_SUBSYSTEM</code>,
|
used with an invalid
|
||||||
and <code>IMAGE_DLLCHARACTERISTICS</code> constants used by the
|
<a href="/pkg/io/#Reader"><code>io.Reader</code></a> that
|
||||||
PE file format.
|
incorrectly returns a negative number from <code>Read</code>,
|
||||||
|
the <code>Scanner</code> will no longer panic, but will instead
|
||||||
|
return the new error
|
||||||
|
<a href="/pkg/bufio/#ErrBadReadCount"><code>ErrBadReadCount</code></a>.
|
||||||
</p>
|
</p>
|
||||||
</dd>
|
</dd>
|
||||||
</dl><!-- debug/pe -->
|
</dl><!-- bufio -->
|
||||||
|
|
||||||
<dl id="crypto"><dt><a href="/pkg/crypto/">crypto</a></dt>
|
<dl id="crypto"><dt><a href="/pkg/crypto/">crypto</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<p><!-- CL 231417, CL 225460 -->
|
<p><!-- CL 231417, CL 225460 -->
|
||||||
The <code>PrivateKey</code> and <code>PublicKey</code> types in the
|
The <code>PrivateKey</code> and <code>PublicKey</code> types in the
|
||||||
<a href="/pkg/crypto/rsa"><code>crypto/rsa</code></a>,
|
<a href="/pkg/crypto/rsa/"><code>crypto/rsa</code></a>,
|
||||||
<a href="/pkg/crypto/ecdsa"><code>crypto/ecdsa</code></a>, and
|
<a href="/pkg/crypto/ecdsa/"><code>crypto/ecdsa</code></a>, and
|
||||||
<a href="/pkg/crypto/ed25519"><code>crypto/ed25519</code></a> packages
|
<a href="/pkg/crypto/ed25519/"><code>crypto/ed25519</code></a> packages
|
||||||
now have an <code>Equal</code> method to compare keys for equivalence
|
now have an <code>Equal</code> method to compare keys for equivalence
|
||||||
or to make type-safe interfaces for public keys. The method signature
|
or to make type-safe interfaces for public keys. The method signature
|
||||||
is compatible with
|
is compatible with
|
||||||
|
|
@ -551,6 +555,46 @@ Do not send CLs removing the interior tags from such phrases.
|
||||||
</dd>
|
</dd>
|
||||||
</dl><!-- crypto/x509/pkix -->
|
</dl><!-- crypto/x509/pkix -->
|
||||||
|
|
||||||
|
<dl id="database/sql"><dt><a href="/pkg/database/sql/">database/sql</a></dt>
|
||||||
|
<dd>
|
||||||
|
<p><!-- CL 145758 -->
|
||||||
|
The new <a href="/pkg/database/sql/#DB.SetConnMaxIdleTime"><code>DB.SetConnMaxIdleTime</code></a>
|
||||||
|
method allows removing a connection from the connection pool after
|
||||||
|
it has been idle for a period of time, without regard to the total
|
||||||
|
lifespan of the connection. The <a href="/pkg/database/sql/#DBStats.MaxIdleTimeClosed"><code>DBStats.MaxIdleTimeClosed</code></a>
|
||||||
|
field shows the total number of connections closed due to
|
||||||
|
<code>DB.SetConnMaxIdleTime</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><!-- CL 214317 -->
|
||||||
|
The new <a href="/pkg/database/sql/#Row.Err"><code>Row.Err</code></a> getter
|
||||||
|
allows checking for query errors without calling
|
||||||
|
<code>Row.Scan</code>.
|
||||||
|
</p>
|
||||||
|
</dd>
|
||||||
|
</dl><!-- database/sql -->
|
||||||
|
|
||||||
|
<dl id="database/sql/driver"><dt><a href="/pkg/database/sql/driver/">database/sql/driver</a></dt>
|
||||||
|
<dd>
|
||||||
|
<p><!-- CL 174122 -->
|
||||||
|
The new <a href="/pkg/database/sql/driver/#Validator"><code>Validator</code></a>
|
||||||
|
interface may be implemented by <code>Conn</code> to allow drivers
|
||||||
|
to signal if a connection is valid or if it should be discarded.
|
||||||
|
</p>
|
||||||
|
</dd>
|
||||||
|
</dl><!-- database/sql/driver -->
|
||||||
|
|
||||||
|
<dl id="debug/pe"><dt><a href="/pkg/debug/pe/">debug/pe</a></dt>
|
||||||
|
<dd>
|
||||||
|
<p><!-- CL 222637 -->
|
||||||
|
The package now defines the
|
||||||
|
<code>IMAGE_FILE</code>, <code>IMAGE_SUBSYSTEM</code>,
|
||||||
|
and <code>IMAGE_DLLCHARACTERISTICS</code> constants used by the
|
||||||
|
PE file format.
|
||||||
|
</p>
|
||||||
|
</dd>
|
||||||
|
</dl><!-- debug/pe -->
|
||||||
|
|
||||||
<dl id="encoding/json"><dt><a href="/pkg/encoding/json/">encoding/json</a></dt>
|
<dl id="encoding/json"><dt><a href="/pkg/encoding/json/">encoding/json</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<p><!-- CL 191783 -->
|
<p><!-- CL 191783 -->
|
||||||
|
|
@ -604,6 +648,16 @@ Do not send CLs removing the interior tags from such phrases.
|
||||||
</dd>
|
</dd>
|
||||||
</dl><!-- fmt -->
|
</dl><!-- fmt -->
|
||||||
|
|
||||||
|
<dl id="go/printer"><dt><a href="/pkg/go/printer/">go/printer</a></dt>
|
||||||
|
<dd>
|
||||||
|
<p><!-- CL 231461 -->
|
||||||
|
The new <a href="/pkg/go/printer/#Mode"><code>Mode</code></a>
|
||||||
|
value <a href="/pkg/go/printer/#StdFormat"><code>StdFormat</code></a>
|
||||||
|
directs the printer to apply standard formatting changes while
|
||||||
|
printing the output.
|
||||||
|
</dd>
|
||||||
|
</dl><!-- go/printer -->
|
||||||
|
|
||||||
<dl id="io/ioutil"><dt><a href="/pkg/io/ioutil/">io/ioutil</a></dt>
|
<dl id="io/ioutil"><dt><a href="/pkg/io/ioutil/">io/ioutil</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<p><!-- CL 212597 -->
|
<p><!-- CL 212597 -->
|
||||||
|
|
@ -633,7 +687,7 @@ Do not send CLs removing the interior tags from such phrases.
|
||||||
<code>Conn.SetReadDeadline</code>,
|
<code>Conn.SetReadDeadline</code>,
|
||||||
or <code>Conn.SetWriteDeadline</code> methods, it will now
|
or <code>Conn.SetWriteDeadline</code> methods, it will now
|
||||||
return an error that is or wraps
|
return an error that is or wraps
|
||||||
<a href="/pkg/os#ErrDeadlineExceeded"><code>os.ErrDeadlineExceeded</code></a>.
|
<a href="/pkg/os/#ErrDeadlineExceeded"><code>os.ErrDeadlineExceeded</code></a>.
|
||||||
This may be used to reliably detect whether an error is due to
|
This may be used to reliably detect whether an error is due to
|
||||||
an exceeded deadline.
|
an exceeded deadline.
|
||||||
Earlier releases recommended calling the <code>Timeout</code>
|
Earlier releases recommended calling the <code>Timeout</code>
|
||||||
|
|
@ -712,7 +766,7 @@ Do not send CLs removing the interior tags from such phrases.
|
||||||
<a href="/pkg/os/#File.SetReadDeadline"><code>File.SetReadDeadline</code></a>,
|
<a href="/pkg/os/#File.SetReadDeadline"><code>File.SetReadDeadline</code></a>,
|
||||||
or <a href="/pkg/os/#File.SetWriteDeadline"><code>File.SetWriteDeadline</code></a>
|
or <a href="/pkg/os/#File.SetWriteDeadline"><code>File.SetWriteDeadline</code></a>
|
||||||
methods, it will now return an error that is or wraps
|
methods, it will now return an error that is or wraps
|
||||||
<a href="/pkg/os#ErrDeadlineExceeded"><code>os.ErrDeadlineExceeded</code></a>.
|
<a href="/pkg/os/#ErrDeadlineExceeded"><code>os.ErrDeadlineExceeded</code></a>.
|
||||||
This may be used to reliably detect whether an error is due to
|
This may be used to reliably detect whether an error is due to
|
||||||
an exceeded deadline.
|
an exceeded deadline.
|
||||||
Earlier releases recommended calling the <code>Timeout</code>
|
Earlier releases recommended calling the <code>Timeout</code>
|
||||||
|
|
@ -778,7 +832,7 @@ Do not send CLs removing the interior tags from such phrases.
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<dl id="pkg-runtime-pprof"><dt><a href="/pkg/runtime/pprof">runtime/pprof</a></dt>
|
<dl id="pkg-runtime-pprof"><dt><a href="/pkg/runtime/pprof/">runtime/pprof</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<p><!-- CL 189318 -->
|
<p><!-- CL 189318 -->
|
||||||
The goroutine profile includes the profile labels associated with each goroutine
|
The goroutine profile includes the profile labels associated with each goroutine
|
||||||
|
|
@ -797,7 +851,7 @@ Do not send CLs removing the interior tags from such phrases.
|
||||||
<a href="/pkg/strconv/#FormatComplex"><code>FormatComplex</code></a> converts a complex number into a string of the form (a+bi), where a and b are the real and imaginary parts.
|
<a href="/pkg/strconv/#FormatComplex"><code>FormatComplex</code></a> converts a complex number into a string of the form (a+bi), where a and b are the real and imaginary parts.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="/pkg/strconv/#ParseComplex"><code>ParseComplex</code></a> converts a string into a complex number of a specificed precision. <code>ParseComplex</code> accepts complex numbers in the format <code>N+Ni</code>.
|
<a href="/pkg/strconv/#ParseComplex"><code>ParseComplex</code></a> converts a string into a complex number of a specified precision. <code>ParseComplex</code> accepts complex numbers in the format <code>N+Ni</code>.
|
||||||
</p>
|
</p>
|
||||||
</dd>
|
</dd>
|
||||||
</dl><!-- strconv -->
|
</dl><!-- strconv -->
|
||||||
|
|
|
||||||
12
misc/cgo/testgodefs/testdata/issue39534.go
vendored
Normal file
12
misc/cgo/testgodefs/testdata/issue39534.go
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
//
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// enum { ENUMVAL = 0x1 };
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
const ENUMVAL = C.ENUMVAL
|
||||||
|
|
@ -24,6 +24,7 @@ var filePrefixes = []string{
|
||||||
"issue37479",
|
"issue37479",
|
||||||
"issue37621",
|
"issue37621",
|
||||||
"issue38649",
|
"issue38649",
|
||||||
|
"issue39534",
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGoDefs(t *testing.T) {
|
func TestGoDefs(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -558,8 +558,8 @@ func (r *negativeEOFReader) Read(p []byte) (int, error) {
|
||||||
return -1, io.EOF
|
return -1, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the scanner doesn't panic on a reader that returns a
|
// Test that the scanner doesn't panic and returns ErrBadReadCount
|
||||||
// negative count of bytes read (issue 38053).
|
// on a reader that returns a negative count of bytes read (issue 38053).
|
||||||
func TestNegativeEOFReader(t *testing.T) {
|
func TestNegativeEOFReader(t *testing.T) {
|
||||||
r := negativeEOFReader(10)
|
r := negativeEOFReader(10)
|
||||||
scanner := NewScanner(&r)
|
scanner := NewScanner(&r)
|
||||||
|
|
@ -571,8 +571,8 @@ func TestNegativeEOFReader(t *testing.T) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if scanner.Err() == nil {
|
if got, want := scanner.Err(), ErrBadReadCount; got != want {
|
||||||
t.Error("scanner.Err returned nil, expected an error")
|
t.Errorf("scanner.Err: got %v, want %v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -584,11 +584,13 @@ func (largeReader) Read(p []byte) (int, error) {
|
||||||
return len(p) + 1, nil
|
return len(p) + 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that the scanner doesn't panic and returns ErrBadReadCount
|
||||||
|
// on a reader that returns an impossibly large count of bytes read (issue 38053).
|
||||||
func TestLargeReader(t *testing.T) {
|
func TestLargeReader(t *testing.T) {
|
||||||
scanner := NewScanner(largeReader{})
|
scanner := NewScanner(largeReader{})
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
}
|
}
|
||||||
if scanner.Err() == nil {
|
if got, want := scanner.Err(), ErrBadReadCount; got != want {
|
||||||
t.Error("scanner.Err returned nil, expected an error")
|
t.Errorf("scanner.Err: got %v, want %v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1354,7 +1354,7 @@ func (p *Package) rewriteRef(f *File) {
|
||||||
|
|
||||||
if *godefs {
|
if *godefs {
|
||||||
// Substitute definition for mangled type name.
|
// Substitute definition for mangled type name.
|
||||||
if r.Name.Type != nil {
|
if r.Name.Type != nil && r.Name.Kind == "type" {
|
||||||
expr = r.Name.Type.Go
|
expr = r.Name.Type.Go
|
||||||
}
|
}
|
||||||
if id, ok := expr.(*ast.Ident); ok {
|
if id, ok := expr.(*ast.Ident); ok {
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@ var knownFormats = map[string]string{
|
||||||
"cmd/compile/internal/ssa.Sym %v": "",
|
"cmd/compile/internal/ssa.Sym %v": "",
|
||||||
"cmd/compile/internal/ssa.ValAndOff %s": "",
|
"cmd/compile/internal/ssa.ValAndOff %s": "",
|
||||||
"cmd/compile/internal/ssa.domain %v": "",
|
"cmd/compile/internal/ssa.domain %v": "",
|
||||||
|
"cmd/compile/internal/ssa.flagConstant %s": "",
|
||||||
"cmd/compile/internal/ssa.posetNode %v": "",
|
"cmd/compile/internal/ssa.posetNode %v": "",
|
||||||
"cmd/compile/internal/ssa.posetTestOp %v": "",
|
"cmd/compile/internal/ssa.posetTestOp %v": "",
|
||||||
"cmd/compile/internal/ssa.rbrank %d": "",
|
"cmd/compile/internal/ssa.rbrank %d": "",
|
||||||
|
|
|
||||||
|
|
@ -857,12 +857,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
|
||||||
p := s.Prog(obj.AGETCALLERPC)
|
p := s.Prog(obj.AGETCALLERPC)
|
||||||
p.To.Type = obj.TYPE_REG
|
p.To.Type = obj.TYPE_REG
|
||||||
p.To.Reg = v.Reg()
|
p.To.Reg = v.Reg()
|
||||||
case ssa.OpARMFlagEQ,
|
case ssa.OpARMFlagConstant:
|
||||||
ssa.OpARMFlagLT_ULT,
|
v.Fatalf("FlagConstant op should never make it to codegen %v", v.LongString())
|
||||||
ssa.OpARMFlagLT_UGT,
|
|
||||||
ssa.OpARMFlagGT_ULT,
|
|
||||||
ssa.OpARMFlagGT_UGT:
|
|
||||||
v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
|
|
||||||
case ssa.OpARMInvertFlags:
|
case ssa.OpARMInvertFlags:
|
||||||
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
|
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
|
||||||
case ssa.OpClobber:
|
case ssa.OpClobber:
|
||||||
|
|
|
||||||
|
|
@ -943,12 +943,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
|
||||||
p := s.Prog(obj.AGETCALLERPC)
|
p := s.Prog(obj.AGETCALLERPC)
|
||||||
p.To.Type = obj.TYPE_REG
|
p.To.Type = obj.TYPE_REG
|
||||||
p.To.Reg = v.Reg()
|
p.To.Reg = v.Reg()
|
||||||
case ssa.OpARM64FlagEQ,
|
case ssa.OpARM64FlagConstant:
|
||||||
ssa.OpARM64FlagLT_ULT,
|
v.Fatalf("FlagConstant op should never make it to codegen %v", v.LongString())
|
||||||
ssa.OpARM64FlagLT_UGT,
|
|
||||||
ssa.OpARM64FlagGT_ULT,
|
|
||||||
ssa.OpARM64FlagGT_UGT:
|
|
||||||
v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
|
|
||||||
case ssa.OpARM64InvertFlags:
|
case ssa.OpARM64InvertFlags:
|
||||||
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
|
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
|
||||||
case ssa.OpClobber:
|
case ssa.OpClobber:
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,26 @@ func IncomparableField(t *types.Type) *types.Field {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EqCanPanic reports whether == on type t could panic (has an interface somewhere).
|
||||||
|
// t must be comparable.
|
||||||
|
func EqCanPanic(t *types.Type) bool {
|
||||||
|
switch t.Etype {
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
case TINTER:
|
||||||
|
return true
|
||||||
|
case TARRAY:
|
||||||
|
return EqCanPanic(t.Elem())
|
||||||
|
case TSTRUCT:
|
||||||
|
for _, f := range t.FieldSlice() {
|
||||||
|
if !f.Sym.IsBlank() && EqCanPanic(f.Type) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// algtype is like algtype1, except it returns the fixed-width AMEMxx variants
|
// algtype is like algtype1, except it returns the fixed-width AMEMxx variants
|
||||||
// instead of the general AMEM kind when possible.
|
// instead of the general AMEM kind when possible.
|
||||||
func algtype(t *types.Type) AlgKind {
|
func algtype(t *types.Type) AlgKind {
|
||||||
|
|
@ -624,14 +644,19 @@ func geneq(t *types.Type) *obj.LSym {
|
||||||
|
|
||||||
case TSTRUCT:
|
case TSTRUCT:
|
||||||
// Build a list of conditions to satisfy.
|
// Build a list of conditions to satisfy.
|
||||||
// Track their order so that we can preserve aspects of that order.
|
// The conditions are a list-of-lists. Conditions are reorderable
|
||||||
|
// within each inner list. The outer lists must be evaluated in order.
|
||||||
|
// Even within each inner list, track their order so that we can preserve
|
||||||
|
// aspects of that order. (TODO: latter part needed?)
|
||||||
type nodeIdx struct {
|
type nodeIdx struct {
|
||||||
n *Node
|
n *Node
|
||||||
idx int
|
idx int
|
||||||
}
|
}
|
||||||
var conds []nodeIdx
|
var conds [][]nodeIdx
|
||||||
|
conds = append(conds, []nodeIdx{})
|
||||||
and := func(n *Node) {
|
and := func(n *Node) {
|
||||||
conds = append(conds, nodeIdx{n: n, idx: len(conds)})
|
i := len(conds) - 1
|
||||||
|
conds[i] = append(conds[i], nodeIdx{n: n, idx: len(conds[i])})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk the struct using memequal for runs of AMEM
|
// Walk the struct using memequal for runs of AMEM
|
||||||
|
|
@ -647,6 +672,10 @@ func geneq(t *types.Type) *obj.LSym {
|
||||||
|
|
||||||
// Compare non-memory fields with field equality.
|
// Compare non-memory fields with field equality.
|
||||||
if !IsRegularMemory(f.Type) {
|
if !IsRegularMemory(f.Type) {
|
||||||
|
if EqCanPanic(f.Type) {
|
||||||
|
// Enforce ordering by starting a new set of reorderable conditions.
|
||||||
|
conds = append(conds, []nodeIdx{})
|
||||||
|
}
|
||||||
p := nodSym(OXDOT, np, f.Sym)
|
p := nodSym(OXDOT, np, f.Sym)
|
||||||
q := nodSym(OXDOT, nq, f.Sym)
|
q := nodSym(OXDOT, nq, f.Sym)
|
||||||
switch {
|
switch {
|
||||||
|
|
@ -657,6 +686,10 @@ func geneq(t *types.Type) *obj.LSym {
|
||||||
default:
|
default:
|
||||||
and(nod(OEQ, p, q))
|
and(nod(OEQ, p, q))
|
||||||
}
|
}
|
||||||
|
if EqCanPanic(f.Type) {
|
||||||
|
// Also enforce ordering after something that can panic.
|
||||||
|
conds = append(conds, []nodeIdx{})
|
||||||
|
}
|
||||||
i++
|
i++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -680,20 +713,24 @@ func geneq(t *types.Type) *obj.LSym {
|
||||||
|
|
||||||
// Sort conditions to put runtime calls last.
|
// Sort conditions to put runtime calls last.
|
||||||
// Preserve the rest of the ordering.
|
// Preserve the rest of the ordering.
|
||||||
sort.SliceStable(conds, func(i, j int) bool {
|
var flatConds []nodeIdx
|
||||||
x, y := conds[i], conds[j]
|
for _, c := range conds {
|
||||||
if (x.n.Op != OCALL) == (y.n.Op != OCALL) {
|
sort.SliceStable(c, func(i, j int) bool {
|
||||||
return x.idx < y.idx
|
x, y := c[i], c[j]
|
||||||
}
|
if (x.n.Op != OCALL) == (y.n.Op != OCALL) {
|
||||||
return x.n.Op != OCALL
|
return x.idx < y.idx
|
||||||
})
|
}
|
||||||
|
return x.n.Op != OCALL
|
||||||
|
})
|
||||||
|
flatConds = append(flatConds, c...)
|
||||||
|
}
|
||||||
|
|
||||||
var cond *Node
|
var cond *Node
|
||||||
if len(conds) == 0 {
|
if len(flatConds) == 0 {
|
||||||
cond = nodbool(true)
|
cond = nodbool(true)
|
||||||
} else {
|
} else {
|
||||||
cond = conds[0].n
|
cond = flatConds[0].n
|
||||||
for _, c := range conds[1:] {
|
for _, c := range flatConds[1:] {
|
||||||
cond = nod(OANDAND, cond, c.n)
|
cond = nod(OANDAND, cond, c.n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -680,8 +680,9 @@ func (lv *Liveness) pointerMap(liveout bvec, vars []*Node, args, locals bvec) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// markUnsafePoints finds unsafe points and computes lv.unsafePoints.
|
// allUnsafe indicates that all points in this function are
|
||||||
func (lv *Liveness) markUnsafePoints() {
|
// unsafe-points.
|
||||||
|
func allUnsafe(f *ssa.Func) bool {
|
||||||
// The runtime assumes the only safe-points are function
|
// The runtime assumes the only safe-points are function
|
||||||
// prologues (because that's how it used to be). We could and
|
// prologues (because that's how it used to be). We could and
|
||||||
// should improve that, but for now keep consider all points
|
// should improve that, but for now keep consider all points
|
||||||
|
|
@ -691,7 +692,12 @@ func (lv *Liveness) markUnsafePoints() {
|
||||||
// go:nosplit functions are similar. Since safe points used to
|
// go:nosplit functions are similar. Since safe points used to
|
||||||
// be coupled with stack checks, go:nosplit often actually
|
// be coupled with stack checks, go:nosplit often actually
|
||||||
// means "no safe points in this function".
|
// means "no safe points in this function".
|
||||||
if compiling_runtime || lv.f.NoSplit {
|
return compiling_runtime || f.NoSplit
|
||||||
|
}
|
||||||
|
|
||||||
|
// markUnsafePoints finds unsafe points and computes lv.unsafePoints.
|
||||||
|
func (lv *Liveness) markUnsafePoints() {
|
||||||
|
if allUnsafe(lv.f) {
|
||||||
// No complex analysis necessary.
|
// No complex analysis necessary.
|
||||||
lv.allUnsafe = true
|
lv.allUnsafe = true
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -6010,8 +6010,8 @@ func genssa(f *ssa.Func, pp *Progs) {
|
||||||
// for an empty block this will be used for its control
|
// for an empty block this will be used for its control
|
||||||
// instruction. We won't use the actual liveness map on a
|
// instruction. We won't use the actual liveness map on a
|
||||||
// control instruction. Just mark it something that is
|
// control instruction. Just mark it something that is
|
||||||
// preemptible.
|
// preemptible, unless this function is "all unsafe".
|
||||||
s.pp.nextLive = LivenessIndex{-1, -1, false}
|
s.pp.nextLive = LivenessIndex{-1, -1, allUnsafe(f)}
|
||||||
|
|
||||||
// Emit values in block
|
// Emit values in block
|
||||||
thearch.SSAMarkMoves(&s, b)
|
thearch.SSAMarkMoves(&s, b)
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,11 @@ func checkFunc(f *Func) {
|
||||||
f.Fatalf("bad type %T for S390XRotateParams in %v", v.Aux, v)
|
f.Fatalf("bad type %T for S390XRotateParams in %v", v.Aux, v)
|
||||||
}
|
}
|
||||||
canHaveAux = true
|
canHaveAux = true
|
||||||
|
case auxFlagConstant:
|
||||||
|
if v.AuxInt < 0 || v.AuxInt > 15 {
|
||||||
|
f.Fatalf("bad FlagConstant AuxInt value for %v", v)
|
||||||
|
}
|
||||||
|
canHaveAuxInt = true
|
||||||
default:
|
default:
|
||||||
f.Fatalf("unknown aux type for %s", v.Op)
|
f.Fatalf("unknown aux type for %s", v.Op)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,11 +49,11 @@ var gogcflags = os.Getenv("GO_GCFLAGS")
|
||||||
// optimizedLibs usually means "not running in a noopt test builder".
|
// optimizedLibs usually means "not running in a noopt test builder".
|
||||||
var optimizedLibs = (!strings.Contains(gogcflags, "-N") && !strings.Contains(gogcflags, "-l"))
|
var optimizedLibs = (!strings.Contains(gogcflags, "-N") && !strings.Contains(gogcflags, "-l"))
|
||||||
|
|
||||||
// TestNexting go-builds a file, then uses a debugger (default gdb, optionally delve)
|
// TestNexting go-builds a file, then uses a debugger (default delve, optionally gdb)
|
||||||
// to next through the generated executable, recording each line landed at, and
|
// to next through the generated executable, recording each line landed at, and
|
||||||
// then compares those lines with reference file(s).
|
// then compares those lines with reference file(s).
|
||||||
// Flag -u updates the reference file(s).
|
// Flag -u updates the reference file(s).
|
||||||
// Flag -d changes the debugger to delve (and uses delve-specific reference files)
|
// Flag -g changes the debugger to gdb (and uses gdb-specific reference files)
|
||||||
// Flag -v is ever-so-slightly verbose.
|
// Flag -v is ever-so-slightly verbose.
|
||||||
// Flag -n is for dry-run, and prints the shell and first debug commands.
|
// Flag -n is for dry-run, and prints the shell and first debug commands.
|
||||||
//
|
//
|
||||||
|
|
@ -83,9 +83,9 @@ var optimizedLibs = (!strings.Contains(gogcflags, "-N") && !strings.Contains(gog
|
||||||
// to indicate normalization of Strings, (hex) addresses, and numbers.
|
// to indicate normalization of Strings, (hex) addresses, and numbers.
|
||||||
// "O" is an explicit indication that we expect it to be optimized out.
|
// "O" is an explicit indication that we expect it to be optimized out.
|
||||||
// For example:
|
// For example:
|
||||||
/*
|
//
|
||||||
if len(os.Args) > 1 { //gdb-dbg=(hist/A,cannedInput/A) //dlv-dbg=(hist/A,cannedInput/A)
|
// if len(os.Args) > 1 { //gdb-dbg=(hist/A,cannedInput/A) //dlv-dbg=(hist/A,cannedInput/A)
|
||||||
*/
|
//
|
||||||
// TODO: not implemented for Delve yet, but this is the plan
|
// TODO: not implemented for Delve yet, but this is the plan
|
||||||
//
|
//
|
||||||
// After a compiler change that causes a difference in the debug behavior, check
|
// After a compiler change that causes a difference in the debug behavior, check
|
||||||
|
|
@ -93,7 +93,7 @@ var optimizedLibs = (!strings.Contains(gogcflags, "-N") && !strings.Contains(gog
|
||||||
// go test debug_test.go -args -u
|
// go test debug_test.go -args -u
|
||||||
// (for Delve)
|
// (for Delve)
|
||||||
// go test debug_test.go -args -u -d
|
// go test debug_test.go -args -u -d
|
||||||
|
//
|
||||||
func TestNexting(t *testing.T) {
|
func TestNexting(t *testing.T) {
|
||||||
testenv.SkipFlaky(t, 37404)
|
testenv.SkipFlaky(t, 37404)
|
||||||
|
|
||||||
|
|
@ -110,7 +110,13 @@ func TestNexting(t *testing.T) {
|
||||||
// Various architectures tend to differ slightly sometimes, and keeping them
|
// Various architectures tend to differ slightly sometimes, and keeping them
|
||||||
// all in sync is a pain for people who don't have them all at hand,
|
// all in sync is a pain for people who don't have them all at hand,
|
||||||
// so limit testing to amd64 (for now)
|
// so limit testing to amd64 (for now)
|
||||||
skipReasons += "not run when testing gdb (-g) unless forced (-f) or linux-amd64"
|
skipReasons += "not run when testing gdb (-g) unless forced (-f) or linux-amd64; "
|
||||||
|
}
|
||||||
|
|
||||||
|
if !*useGdb && !*force && testenv.Builder() == "linux-386-longtest" {
|
||||||
|
// The latest version of Delve does support linux/386. However, the version currently
|
||||||
|
// installed in the linux-386-longtest builder does not. See golang.org/issue/39309.
|
||||||
|
skipReasons += "not run when testing delve on linux-386-longtest builder unless forced (-f); "
|
||||||
}
|
}
|
||||||
|
|
||||||
if *useGdb {
|
if *useGdb {
|
||||||
|
|
|
||||||
31
src/cmd/compile/internal/ssa/flags_amd64_test.s
Normal file
31
src/cmd/compile/internal/ssa/flags_amd64_test.s
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build amd64
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·asmAddFlags(SB),NOSPLIT,$0-24
|
||||||
|
MOVQ x+0(FP), AX
|
||||||
|
ADDQ y+8(FP), AX
|
||||||
|
PUSHFQ
|
||||||
|
POPQ AX
|
||||||
|
MOVQ AX, ret+16(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·asmSubFlags(SB),NOSPLIT,$0-24
|
||||||
|
MOVQ x+0(FP), AX
|
||||||
|
SUBQ y+8(FP), AX
|
||||||
|
PUSHFQ
|
||||||
|
POPQ AX
|
||||||
|
MOVQ AX, ret+16(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·asmAndFlags(SB),NOSPLIT,$0-24
|
||||||
|
MOVQ x+0(FP), AX
|
||||||
|
ANDQ y+8(FP), AX
|
||||||
|
PUSHFQ
|
||||||
|
POPQ AX
|
||||||
|
MOVQ AX, ret+16(FP)
|
||||||
|
RET
|
||||||
32
src/cmd/compile/internal/ssa/flags_arm64_test.s
Normal file
32
src/cmd/compile/internal/ssa/flags_arm64_test.s
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build arm64
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·asmAddFlags(SB),NOSPLIT,$0-24
|
||||||
|
MOVD x+0(FP), R0
|
||||||
|
MOVD y+8(FP), R1
|
||||||
|
CMN R0, R1
|
||||||
|
WORD $0xd53b4200 // MOVD NZCV, R0
|
||||||
|
MOVD R0, ret+16(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·asmSubFlags(SB),NOSPLIT,$0-24
|
||||||
|
MOVD x+0(FP), R0
|
||||||
|
MOVD y+8(FP), R1
|
||||||
|
CMP R1, R0
|
||||||
|
WORD $0xd53b4200 // MOVD NZCV, R0
|
||||||
|
MOVD R0, ret+16(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·asmAndFlags(SB),NOSPLIT,$0-24
|
||||||
|
MOVD x+0(FP), R0
|
||||||
|
MOVD y+8(FP), R1
|
||||||
|
TST R1, R0
|
||||||
|
WORD $0xd53b4200 // MOVD NZCV, R0
|
||||||
|
BIC $0x30000000, R0 // clear C, V bits, as TST does not change those flags
|
||||||
|
MOVD R0, ret+16(FP)
|
||||||
|
RET
|
||||||
108
src/cmd/compile/internal/ssa/flags_test.go
Normal file
108
src/cmd/compile/internal/ssa/flags_test.go
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build amd64 arm64
|
||||||
|
|
||||||
|
package ssa
|
||||||
|
|
||||||
|
// This file tests the functions addFlags64 and subFlags64 by comparing their
|
||||||
|
// results to what the chip calculates.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAddFlagsNative(t *testing.T) {
|
||||||
|
var numbers = []int64{
|
||||||
|
1, 0, -1,
|
||||||
|
2, -2,
|
||||||
|
1<<63 - 1, -1 << 63,
|
||||||
|
}
|
||||||
|
coverage := map[flagConstant]bool{}
|
||||||
|
for _, x := range numbers {
|
||||||
|
for _, y := range numbers {
|
||||||
|
a := addFlags64(x, y)
|
||||||
|
b := flagRegister2flagConstant(asmAddFlags(x, y), false)
|
||||||
|
if a != b {
|
||||||
|
t.Errorf("asmAdd diff: x=%x y=%x got=%s want=%s\n", x, y, a, b)
|
||||||
|
}
|
||||||
|
coverage[a] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(coverage) != 9 { // TODO: can we cover all outputs?
|
||||||
|
t.Errorf("coverage too small, got %d want 9", len(coverage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubFlagsNative(t *testing.T) {
|
||||||
|
var numbers = []int64{
|
||||||
|
1, 0, -1,
|
||||||
|
2, -2,
|
||||||
|
1<<63 - 1, -1 << 63,
|
||||||
|
}
|
||||||
|
coverage := map[flagConstant]bool{}
|
||||||
|
for _, x := range numbers {
|
||||||
|
for _, y := range numbers {
|
||||||
|
a := subFlags64(x, y)
|
||||||
|
b := flagRegister2flagConstant(asmSubFlags(x, y), true)
|
||||||
|
if a != b {
|
||||||
|
t.Errorf("asmSub diff: x=%x y=%x got=%s want=%s\n", x, y, a, b)
|
||||||
|
}
|
||||||
|
coverage[a] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(coverage) != 7 { // TODO: can we cover all outputs?
|
||||||
|
t.Errorf("coverage too small, got %d want 7", len(coverage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAndFlagsNative(t *testing.T) {
|
||||||
|
var numbers = []int64{
|
||||||
|
1, 0, -1,
|
||||||
|
2, -2,
|
||||||
|
1<<63 - 1, -1 << 63,
|
||||||
|
}
|
||||||
|
coverage := map[flagConstant]bool{}
|
||||||
|
for _, x := range numbers {
|
||||||
|
for _, y := range numbers {
|
||||||
|
a := logicFlags64(x & y)
|
||||||
|
b := flagRegister2flagConstant(asmAndFlags(x, y), false)
|
||||||
|
if a != b {
|
||||||
|
t.Errorf("asmAnd diff: x=%x y=%x got=%s want=%s\n", x, y, a, b)
|
||||||
|
}
|
||||||
|
coverage[a] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(coverage) != 3 {
|
||||||
|
t.Errorf("coverage too small, got %d want 3", len(coverage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func asmAddFlags(x, y int64) int
|
||||||
|
func asmSubFlags(x, y int64) int
|
||||||
|
func asmAndFlags(x, y int64) int
|
||||||
|
|
||||||
|
func flagRegister2flagConstant(x int, sub bool) flagConstant {
|
||||||
|
var fcb flagConstantBuilder
|
||||||
|
switch runtime.GOARCH {
|
||||||
|
case "amd64":
|
||||||
|
fcb.Z = x>>6&1 != 0
|
||||||
|
fcb.N = x>>7&1 != 0
|
||||||
|
fcb.C = x>>0&1 != 0
|
||||||
|
if sub {
|
||||||
|
// Convert from amd64-sense to arm-sense
|
||||||
|
fcb.C = !fcb.C
|
||||||
|
}
|
||||||
|
fcb.V = x>>11&1 != 0
|
||||||
|
case "arm64":
|
||||||
|
fcb.Z = x>>30&1 != 0
|
||||||
|
fcb.N = x>>31&1 != 0
|
||||||
|
fcb.C = x>>29&1 != 0
|
||||||
|
fcb.V = x>>28&1 != 0
|
||||||
|
default:
|
||||||
|
panic("unsupported architecture: " + runtime.GOARCH)
|
||||||
|
}
|
||||||
|
return fcb.encode()
|
||||||
|
}
|
||||||
|
|
@ -609,89 +609,59 @@
|
||||||
(Select1 (CALLudiv x (MOVWconst [c]))) && isPowerOfTwo(c) -> (ANDconst [c-1] x)
|
(Select1 (CALLudiv x (MOVWconst [c]))) && isPowerOfTwo(c) -> (ANDconst [c-1] x)
|
||||||
|
|
||||||
// constant comparisons
|
// constant comparisons
|
||||||
(CMPconst (MOVWconst [x]) [y]) && int32(x)==int32(y) -> (FlagEQ)
|
(CMPconst (MOVWconst [x]) [y]) => (FlagConstant [subFlags32(x,y)])
|
||||||
(CMPconst (MOVWconst [x]) [y]) && int32(x)<int32(y) && uint32(x)<uint32(y) -> (FlagLT_ULT)
|
(CMNconst (MOVWconst [x]) [y]) => (FlagConstant [addFlags32(x,y)])
|
||||||
(CMPconst (MOVWconst [x]) [y]) && int32(x)<int32(y) && uint32(x)>uint32(y) -> (FlagLT_UGT)
|
(TSTconst (MOVWconst [x]) [y]) => (FlagConstant [logicFlags32(x&y)])
|
||||||
(CMPconst (MOVWconst [x]) [y]) && int32(x)>int32(y) && uint32(x)<uint32(y) -> (FlagGT_ULT)
|
(TEQconst (MOVWconst [x]) [y]) => (FlagConstant [logicFlags32(x^y)])
|
||||||
(CMPconst (MOVWconst [x]) [y]) && int32(x)>int32(y) && uint32(x)>uint32(y) -> (FlagGT_UGT)
|
|
||||||
(CMNconst (MOVWconst [x]) [y]) && int32(x)==int32(-y) -> (FlagEQ)
|
|
||||||
(CMNconst (MOVWconst [x]) [y]) && int32(x)<int32(-y) && uint32(x)<uint32(-y) -> (FlagLT_ULT)
|
|
||||||
(CMNconst (MOVWconst [x]) [y]) && int32(x)<int32(-y) && uint32(x)>uint32(-y) -> (FlagLT_UGT)
|
|
||||||
(CMNconst (MOVWconst [x]) [y]) && int32(x)>int32(-y) && uint32(x)<uint32(-y) -> (FlagGT_ULT)
|
|
||||||
(CMNconst (MOVWconst [x]) [y]) && int32(x)>int32(-y) && uint32(x)>uint32(-y) -> (FlagGT_UGT)
|
|
||||||
(TSTconst (MOVWconst [x]) [y]) && int32(x&y)==0 -> (FlagEQ)
|
|
||||||
(TSTconst (MOVWconst [x]) [y]) && int32(x&y)<0 -> (FlagLT_UGT)
|
|
||||||
(TSTconst (MOVWconst [x]) [y]) && int32(x&y)>0 -> (FlagGT_UGT)
|
|
||||||
(TEQconst (MOVWconst [x]) [y]) && int32(x^y)==0 -> (FlagEQ)
|
|
||||||
(TEQconst (MOVWconst [x]) [y]) && int32(x^y)<0 -> (FlagLT_UGT)
|
|
||||||
(TEQconst (MOVWconst [x]) [y]) && int32(x^y)>0 -> (FlagGT_UGT)
|
|
||||||
|
|
||||||
// other known comparisons
|
// other known comparisons
|
||||||
(CMPconst (MOVBUreg _) [c]) && 0xff < c -> (FlagLT_ULT)
|
(CMPconst (MOVBUreg _) [c]) && 0xff < c => (FlagConstant [subFlags32(0, 1)])
|
||||||
(CMPconst (MOVHUreg _) [c]) && 0xffff < c -> (FlagLT_ULT)
|
(CMPconst (MOVHUreg _) [c]) && 0xffff < c => (FlagConstant [subFlags32(0, 1)])
|
||||||
(CMPconst (ANDconst _ [m]) [n]) && 0 <= int32(m) && int32(m) < int32(n) -> (FlagLT_ULT)
|
(CMPconst (ANDconst _ [m]) [n]) && 0 <= m && m < n => (FlagConstant [subFlags32(0, 1)])
|
||||||
(CMPconst (SRLconst _ [c]) [n]) && 0 <= n && 0 < c && c <= 32 && (1<<uint32(32-c)) <= uint32(n) -> (FlagLT_ULT)
|
(CMPconst (SRLconst _ [c]) [n]) && 0 <= n && 0 < c && c <= 32 && (1<<uint32(32-c)) <= uint32(n) => (FlagConstant [subFlags32(0, 1)])
|
||||||
|
|
||||||
// absorb flag constants into branches
|
// absorb flag constants into branches
|
||||||
(EQ (FlagEQ) yes no) -> (First yes no)
|
(EQ (FlagConstant [fc]) yes no) && fc.eq() => (First yes no)
|
||||||
(EQ (FlagLT_ULT) yes no) -> (First no yes)
|
(EQ (FlagConstant [fc]) yes no) && !fc.eq() => (First no yes)
|
||||||
(EQ (FlagLT_UGT) yes no) -> (First no yes)
|
|
||||||
(EQ (FlagGT_ULT) yes no) -> (First no yes)
|
|
||||||
(EQ (FlagGT_UGT) yes no) -> (First no yes)
|
|
||||||
|
|
||||||
(NE (FlagEQ) yes no) -> (First no yes)
|
(NE (FlagConstant [fc]) yes no) && fc.ne() => (First yes no)
|
||||||
(NE (FlagLT_ULT) yes no) -> (First yes no)
|
(NE (FlagConstant [fc]) yes no) && !fc.ne() => (First no yes)
|
||||||
(NE (FlagLT_UGT) yes no) -> (First yes no)
|
|
||||||
(NE (FlagGT_ULT) yes no) -> (First yes no)
|
|
||||||
(NE (FlagGT_UGT) yes no) -> (First yes no)
|
|
||||||
|
|
||||||
(LT (FlagEQ) yes no) -> (First no yes)
|
(LT (FlagConstant [fc]) yes no) && fc.lt() => (First yes no)
|
||||||
(LT (FlagLT_ULT) yes no) -> (First yes no)
|
(LT (FlagConstant [fc]) yes no) && !fc.lt() => (First no yes)
|
||||||
(LT (FlagLT_UGT) yes no) -> (First yes no)
|
|
||||||
(LT (FlagGT_ULT) yes no) -> (First no yes)
|
|
||||||
(LT (FlagGT_UGT) yes no) -> (First no yes)
|
|
||||||
|
|
||||||
(LE (FlagEQ) yes no) -> (First yes no)
|
(LE (FlagConstant [fc]) yes no) && fc.le() => (First yes no)
|
||||||
(LE (FlagLT_ULT) yes no) -> (First yes no)
|
(LE (FlagConstant [fc]) yes no) && !fc.le() => (First no yes)
|
||||||
(LE (FlagLT_UGT) yes no) -> (First yes no)
|
|
||||||
(LE (FlagGT_ULT) yes no) -> (First no yes)
|
|
||||||
(LE (FlagGT_UGT) yes no) -> (First no yes)
|
|
||||||
|
|
||||||
(GT (FlagEQ) yes no) -> (First no yes)
|
(GT (FlagConstant [fc]) yes no) && fc.gt() => (First yes no)
|
||||||
(GT (FlagLT_ULT) yes no) -> (First no yes)
|
(GT (FlagConstant [fc]) yes no) && !fc.gt() => (First no yes)
|
||||||
(GT (FlagLT_UGT) yes no) -> (First no yes)
|
|
||||||
(GT (FlagGT_ULT) yes no) -> (First yes no)
|
|
||||||
(GT (FlagGT_UGT) yes no) -> (First yes no)
|
|
||||||
|
|
||||||
(GE (FlagEQ) yes no) -> (First yes no)
|
(GE (FlagConstant [fc]) yes no) && fc.ge() => (First yes no)
|
||||||
(GE (FlagLT_ULT) yes no) -> (First no yes)
|
(GE (FlagConstant [fc]) yes no) && !fc.ge() => (First no yes)
|
||||||
(GE (FlagLT_UGT) yes no) -> (First no yes)
|
|
||||||
(GE (FlagGT_ULT) yes no) -> (First yes no)
|
|
||||||
(GE (FlagGT_UGT) yes no) -> (First yes no)
|
|
||||||
|
|
||||||
(ULT (FlagEQ) yes no) -> (First no yes)
|
(ULT (FlagConstant [fc]) yes no) && fc.ult() => (First yes no)
|
||||||
(ULT (FlagLT_ULT) yes no) -> (First yes no)
|
(ULT (FlagConstant [fc]) yes no) && !fc.ult() => (First no yes)
|
||||||
(ULT (FlagLT_UGT) yes no) -> (First no yes)
|
|
||||||
(ULT (FlagGT_ULT) yes no) -> (First yes no)
|
|
||||||
(ULT (FlagGT_UGT) yes no) -> (First no yes)
|
|
||||||
|
|
||||||
(ULE (FlagEQ) yes no) -> (First yes no)
|
(ULE (FlagConstant [fc]) yes no) && fc.ule() => (First yes no)
|
||||||
(ULE (FlagLT_ULT) yes no) -> (First yes no)
|
(ULE (FlagConstant [fc]) yes no) && !fc.ule() => (First no yes)
|
||||||
(ULE (FlagLT_UGT) yes no) -> (First no yes)
|
|
||||||
(ULE (FlagGT_ULT) yes no) -> (First yes no)
|
|
||||||
(ULE (FlagGT_UGT) yes no) -> (First no yes)
|
|
||||||
|
|
||||||
(UGT (FlagEQ) yes no) -> (First no yes)
|
(UGT (FlagConstant [fc]) yes no) && fc.ugt() => (First yes no)
|
||||||
(UGT (FlagLT_ULT) yes no) -> (First no yes)
|
(UGT (FlagConstant [fc]) yes no) && !fc.ugt() => (First no yes)
|
||||||
(UGT (FlagLT_UGT) yes no) -> (First yes no)
|
|
||||||
(UGT (FlagGT_ULT) yes no) -> (First no yes)
|
|
||||||
(UGT (FlagGT_UGT) yes no) -> (First yes no)
|
|
||||||
|
|
||||||
(UGE (FlagEQ) yes no) -> (First yes no)
|
(UGE (FlagConstant [fc]) yes no) && fc.uge() => (First yes no)
|
||||||
(UGE (FlagLT_ULT) yes no) -> (First no yes)
|
(UGE (FlagConstant [fc]) yes no) && !fc.uge() => (First no yes)
|
||||||
(UGE (FlagLT_UGT) yes no) -> (First yes no)
|
|
||||||
(UGE (FlagGT_ULT) yes no) -> (First no yes)
|
(LTnoov (FlagConstant [fc]) yes no) && fc.ltNoov() => (First yes no)
|
||||||
(UGE (FlagGT_UGT) yes no) -> (First yes no)
|
(LTnoov (FlagConstant [fc]) yes no) && !fc.ltNoov() => (First no yes)
|
||||||
|
|
||||||
|
(LEnoov (FlagConstant [fc]) yes no) && fc.leNoov() => (First yes no)
|
||||||
|
(LEnoov (FlagConstant [fc]) yes no) && !fc.leNoov() => (First no yes)
|
||||||
|
|
||||||
|
(GTnoov (FlagConstant [fc]) yes no) && fc.gtNoov() => (First yes no)
|
||||||
|
(GTnoov (FlagConstant [fc]) yes no) && !fc.gtNoov() => (First no yes)
|
||||||
|
|
||||||
|
(GEnoov (FlagConstant [fc]) yes no) && fc.geNoov() => (First yes no)
|
||||||
|
(GEnoov (FlagConstant [fc]) yes no) && !fc.geNoov() => (First no yes)
|
||||||
|
|
||||||
// absorb InvertFlags into branches
|
// absorb InvertFlags into branches
|
||||||
(LT (InvertFlags cmp) yes no) -> (GT cmp yes no)
|
(LT (InvertFlags cmp) yes no) -> (GT cmp yes no)
|
||||||
|
|
@ -710,65 +680,16 @@
|
||||||
(GTnoov (InvertFlags cmp) yes no) => (LTnoov cmp yes no)
|
(GTnoov (InvertFlags cmp) yes no) => (LTnoov cmp yes no)
|
||||||
|
|
||||||
// absorb flag constants into boolean values
|
// absorb flag constants into boolean values
|
||||||
(Equal (FlagEQ)) -> (MOVWconst [1])
|
(Equal (FlagConstant [fc])) => (MOVWconst [b2i32(fc.eq())])
|
||||||
(Equal (FlagLT_ULT)) -> (MOVWconst [0])
|
(NotEqual (FlagConstant [fc])) => (MOVWconst [b2i32(fc.ne())])
|
||||||
(Equal (FlagLT_UGT)) -> (MOVWconst [0])
|
(LessThan (FlagConstant [fc])) => (MOVWconst [b2i32(fc.lt())])
|
||||||
(Equal (FlagGT_ULT)) -> (MOVWconst [0])
|
(LessThanU (FlagConstant [fc])) => (MOVWconst [b2i32(fc.ult())])
|
||||||
(Equal (FlagGT_UGT)) -> (MOVWconst [0])
|
(LessEqual (FlagConstant [fc])) => (MOVWconst [b2i32(fc.le())])
|
||||||
|
(LessEqualU (FlagConstant [fc])) => (MOVWconst [b2i32(fc.ule())])
|
||||||
(NotEqual (FlagEQ)) -> (MOVWconst [0])
|
(GreaterThan (FlagConstant [fc])) => (MOVWconst [b2i32(fc.gt())])
|
||||||
(NotEqual (FlagLT_ULT)) -> (MOVWconst [1])
|
(GreaterThanU (FlagConstant [fc])) => (MOVWconst [b2i32(fc.ugt())])
|
||||||
(NotEqual (FlagLT_UGT)) -> (MOVWconst [1])
|
(GreaterEqual (FlagConstant [fc])) => (MOVWconst [b2i32(fc.ge())])
|
||||||
(NotEqual (FlagGT_ULT)) -> (MOVWconst [1])
|
(GreaterEqualU (FlagConstant [fc])) => (MOVWconst [b2i32(fc.uge())])
|
||||||
(NotEqual (FlagGT_UGT)) -> (MOVWconst [1])
|
|
||||||
|
|
||||||
(LessThan (FlagEQ)) -> (MOVWconst [0])
|
|
||||||
(LessThan (FlagLT_ULT)) -> (MOVWconst [1])
|
|
||||||
(LessThan (FlagLT_UGT)) -> (MOVWconst [1])
|
|
||||||
(LessThan (FlagGT_ULT)) -> (MOVWconst [0])
|
|
||||||
(LessThan (FlagGT_UGT)) -> (MOVWconst [0])
|
|
||||||
|
|
||||||
(LessThanU (FlagEQ)) -> (MOVWconst [0])
|
|
||||||
(LessThanU (FlagLT_ULT)) -> (MOVWconst [1])
|
|
||||||
(LessThanU (FlagLT_UGT)) -> (MOVWconst [0])
|
|
||||||
(LessThanU (FlagGT_ULT)) -> (MOVWconst [1])
|
|
||||||
(LessThanU (FlagGT_UGT)) -> (MOVWconst [0])
|
|
||||||
|
|
||||||
(LessEqual (FlagEQ)) -> (MOVWconst [1])
|
|
||||||
(LessEqual (FlagLT_ULT)) -> (MOVWconst [1])
|
|
||||||
(LessEqual (FlagLT_UGT)) -> (MOVWconst [1])
|
|
||||||
(LessEqual (FlagGT_ULT)) -> (MOVWconst [0])
|
|
||||||
(LessEqual (FlagGT_UGT)) -> (MOVWconst [0])
|
|
||||||
|
|
||||||
(LessEqualU (FlagEQ)) -> (MOVWconst [1])
|
|
||||||
(LessEqualU (FlagLT_ULT)) -> (MOVWconst [1])
|
|
||||||
(LessEqualU (FlagLT_UGT)) -> (MOVWconst [0])
|
|
||||||
(LessEqualU (FlagGT_ULT)) -> (MOVWconst [1])
|
|
||||||
(LessEqualU (FlagGT_UGT)) -> (MOVWconst [0])
|
|
||||||
|
|
||||||
(GreaterThan (FlagEQ)) -> (MOVWconst [0])
|
|
||||||
(GreaterThan (FlagLT_ULT)) -> (MOVWconst [0])
|
|
||||||
(GreaterThan (FlagLT_UGT)) -> (MOVWconst [0])
|
|
||||||
(GreaterThan (FlagGT_ULT)) -> (MOVWconst [1])
|
|
||||||
(GreaterThan (FlagGT_UGT)) -> (MOVWconst [1])
|
|
||||||
|
|
||||||
(GreaterThanU (FlagEQ)) -> (MOVWconst [0])
|
|
||||||
(GreaterThanU (FlagLT_ULT)) -> (MOVWconst [0])
|
|
||||||
(GreaterThanU (FlagLT_UGT)) -> (MOVWconst [1])
|
|
||||||
(GreaterThanU (FlagGT_ULT)) -> (MOVWconst [0])
|
|
||||||
(GreaterThanU (FlagGT_UGT)) -> (MOVWconst [1])
|
|
||||||
|
|
||||||
(GreaterEqual (FlagEQ)) -> (MOVWconst [1])
|
|
||||||
(GreaterEqual (FlagLT_ULT)) -> (MOVWconst [0])
|
|
||||||
(GreaterEqual (FlagLT_UGT)) -> (MOVWconst [0])
|
|
||||||
(GreaterEqual (FlagGT_ULT)) -> (MOVWconst [1])
|
|
||||||
(GreaterEqual (FlagGT_UGT)) -> (MOVWconst [1])
|
|
||||||
|
|
||||||
(GreaterEqualU (FlagEQ)) -> (MOVWconst [1])
|
|
||||||
(GreaterEqualU (FlagLT_ULT)) -> (MOVWconst [0])
|
|
||||||
(GreaterEqualU (FlagLT_UGT)) -> (MOVWconst [1])
|
|
||||||
(GreaterEqualU (FlagGT_ULT)) -> (MOVWconst [0])
|
|
||||||
(GreaterEqualU (FlagGT_UGT)) -> (MOVWconst [1])
|
|
||||||
|
|
||||||
// absorb InvertFlags into boolean values
|
// absorb InvertFlags into boolean values
|
||||||
(Equal (InvertFlags x)) -> (Equal x)
|
(Equal (InvertFlags x)) -> (Equal x)
|
||||||
|
|
@ -783,26 +704,17 @@
|
||||||
(GreaterEqualU (InvertFlags x)) -> (LessEqualU x)
|
(GreaterEqualU (InvertFlags x)) -> (LessEqualU x)
|
||||||
|
|
||||||
// absorb flag constants into conditional instructions
|
// absorb flag constants into conditional instructions
|
||||||
(CMOVWLSconst _ (FlagEQ) [c]) -> (MOVWconst [c])
|
(CMOVWLSconst _ (FlagConstant [fc]) [c]) && fc.ule() => (MOVWconst [c])
|
||||||
(CMOVWLSconst _ (FlagLT_ULT) [c]) -> (MOVWconst [c])
|
(CMOVWLSconst x (FlagConstant [fc]) [c]) && fc.ugt() => x
|
||||||
(CMOVWLSconst x (FlagLT_UGT)) -> x
|
|
||||||
(CMOVWLSconst _ (FlagGT_ULT) [c]) -> (MOVWconst [c])
|
|
||||||
(CMOVWLSconst x (FlagGT_UGT)) -> x
|
|
||||||
|
|
||||||
(CMOVWHSconst _ (FlagEQ) [c]) -> (MOVWconst [c])
|
(CMOVWHSconst _ (FlagConstant [fc]) [c]) && fc.uge() => (MOVWconst [c])
|
||||||
(CMOVWHSconst x (FlagLT_ULT)) -> x
|
(CMOVWHSconst x (FlagConstant [fc]) [c]) && fc.ult() => x
|
||||||
(CMOVWHSconst _ (FlagLT_UGT) [c]) -> (MOVWconst [c])
|
|
||||||
(CMOVWHSconst x (FlagGT_ULT)) -> x
|
|
||||||
(CMOVWHSconst _ (FlagGT_UGT) [c]) -> (MOVWconst [c])
|
|
||||||
|
|
||||||
(CMOVWLSconst x (InvertFlags flags) [c]) -> (CMOVWHSconst x flags [c])
|
(CMOVWLSconst x (InvertFlags flags) [c]) -> (CMOVWHSconst x flags [c])
|
||||||
(CMOVWHSconst x (InvertFlags flags) [c]) -> (CMOVWLSconst x flags [c])
|
(CMOVWHSconst x (InvertFlags flags) [c]) -> (CMOVWLSconst x flags [c])
|
||||||
|
|
||||||
(SRAcond x _ (FlagEQ)) -> (SRAconst x [31])
|
(SRAcond x _ (FlagConstant [fc])) && fc.uge() => (SRAconst x [31])
|
||||||
(SRAcond x y (FlagLT_ULT)) -> (SRA x y)
|
(SRAcond x y (FlagConstant [fc])) && fc.ult() => (SRA x y)
|
||||||
(SRAcond x _ (FlagLT_UGT)) -> (SRAconst x [31])
|
|
||||||
(SRAcond x y (FlagGT_ULT)) -> (SRA x y)
|
|
||||||
(SRAcond x _ (FlagGT_UGT)) -> (SRAconst x [31])
|
|
||||||
|
|
||||||
// remove redundant *const ops
|
// remove redundant *const ops
|
||||||
(ADDconst [0] x) -> x
|
(ADDconst [0] x) -> x
|
||||||
|
|
|
||||||
|
|
@ -1381,103 +1381,64 @@
|
||||||
(MOVDreg (MOVDconst [c])) -> (MOVDconst [c])
|
(MOVDreg (MOVDconst [c])) -> (MOVDconst [c])
|
||||||
|
|
||||||
// constant comparisons
|
// constant comparisons
|
||||||
(CMPconst (MOVDconst [x]) [y]) && x==y -> (FlagEQ)
|
(CMPconst (MOVDconst [x]) [y]) => (FlagConstant [subFlags64(x,y)])
|
||||||
(CMPconst (MOVDconst [x]) [y]) && x<y && uint64(x)<uint64(y) -> (FlagLT_ULT)
|
(CMPWconst (MOVDconst [x]) [y]) => (FlagConstant [subFlags32(int32(x),y)])
|
||||||
(CMPconst (MOVDconst [x]) [y]) && x<y && uint64(x)>uint64(y) -> (FlagLT_UGT)
|
(TSTconst (MOVDconst [x]) [y]) => (FlagConstant [logicFlags64(x&y)])
|
||||||
(CMPconst (MOVDconst [x]) [y]) && x>y && uint64(x)<uint64(y) -> (FlagGT_ULT)
|
(TSTWconst (MOVDconst [x]) [y]) => (FlagConstant [logicFlags32(int32(x)&y)])
|
||||||
(CMPconst (MOVDconst [x]) [y]) && x>y && uint64(x)>uint64(y) -> (FlagGT_UGT)
|
(CMNconst (MOVDconst [x]) [y]) => (FlagConstant [addFlags64(x,y)])
|
||||||
(CMPWconst (MOVDconst [x]) [y]) && int32(x)==int32(y) -> (FlagEQ)
|
(CMNWconst (MOVDconst [x]) [y]) => (FlagConstant [addFlags32(int32(x),y)])
|
||||||
(CMPWconst (MOVDconst [x]) [y]) && int32(x)<int32(y) && uint32(x)<uint32(y) -> (FlagLT_ULT)
|
|
||||||
(CMPWconst (MOVDconst [x]) [y]) && int32(x)<int32(y) && uint32(x)>uint32(y) -> (FlagLT_UGT)
|
|
||||||
(CMPWconst (MOVDconst [x]) [y]) && int32(x)>int32(y) && uint32(x)<uint32(y) -> (FlagGT_ULT)
|
|
||||||
(CMPWconst (MOVDconst [x]) [y]) && int32(x)>int32(y) && uint32(x)>uint32(y) -> (FlagGT_UGT)
|
|
||||||
(TSTconst (MOVDconst [x]) [y]) && int64(x&y)==0 -> (FlagEQ)
|
|
||||||
(TSTconst (MOVDconst [x]) [y]) && int64(x&y)<0 -> (FlagLT_UGT)
|
|
||||||
(TSTconst (MOVDconst [x]) [y]) && int64(x&y)>0 -> (FlagGT_UGT)
|
|
||||||
(TSTWconst (MOVDconst [x]) [y]) && int32(x&y)==0 -> (FlagEQ)
|
|
||||||
(TSTWconst (MOVDconst [x]) [y]) && int32(x&y)<0 -> (FlagLT_UGT)
|
|
||||||
(TSTWconst (MOVDconst [x]) [y]) && int32(x&y)>0 -> (FlagGT_UGT)
|
|
||||||
(CMNconst (MOVDconst [x]) [y]) && int64(x)==int64(-y) -> (FlagEQ)
|
|
||||||
(CMNconst (MOVDconst [x]) [y]) && int64(x)<int64(-y) && uint64(x)<uint64(-y) -> (FlagLT_ULT)
|
|
||||||
(CMNconst (MOVDconst [x]) [y]) && int64(x)<int64(-y) && uint64(x)>uint64(-y) -> (FlagLT_UGT)
|
|
||||||
(CMNconst (MOVDconst [x]) [y]) && int64(x)>int64(-y) && uint64(x)<uint64(-y) -> (FlagGT_ULT)
|
|
||||||
(CMNconst (MOVDconst [x]) [y]) && int64(x)>int64(-y) && uint64(x)>uint64(-y) -> (FlagGT_UGT)
|
|
||||||
(CMNWconst (MOVDconst [x]) [y]) && int32(x)==int32(-y) -> (FlagEQ)
|
|
||||||
(CMNWconst (MOVDconst [x]) [y]) && int32(x)<int32(-y) && uint32(x)<uint32(-y) -> (FlagLT_ULT)
|
|
||||||
(CMNWconst (MOVDconst [x]) [y]) && int32(x)<int32(-y) && uint32(x)>uint32(-y) -> (FlagLT_UGT)
|
|
||||||
(CMNWconst (MOVDconst [x]) [y]) && int32(x)>int32(-y) && uint32(x)<uint32(-y) -> (FlagGT_ULT)
|
|
||||||
(CMNWconst (MOVDconst [x]) [y]) && int32(x)>int32(-y) && uint32(x)>uint32(-y) -> (FlagGT_UGT)
|
|
||||||
|
|
||||||
|
|
||||||
// other known comparisons
|
// other known comparisons
|
||||||
(CMPconst (MOVBUreg _) [c]) && 0xff < c -> (FlagLT_ULT)
|
(CMPconst (MOVBUreg _) [c]) && 0xff < c => (FlagConstant [subFlags64(0,1)])
|
||||||
(CMPconst (MOVHUreg _) [c]) && 0xffff < c -> (FlagLT_ULT)
|
(CMPconst (MOVHUreg _) [c]) && 0xffff < c => (FlagConstant [subFlags64(0,1)])
|
||||||
(CMPconst (MOVWUreg _) [c]) && 0xffffffff < c -> (FlagLT_ULT)
|
(CMPconst (MOVWUreg _) [c]) && 0xffffffff < c => (FlagConstant [subFlags64(0,1)])
|
||||||
(CMPconst (ANDconst _ [m]) [n]) && 0 <= m && m < n -> (FlagLT_ULT)
|
(CMPconst (ANDconst _ [m]) [n]) && 0 <= m && m < n => (FlagConstant [subFlags64(0,1)])
|
||||||
(CMPconst (SRLconst _ [c]) [n]) && 0 <= n && 0 < c && c <= 63 && (1<<uint64(64-c)) <= uint64(n) -> (FlagLT_ULT)
|
(CMPconst (SRLconst _ [c]) [n]) && 0 <= n && 0 < c && c <= 63 && (1<<uint64(64-c)) <= uint64(n) => (FlagConstant [subFlags64(0,1)])
|
||||||
(CMPWconst (MOVBUreg _) [c]) && 0xff < int32(c) -> (FlagLT_ULT)
|
(CMPWconst (MOVBUreg _) [c]) && 0xff < c => (FlagConstant [subFlags64(0,1)])
|
||||||
(CMPWconst (MOVHUreg _) [c]) && 0xffff < int32(c) -> (FlagLT_ULT)
|
(CMPWconst (MOVHUreg _) [c]) && 0xffff < c => (FlagConstant [subFlags64(0,1)])
|
||||||
|
|
||||||
// absorb flag constants into branches
|
// absorb flag constants into branches
|
||||||
(EQ (FlagEQ) yes no) -> (First yes no)
|
(EQ (FlagConstant [fc]) yes no) && fc.eq() => (First yes no)
|
||||||
(EQ (FlagLT_ULT) yes no) -> (First no yes)
|
(EQ (FlagConstant [fc]) yes no) && !fc.eq() => (First no yes)
|
||||||
(EQ (FlagLT_UGT) yes no) -> (First no yes)
|
|
||||||
(EQ (FlagGT_ULT) yes no) -> (First no yes)
|
|
||||||
(EQ (FlagGT_UGT) yes no) -> (First no yes)
|
|
||||||
|
|
||||||
(NE (FlagEQ) yes no) -> (First no yes)
|
(NE (FlagConstant [fc]) yes no) && fc.ne() => (First yes no)
|
||||||
(NE (FlagLT_ULT) yes no) -> (First yes no)
|
(NE (FlagConstant [fc]) yes no) && !fc.ne() => (First no yes)
|
||||||
(NE (FlagLT_UGT) yes no) -> (First yes no)
|
|
||||||
(NE (FlagGT_ULT) yes no) -> (First yes no)
|
|
||||||
(NE (FlagGT_UGT) yes no) -> (First yes no)
|
|
||||||
|
|
||||||
(LT (FlagEQ) yes no) -> (First no yes)
|
(LT (FlagConstant [fc]) yes no) && fc.lt() => (First yes no)
|
||||||
(LT (FlagLT_ULT) yes no) -> (First yes no)
|
(LT (FlagConstant [fc]) yes no) && !fc.lt() => (First no yes)
|
||||||
(LT (FlagLT_UGT) yes no) -> (First yes no)
|
|
||||||
(LT (FlagGT_ULT) yes no) -> (First no yes)
|
|
||||||
(LT (FlagGT_UGT) yes no) -> (First no yes)
|
|
||||||
|
|
||||||
(LE (FlagEQ) yes no) -> (First yes no)
|
(LE (FlagConstant [fc]) yes no) && fc.le() => (First yes no)
|
||||||
(LE (FlagLT_ULT) yes no) -> (First yes no)
|
(LE (FlagConstant [fc]) yes no) && !fc.le() => (First no yes)
|
||||||
(LE (FlagLT_UGT) yes no) -> (First yes no)
|
|
||||||
(LE (FlagGT_ULT) yes no) -> (First no yes)
|
|
||||||
(LE (FlagGT_UGT) yes no) -> (First no yes)
|
|
||||||
|
|
||||||
(GT (FlagEQ) yes no) -> (First no yes)
|
(GT (FlagConstant [fc]) yes no) && fc.gt() => (First yes no)
|
||||||
(GT (FlagLT_ULT) yes no) -> (First no yes)
|
(GT (FlagConstant [fc]) yes no) && !fc.gt() => (First no yes)
|
||||||
(GT (FlagLT_UGT) yes no) -> (First no yes)
|
|
||||||
(GT (FlagGT_ULT) yes no) -> (First yes no)
|
|
||||||
(GT (FlagGT_UGT) yes no) -> (First yes no)
|
|
||||||
|
|
||||||
(GE (FlagEQ) yes no) -> (First yes no)
|
(GE (FlagConstant [fc]) yes no) && fc.ge() => (First yes no)
|
||||||
(GE (FlagLT_ULT) yes no) -> (First no yes)
|
(GE (FlagConstant [fc]) yes no) && !fc.ge() => (First no yes)
|
||||||
(GE (FlagLT_UGT) yes no) -> (First no yes)
|
|
||||||
(GE (FlagGT_ULT) yes no) -> (First yes no)
|
|
||||||
(GE (FlagGT_UGT) yes no) -> (First yes no)
|
|
||||||
|
|
||||||
(ULT (FlagEQ) yes no) -> (First no yes)
|
(ULT (FlagConstant [fc]) yes no) && fc.ult() => (First yes no)
|
||||||
(ULT (FlagLT_ULT) yes no) -> (First yes no)
|
(ULT (FlagConstant [fc]) yes no) && !fc.ult() => (First no yes)
|
||||||
(ULT (FlagLT_UGT) yes no) -> (First no yes)
|
|
||||||
(ULT (FlagGT_ULT) yes no) -> (First yes no)
|
|
||||||
(ULT (FlagGT_UGT) yes no) -> (First no yes)
|
|
||||||
|
|
||||||
(ULE (FlagEQ) yes no) -> (First yes no)
|
(ULE (FlagConstant [fc]) yes no) && fc.ule() => (First yes no)
|
||||||
(ULE (FlagLT_ULT) yes no) -> (First yes no)
|
(ULE (FlagConstant [fc]) yes no) && !fc.ule() => (First no yes)
|
||||||
(ULE (FlagLT_UGT) yes no) -> (First no yes)
|
|
||||||
(ULE (FlagGT_ULT) yes no) -> (First yes no)
|
|
||||||
(ULE (FlagGT_UGT) yes no) -> (First no yes)
|
|
||||||
|
|
||||||
(UGT (FlagEQ) yes no) -> (First no yes)
|
(UGT (FlagConstant [fc]) yes no) && fc.ugt() => (First yes no)
|
||||||
(UGT (FlagLT_ULT) yes no) -> (First no yes)
|
(UGT (FlagConstant [fc]) yes no) && !fc.ugt() => (First no yes)
|
||||||
(UGT (FlagLT_UGT) yes no) -> (First yes no)
|
|
||||||
(UGT (FlagGT_ULT) yes no) -> (First no yes)
|
|
||||||
(UGT (FlagGT_UGT) yes no) -> (First yes no)
|
|
||||||
|
|
||||||
(UGE (FlagEQ) yes no) -> (First yes no)
|
(UGE (FlagConstant [fc]) yes no) && fc.uge() => (First yes no)
|
||||||
(UGE (FlagLT_ULT) yes no) -> (First no yes)
|
(UGE (FlagConstant [fc]) yes no) && !fc.uge() => (First no yes)
|
||||||
(UGE (FlagLT_UGT) yes no) -> (First yes no)
|
|
||||||
(UGE (FlagGT_ULT) yes no) -> (First no yes)
|
(LTnoov (FlagConstant [fc]) yes no) && fc.ltNoov() => (First yes no)
|
||||||
(UGE (FlagGT_UGT) yes no) -> (First yes no)
|
(LTnoov (FlagConstant [fc]) yes no) && !fc.ltNoov() => (First no yes)
|
||||||
|
|
||||||
|
(LEnoov (FlagConstant [fc]) yes no) && fc.leNoov() => (First yes no)
|
||||||
|
(LEnoov (FlagConstant [fc]) yes no) && !fc.leNoov() => (First no yes)
|
||||||
|
|
||||||
|
(GTnoov (FlagConstant [fc]) yes no) && fc.gtNoov() => (First yes no)
|
||||||
|
(GTnoov (FlagConstant [fc]) yes no) && !fc.gtNoov() => (First no yes)
|
||||||
|
|
||||||
|
(GEnoov (FlagConstant [fc]) yes no) && fc.geNoov() => (First yes no)
|
||||||
|
(GEnoov (FlagConstant [fc]) yes no) && !fc.geNoov() => (First no yes)
|
||||||
|
|
||||||
(Z (MOVDconst [0]) yes no) -> (First yes no)
|
(Z (MOVDconst [0]) yes no) -> (First yes no)
|
||||||
(Z (MOVDconst [c]) yes no) && c != 0 -> (First no yes)
|
(Z (MOVDconst [c]) yes no) && c != 0 -> (First no yes)
|
||||||
|
|
@ -1513,65 +1474,16 @@
|
||||||
(CSEL0 {cc} x (InvertFlags cmp)) -> (CSEL0 {arm64Invert(cc.(Op))} x cmp)
|
(CSEL0 {cc} x (InvertFlags cmp)) -> (CSEL0 {arm64Invert(cc.(Op))} x cmp)
|
||||||
|
|
||||||
// absorb flag constants into boolean values
|
// absorb flag constants into boolean values
|
||||||
(Equal (FlagEQ)) -> (MOVDconst [1])
|
(Equal (FlagConstant [fc])) => (MOVDconst [b2i(fc.eq())])
|
||||||
(Equal (FlagLT_ULT)) -> (MOVDconst [0])
|
(NotEqual (FlagConstant [fc])) => (MOVDconst [b2i(fc.ne())])
|
||||||
(Equal (FlagLT_UGT)) -> (MOVDconst [0])
|
(LessThan (FlagConstant [fc])) => (MOVDconst [b2i(fc.lt())])
|
||||||
(Equal (FlagGT_ULT)) -> (MOVDconst [0])
|
(LessThanU (FlagConstant [fc])) => (MOVDconst [b2i(fc.ult())])
|
||||||
(Equal (FlagGT_UGT)) -> (MOVDconst [0])
|
(LessEqual (FlagConstant [fc])) => (MOVDconst [b2i(fc.le())])
|
||||||
|
(LessEqualU (FlagConstant [fc])) => (MOVDconst [b2i(fc.ule())])
|
||||||
(NotEqual (FlagEQ)) -> (MOVDconst [0])
|
(GreaterThan (FlagConstant [fc])) => (MOVDconst [b2i(fc.gt())])
|
||||||
(NotEqual (FlagLT_ULT)) -> (MOVDconst [1])
|
(GreaterThanU (FlagConstant [fc])) => (MOVDconst [b2i(fc.ugt())])
|
||||||
(NotEqual (FlagLT_UGT)) -> (MOVDconst [1])
|
(GreaterEqual (FlagConstant [fc])) => (MOVDconst [b2i(fc.ge())])
|
||||||
(NotEqual (FlagGT_ULT)) -> (MOVDconst [1])
|
(GreaterEqualU (FlagConstant [fc])) => (MOVDconst [b2i(fc.uge())])
|
||||||
(NotEqual (FlagGT_UGT)) -> (MOVDconst [1])
|
|
||||||
|
|
||||||
(LessThan (FlagEQ)) -> (MOVDconst [0])
|
|
||||||
(LessThan (FlagLT_ULT)) -> (MOVDconst [1])
|
|
||||||
(LessThan (FlagLT_UGT)) -> (MOVDconst [1])
|
|
||||||
(LessThan (FlagGT_ULT)) -> (MOVDconst [0])
|
|
||||||
(LessThan (FlagGT_UGT)) -> (MOVDconst [0])
|
|
||||||
|
|
||||||
(LessThanU (FlagEQ)) -> (MOVDconst [0])
|
|
||||||
(LessThanU (FlagLT_ULT)) -> (MOVDconst [1])
|
|
||||||
(LessThanU (FlagLT_UGT)) -> (MOVDconst [0])
|
|
||||||
(LessThanU (FlagGT_ULT)) -> (MOVDconst [1])
|
|
||||||
(LessThanU (FlagGT_UGT)) -> (MOVDconst [0])
|
|
||||||
|
|
||||||
(LessEqual (FlagEQ)) -> (MOVDconst [1])
|
|
||||||
(LessEqual (FlagLT_ULT)) -> (MOVDconst [1])
|
|
||||||
(LessEqual (FlagLT_UGT)) -> (MOVDconst [1])
|
|
||||||
(LessEqual (FlagGT_ULT)) -> (MOVDconst [0])
|
|
||||||
(LessEqual (FlagGT_UGT)) -> (MOVDconst [0])
|
|
||||||
|
|
||||||
(LessEqualU (FlagEQ)) -> (MOVDconst [1])
|
|
||||||
(LessEqualU (FlagLT_ULT)) -> (MOVDconst [1])
|
|
||||||
(LessEqualU (FlagLT_UGT)) -> (MOVDconst [0])
|
|
||||||
(LessEqualU (FlagGT_ULT)) -> (MOVDconst [1])
|
|
||||||
(LessEqualU (FlagGT_UGT)) -> (MOVDconst [0])
|
|
||||||
|
|
||||||
(GreaterThan (FlagEQ)) -> (MOVDconst [0])
|
|
||||||
(GreaterThan (FlagLT_ULT)) -> (MOVDconst [0])
|
|
||||||
(GreaterThan (FlagLT_UGT)) -> (MOVDconst [0])
|
|
||||||
(GreaterThan (FlagGT_ULT)) -> (MOVDconst [1])
|
|
||||||
(GreaterThan (FlagGT_UGT)) -> (MOVDconst [1])
|
|
||||||
|
|
||||||
(GreaterThanU (FlagEQ)) -> (MOVDconst [0])
|
|
||||||
(GreaterThanU (FlagLT_ULT)) -> (MOVDconst [0])
|
|
||||||
(GreaterThanU (FlagLT_UGT)) -> (MOVDconst [1])
|
|
||||||
(GreaterThanU (FlagGT_ULT)) -> (MOVDconst [0])
|
|
||||||
(GreaterThanU (FlagGT_UGT)) -> (MOVDconst [1])
|
|
||||||
|
|
||||||
(GreaterEqual (FlagEQ)) -> (MOVDconst [1])
|
|
||||||
(GreaterEqual (FlagLT_ULT)) -> (MOVDconst [0])
|
|
||||||
(GreaterEqual (FlagLT_UGT)) -> (MOVDconst [0])
|
|
||||||
(GreaterEqual (FlagGT_ULT)) -> (MOVDconst [1])
|
|
||||||
(GreaterEqual (FlagGT_UGT)) -> (MOVDconst [1])
|
|
||||||
|
|
||||||
(GreaterEqualU (FlagEQ)) -> (MOVDconst [1])
|
|
||||||
(GreaterEqualU (FlagLT_ULT)) -> (MOVDconst [0])
|
|
||||||
(GreaterEqualU (FlagLT_UGT)) -> (MOVDconst [1])
|
|
||||||
(GreaterEqualU (FlagGT_ULT)) -> (MOVDconst [0])
|
|
||||||
(GreaterEqualU (FlagGT_UGT)) -> (MOVDconst [1])
|
|
||||||
|
|
||||||
// absorb InvertFlags into boolean values
|
// absorb InvertFlags into boolean values
|
||||||
(Equal (InvertFlags x)) -> (Equal x)
|
(Equal (InvertFlags x)) -> (Equal x)
|
||||||
|
|
|
||||||
|
|
@ -587,18 +587,12 @@ func init() {
|
||||||
// See runtime/stubs.go for a more detailed discussion.
|
// See runtime/stubs.go for a more detailed discussion.
|
||||||
{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
|
{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
|
||||||
|
|
||||||
// Constant flag values. For any comparison, there are 5 possible
|
// Constant flag value.
|
||||||
// outcomes: the three from the signed total order (<,==,>) and the
|
// Note: there's an "unordered" outcome for floating-point
|
||||||
// three from the unsigned total order. The == cases overlap.
|
|
||||||
// Note: there's a sixth "unordered" outcome for floating-point
|
|
||||||
// comparisons, but we don't use such a beast yet.
|
// comparisons, but we don't use such a beast yet.
|
||||||
// These ops are for temporary use by rewrite rules. They
|
// This op is for temporary use by rewrite rules. It
|
||||||
// cannot appear in the generated assembly.
|
// cannot appear in the generated assembly.
|
||||||
{name: "FlagEQ"}, // equal
|
{name: "FlagConstant", aux: "FlagConstant"},
|
||||||
{name: "FlagLT_ULT"}, // signed < and unsigned <
|
|
||||||
{name: "FlagLT_UGT"}, // signed < and unsigned >
|
|
||||||
{name: "FlagGT_UGT"}, // signed > and unsigned <
|
|
||||||
{name: "FlagGT_ULT"}, // signed > and unsigned >
|
|
||||||
|
|
||||||
// (InvertFlags (CMP a b)) == (CMP b a)
|
// (InvertFlags (CMP a b)) == (CMP b a)
|
||||||
// InvertFlags is a pseudo-op which can't appear in assembly output.
|
// InvertFlags is a pseudo-op which can't appear in assembly output.
|
||||||
|
|
|
||||||
|
|
@ -550,18 +550,12 @@ func init() {
|
||||||
{name: "LoweredPanicExtendB", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r4, r1, r2}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
|
{name: "LoweredPanicExtendB", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r4, r1, r2}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
|
||||||
{name: "LoweredPanicExtendC", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r4, r0, r1}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
|
{name: "LoweredPanicExtendC", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r4, r0, r1}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
|
||||||
|
|
||||||
// Constant flag values. For any comparison, there are 5 possible
|
// Constant flag value.
|
||||||
// outcomes: the three from the signed total order (<,==,>) and the
|
// Note: there's an "unordered" outcome for floating-point
|
||||||
// three from the unsigned total order. The == cases overlap.
|
|
||||||
// Note: there's a sixth "unordered" outcome for floating-point
|
|
||||||
// comparisons, but we don't use such a beast yet.
|
// comparisons, but we don't use such a beast yet.
|
||||||
// These ops are for temporary use by rewrite rules. They
|
// This op is for temporary use by rewrite rules. It
|
||||||
// cannot appear in the generated assembly.
|
// cannot appear in the generated assembly.
|
||||||
{name: "FlagEQ"}, // equal
|
{name: "FlagConstant", aux: "FlagConstant"},
|
||||||
{name: "FlagLT_ULT"}, // signed < and unsigned <
|
|
||||||
{name: "FlagLT_UGT"}, // signed < and unsigned >
|
|
||||||
{name: "FlagGT_UGT"}, // signed > and unsigned <
|
|
||||||
{name: "FlagGT_ULT"}, // signed > and unsigned >
|
|
||||||
|
|
||||||
// (InvertFlags (CMP a b)) == (CMP b a)
|
// (InvertFlags (CMP a b)) == (CMP b a)
|
||||||
// InvertFlags is a pseudo-op which can't appear in assembly output.
|
// InvertFlags is a pseudo-op which can't appear in assembly output.
|
||||||
|
|
|
||||||
|
|
@ -394,24 +394,25 @@ func init() {
|
||||||
|
|
||||||
{name: "MOVDconst", reg: gp01, asm: "MOVD", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint
|
{name: "MOVDconst", reg: gp01, asm: "MOVD", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint
|
||||||
|
|
||||||
{name: "LDGR", argLength: 1, reg: gpfp, asm: "LDGR"}, // move int64 to float64 (no conversion)
|
{name: "LDGR", argLength: 1, reg: gpfp, asm: "LDGR"}, // move int64 to float64 (no conversion)
|
||||||
{name: "LGDR", argLength: 1, reg: fpgp, asm: "LGDR"}, // move float64 to int64 (no conversion)
|
{name: "LGDR", argLength: 1, reg: fpgp, asm: "LGDR"}, // move float64 to int64 (no conversion)
|
||||||
{name: "CFDBRA", argLength: 1, reg: fpgp, asm: "CFDBRA"}, // convert float64 to int32
|
|
||||||
{name: "CGDBRA", argLength: 1, reg: fpgp, asm: "CGDBRA"}, // convert float64 to int64
|
{name: "CFDBRA", argLength: 1, reg: fpgp, asm: "CFDBRA", clobberFlags: true}, // convert float64 to int32
|
||||||
{name: "CFEBRA", argLength: 1, reg: fpgp, asm: "CFEBRA"}, // convert float32 to int32
|
{name: "CGDBRA", argLength: 1, reg: fpgp, asm: "CGDBRA", clobberFlags: true}, // convert float64 to int64
|
||||||
{name: "CGEBRA", argLength: 1, reg: fpgp, asm: "CGEBRA"}, // convert float32 to int64
|
{name: "CFEBRA", argLength: 1, reg: fpgp, asm: "CFEBRA", clobberFlags: true}, // convert float32 to int32
|
||||||
{name: "CEFBRA", argLength: 1, reg: gpfp, asm: "CEFBRA"}, // convert int32 to float32
|
{name: "CGEBRA", argLength: 1, reg: fpgp, asm: "CGEBRA", clobberFlags: true}, // convert float32 to int64
|
||||||
{name: "CDFBRA", argLength: 1, reg: gpfp, asm: "CDFBRA"}, // convert int32 to float64
|
{name: "CEFBRA", argLength: 1, reg: gpfp, asm: "CEFBRA", clobberFlags: true}, // convert int32 to float32
|
||||||
{name: "CEGBRA", argLength: 1, reg: gpfp, asm: "CEGBRA"}, // convert int64 to float32
|
{name: "CDFBRA", argLength: 1, reg: gpfp, asm: "CDFBRA", clobberFlags: true}, // convert int32 to float64
|
||||||
{name: "CDGBRA", argLength: 1, reg: gpfp, asm: "CDGBRA"}, // convert int64 to float64
|
{name: "CEGBRA", argLength: 1, reg: gpfp, asm: "CEGBRA", clobberFlags: true}, // convert int64 to float32
|
||||||
{name: "CLFEBR", argLength: 1, reg: fpgp, asm: "CLFEBR"}, // convert float32 to uint32
|
{name: "CDGBRA", argLength: 1, reg: gpfp, asm: "CDGBRA", clobberFlags: true}, // convert int64 to float64
|
||||||
{name: "CLFDBR", argLength: 1, reg: fpgp, asm: "CLFDBR"}, // convert float64 to uint32
|
{name: "CLFEBR", argLength: 1, reg: fpgp, asm: "CLFEBR", clobberFlags: true}, // convert float32 to uint32
|
||||||
{name: "CLGEBR", argLength: 1, reg: fpgp, asm: "CLGEBR"}, // convert float32 to uint64
|
{name: "CLFDBR", argLength: 1, reg: fpgp, asm: "CLFDBR", clobberFlags: true}, // convert float64 to uint32
|
||||||
{name: "CLGDBR", argLength: 1, reg: fpgp, asm: "CLGDBR"}, // convert float64 to uint64
|
{name: "CLGEBR", argLength: 1, reg: fpgp, asm: "CLGEBR", clobberFlags: true}, // convert float32 to uint64
|
||||||
{name: "CELFBR", argLength: 1, reg: gpfp, asm: "CELFBR"}, // convert uint32 to float32
|
{name: "CLGDBR", argLength: 1, reg: fpgp, asm: "CLGDBR", clobberFlags: true}, // convert float64 to uint64
|
||||||
{name: "CDLFBR", argLength: 1, reg: gpfp, asm: "CDLFBR"}, // convert uint32 to float64
|
{name: "CELFBR", argLength: 1, reg: gpfp, asm: "CELFBR", clobberFlags: true}, // convert uint32 to float32
|
||||||
{name: "CELGBR", argLength: 1, reg: gpfp, asm: "CELGBR"}, // convert uint64 to float32
|
{name: "CDLFBR", argLength: 1, reg: gpfp, asm: "CDLFBR", clobberFlags: true}, // convert uint32 to float64
|
||||||
{name: "CDLGBR", argLength: 1, reg: gpfp, asm: "CDLGBR"}, // convert uint64 to float64
|
{name: "CELGBR", argLength: 1, reg: gpfp, asm: "CELGBR", clobberFlags: true}, // convert uint64 to float32
|
||||||
|
{name: "CDLGBR", argLength: 1, reg: gpfp, asm: "CDLGBR", clobberFlags: true}, // convert uint64 to float64
|
||||||
|
|
||||||
{name: "LEDBR", argLength: 1, reg: fp11, asm: "LEDBR"}, // convert float64 to float32
|
{name: "LEDBR", argLength: 1, reg: fp11, asm: "LEDBR"}, // convert float64 to float32
|
||||||
{name: "LDEBR", argLength: 1, reg: fp11, asm: "LDEBR"}, // convert float32 to float64
|
{name: "LDEBR", argLength: 1, reg: fp11, asm: "LDEBR"}, // convert float32 to float64
|
||||||
|
|
|
||||||
|
|
@ -1423,7 +1423,7 @@ func parseValue(val string, arch arch, loc string) (op opData, oparch, typ, auxi
|
||||||
|
|
||||||
func opHasAuxInt(op opData) bool {
|
func opHasAuxInt(op opData) bool {
|
||||||
switch op.aux {
|
switch op.aux {
|
||||||
case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", "SymOff", "SymValAndOff", "TypSize", "ARM64BitField":
|
case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", "SymOff", "SymValAndOff", "TypSize", "ARM64BitField", "FlagConstant":
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
@ -1818,6 +1818,8 @@ func (op opData) auxIntType() string {
|
||||||
return "int64"
|
return "int64"
|
||||||
case "CCop":
|
case "CCop":
|
||||||
return "Op"
|
return "Op"
|
||||||
|
case "FlagConstant":
|
||||||
|
return "flagConstant"
|
||||||
default:
|
default:
|
||||||
return "invalid"
|
return "invalid"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ const (
|
||||||
auxInt128 // auxInt represents a 128-bit integer. Always 0.
|
auxInt128 // auxInt represents a 128-bit integer. Always 0.
|
||||||
auxFloat32 // auxInt is a float32 (encoded with math.Float64bits)
|
auxFloat32 // auxInt is a float32 (encoded with math.Float64bits)
|
||||||
auxFloat64 // auxInt is a float64 (encoded with math.Float64bits)
|
auxFloat64 // auxInt is a float64 (encoded with math.Float64bits)
|
||||||
|
auxFlagConstant // auxInt is a flagConstant
|
||||||
auxString // aux is a string
|
auxString // aux is a string
|
||||||
auxSym // aux is a symbol (a *gc.Node for locals, an *obj.LSym for globals, or nil for none)
|
auxSym // aux is a symbol (a *gc.Node for locals, an *obj.LSym for globals, or nil for none)
|
||||||
auxSymOff // aux is a symbol, auxInt is an offset
|
auxSymOff // aux is a symbol, auxInt is an offset
|
||||||
|
|
|
||||||
|
|
@ -1283,11 +1283,7 @@ const (
|
||||||
OpARMLoweredPanicExtendA
|
OpARMLoweredPanicExtendA
|
||||||
OpARMLoweredPanicExtendB
|
OpARMLoweredPanicExtendB
|
||||||
OpARMLoweredPanicExtendC
|
OpARMLoweredPanicExtendC
|
||||||
OpARMFlagEQ
|
OpARMFlagConstant
|
||||||
OpARMFlagLT_ULT
|
|
||||||
OpARMFlagLT_UGT
|
|
||||||
OpARMFlagGT_UGT
|
|
||||||
OpARMFlagGT_ULT
|
|
||||||
OpARMInvertFlags
|
OpARMInvertFlags
|
||||||
OpARMLoweredWB
|
OpARMLoweredWB
|
||||||
|
|
||||||
|
|
@ -1558,11 +1554,7 @@ const (
|
||||||
OpARM64LoweredGetClosurePtr
|
OpARM64LoweredGetClosurePtr
|
||||||
OpARM64LoweredGetCallerSP
|
OpARM64LoweredGetCallerSP
|
||||||
OpARM64LoweredGetCallerPC
|
OpARM64LoweredGetCallerPC
|
||||||
OpARM64FlagEQ
|
OpARM64FlagConstant
|
||||||
OpARM64FlagLT_ULT
|
|
||||||
OpARM64FlagLT_UGT
|
|
||||||
OpARM64FlagGT_UGT
|
|
||||||
OpARM64FlagGT_ULT
|
|
||||||
OpARM64InvertFlags
|
OpARM64InvertFlags
|
||||||
OpARM64LDAR
|
OpARM64LDAR
|
||||||
OpARM64LDARB
|
OpARM64LDARB
|
||||||
|
|
@ -16911,29 +16903,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "FlagEQ",
|
name: "FlagConstant",
|
||||||
argLen: 0,
|
auxType: auxFlagConstant,
|
||||||
reg: regInfo{},
|
argLen: 0,
|
||||||
},
|
reg: regInfo{},
|
||||||
{
|
|
||||||
name: "FlagLT_ULT",
|
|
||||||
argLen: 0,
|
|
||||||
reg: regInfo{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "FlagLT_UGT",
|
|
||||||
argLen: 0,
|
|
||||||
reg: regInfo{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "FlagGT_UGT",
|
|
||||||
argLen: 0,
|
|
||||||
reg: regInfo{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "FlagGT_ULT",
|
|
||||||
argLen: 0,
|
|
||||||
reg: regInfo{},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "InvertFlags",
|
name: "InvertFlags",
|
||||||
|
|
@ -20521,29 +20494,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "FlagEQ",
|
name: "FlagConstant",
|
||||||
argLen: 0,
|
auxType: auxFlagConstant,
|
||||||
reg: regInfo{},
|
argLen: 0,
|
||||||
},
|
reg: regInfo{},
|
||||||
{
|
|
||||||
name: "FlagLT_ULT",
|
|
||||||
argLen: 0,
|
|
||||||
reg: regInfo{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "FlagLT_UGT",
|
|
||||||
argLen: 0,
|
|
||||||
reg: regInfo{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "FlagGT_UGT",
|
|
||||||
argLen: 0,
|
|
||||||
reg: regInfo{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "FlagGT_ULT",
|
|
||||||
argLen: 0,
|
|
||||||
reg: regInfo{},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "InvertFlags",
|
name: "InvertFlags",
|
||||||
|
|
@ -30191,9 +30145,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CFDBRA",
|
name: "CFDBRA",
|
||||||
argLen: 1,
|
argLen: 1,
|
||||||
asm: s390x.ACFDBRA,
|
clobberFlags: true,
|
||||||
|
asm: s390x.ACFDBRA,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
inputs: []inputInfo{
|
inputs: []inputInfo{
|
||||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||||
|
|
@ -30204,9 +30159,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CGDBRA",
|
name: "CGDBRA",
|
||||||
argLen: 1,
|
argLen: 1,
|
||||||
asm: s390x.ACGDBRA,
|
clobberFlags: true,
|
||||||
|
asm: s390x.ACGDBRA,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
inputs: []inputInfo{
|
inputs: []inputInfo{
|
||||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||||
|
|
@ -30217,9 +30173,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CFEBRA",
|
name: "CFEBRA",
|
||||||
argLen: 1,
|
argLen: 1,
|
||||||
asm: s390x.ACFEBRA,
|
clobberFlags: true,
|
||||||
|
asm: s390x.ACFEBRA,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
inputs: []inputInfo{
|
inputs: []inputInfo{
|
||||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||||
|
|
@ -30230,9 +30187,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CGEBRA",
|
name: "CGEBRA",
|
||||||
argLen: 1,
|
argLen: 1,
|
||||||
asm: s390x.ACGEBRA,
|
clobberFlags: true,
|
||||||
|
asm: s390x.ACGEBRA,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
inputs: []inputInfo{
|
inputs: []inputInfo{
|
||||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||||
|
|
@ -30243,9 +30201,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CEFBRA",
|
name: "CEFBRA",
|
||||||
argLen: 1,
|
argLen: 1,
|
||||||
asm: s390x.ACEFBRA,
|
clobberFlags: true,
|
||||||
|
asm: s390x.ACEFBRA,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
inputs: []inputInfo{
|
inputs: []inputInfo{
|
||||||
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
||||||
|
|
@ -30256,9 +30215,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CDFBRA",
|
name: "CDFBRA",
|
||||||
argLen: 1,
|
argLen: 1,
|
||||||
asm: s390x.ACDFBRA,
|
clobberFlags: true,
|
||||||
|
asm: s390x.ACDFBRA,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
inputs: []inputInfo{
|
inputs: []inputInfo{
|
||||||
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
||||||
|
|
@ -30269,9 +30229,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CEGBRA",
|
name: "CEGBRA",
|
||||||
argLen: 1,
|
argLen: 1,
|
||||||
asm: s390x.ACEGBRA,
|
clobberFlags: true,
|
||||||
|
asm: s390x.ACEGBRA,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
inputs: []inputInfo{
|
inputs: []inputInfo{
|
||||||
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
||||||
|
|
@ -30282,9 +30243,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CDGBRA",
|
name: "CDGBRA",
|
||||||
argLen: 1,
|
argLen: 1,
|
||||||
asm: s390x.ACDGBRA,
|
clobberFlags: true,
|
||||||
|
asm: s390x.ACDGBRA,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
inputs: []inputInfo{
|
inputs: []inputInfo{
|
||||||
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
||||||
|
|
@ -30295,9 +30257,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CLFEBR",
|
name: "CLFEBR",
|
||||||
argLen: 1,
|
argLen: 1,
|
||||||
asm: s390x.ACLFEBR,
|
clobberFlags: true,
|
||||||
|
asm: s390x.ACLFEBR,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
inputs: []inputInfo{
|
inputs: []inputInfo{
|
||||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||||
|
|
@ -30308,9 +30271,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CLFDBR",
|
name: "CLFDBR",
|
||||||
argLen: 1,
|
argLen: 1,
|
||||||
asm: s390x.ACLFDBR,
|
clobberFlags: true,
|
||||||
|
asm: s390x.ACLFDBR,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
inputs: []inputInfo{
|
inputs: []inputInfo{
|
||||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||||
|
|
@ -30321,9 +30285,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CLGEBR",
|
name: "CLGEBR",
|
||||||
argLen: 1,
|
argLen: 1,
|
||||||
asm: s390x.ACLGEBR,
|
clobberFlags: true,
|
||||||
|
asm: s390x.ACLGEBR,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
inputs: []inputInfo{
|
inputs: []inputInfo{
|
||||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||||
|
|
@ -30334,9 +30299,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CLGDBR",
|
name: "CLGDBR",
|
||||||
argLen: 1,
|
argLen: 1,
|
||||||
asm: s390x.ACLGDBR,
|
clobberFlags: true,
|
||||||
|
asm: s390x.ACLGDBR,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
inputs: []inputInfo{
|
inputs: []inputInfo{
|
||||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||||
|
|
@ -30347,9 +30313,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CELFBR",
|
name: "CELFBR",
|
||||||
argLen: 1,
|
argLen: 1,
|
||||||
asm: s390x.ACELFBR,
|
clobberFlags: true,
|
||||||
|
asm: s390x.ACELFBR,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
inputs: []inputInfo{
|
inputs: []inputInfo{
|
||||||
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
||||||
|
|
@ -30360,9 +30327,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CDLFBR",
|
name: "CDLFBR",
|
||||||
argLen: 1,
|
argLen: 1,
|
||||||
asm: s390x.ACDLFBR,
|
clobberFlags: true,
|
||||||
|
asm: s390x.ACDLFBR,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
inputs: []inputInfo{
|
inputs: []inputInfo{
|
||||||
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
||||||
|
|
@ -30373,9 +30341,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CELGBR",
|
name: "CELGBR",
|
||||||
argLen: 1,
|
argLen: 1,
|
||||||
asm: s390x.ACELGBR,
|
clobberFlags: true,
|
||||||
|
asm: s390x.ACELGBR,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
inputs: []inputInfo{
|
inputs: []inputInfo{
|
||||||
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
||||||
|
|
@ -30386,9 +30355,10 @@ var opcodeTable = [...]opInfo{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CDLGBR",
|
name: "CDLGBR",
|
||||||
argLen: 1,
|
argLen: 1,
|
||||||
asm: s390x.ACDLGBR,
|
clobberFlags: true,
|
||||||
|
asm: s390x.ACDLGBR,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
inputs: []inputInfo{
|
inputs: []inputInfo{
|
||||||
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
||||||
|
|
|
||||||
|
|
@ -511,6 +511,14 @@ func b2i(b bool) int64 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// b2i32 translates a boolean value to 0 or 1.
|
||||||
|
func b2i32(b bool) int32 {
|
||||||
|
if b {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
// shiftIsBounded reports whether (left/right) shift Value v is known to be bounded.
|
// shiftIsBounded reports whether (left/right) shift Value v is known to be bounded.
|
||||||
// A shift is bounded if it is shifting by less than the width of the shifted value.
|
// A shift is bounded if it is shifting by less than the width of the shifted value.
|
||||||
func shiftIsBounded(v *Value) bool {
|
func shiftIsBounded(v *Value) bool {
|
||||||
|
|
@ -616,6 +624,9 @@ func auxIntToInt128(x int64) int128 {
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
func auxIntToFlagConstant(x int64) flagConstant {
|
||||||
|
return flagConstant(x)
|
||||||
|
}
|
||||||
|
|
||||||
func boolToAuxInt(b bool) int64 {
|
func boolToAuxInt(b bool) int64 {
|
||||||
if b {
|
if b {
|
||||||
|
|
@ -653,6 +664,9 @@ func int128ToAuxInt(x int128) int64 {
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
func flagConstantToAuxInt(x flagConstant) int64 {
|
||||||
|
return int64(x)
|
||||||
|
}
|
||||||
|
|
||||||
func auxToString(i interface{}) string {
|
func auxToString(i interface{}) string {
|
||||||
return i.(string)
|
return i.(string)
|
||||||
|
|
@ -997,52 +1011,42 @@ func arm64Invert(op Op) Op {
|
||||||
func ccARM64Eval(cc interface{}, flags *Value) int {
|
func ccARM64Eval(cc interface{}, flags *Value) int {
|
||||||
op := cc.(Op)
|
op := cc.(Op)
|
||||||
fop := flags.Op
|
fop := flags.Op
|
||||||
switch fop {
|
if fop == OpARM64InvertFlags {
|
||||||
case OpARM64InvertFlags:
|
|
||||||
return -ccARM64Eval(op, flags.Args[0])
|
return -ccARM64Eval(op, flags.Args[0])
|
||||||
case OpARM64FlagEQ:
|
}
|
||||||
switch op {
|
if fop != OpARM64FlagConstant {
|
||||||
case OpARM64Equal, OpARM64GreaterEqual, OpARM64LessEqual,
|
|
||||||
OpARM64GreaterEqualU, OpARM64LessEqualU:
|
|
||||||
return 1
|
|
||||||
default:
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
case OpARM64FlagLT_ULT:
|
|
||||||
switch op {
|
|
||||||
case OpARM64LessThan, OpARM64LessThanU,
|
|
||||||
OpARM64LessEqual, OpARM64LessEqualU:
|
|
||||||
return 1
|
|
||||||
default:
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
case OpARM64FlagLT_UGT:
|
|
||||||
switch op {
|
|
||||||
case OpARM64LessThan, OpARM64GreaterThanU,
|
|
||||||
OpARM64LessEqual, OpARM64GreaterEqualU:
|
|
||||||
return 1
|
|
||||||
default:
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
case OpARM64FlagGT_ULT:
|
|
||||||
switch op {
|
|
||||||
case OpARM64GreaterThan, OpARM64LessThanU,
|
|
||||||
OpARM64GreaterEqual, OpARM64LessEqualU:
|
|
||||||
return 1
|
|
||||||
default:
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
case OpARM64FlagGT_UGT:
|
|
||||||
switch op {
|
|
||||||
case OpARM64GreaterThan, OpARM64GreaterThanU,
|
|
||||||
OpARM64GreaterEqual, OpARM64GreaterEqualU:
|
|
||||||
return 1
|
|
||||||
default:
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
fc := flagConstant(flags.AuxInt)
|
||||||
|
b2i := func(b bool) int {
|
||||||
|
if b {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
switch op {
|
||||||
|
case OpARM64Equal:
|
||||||
|
return b2i(fc.eq())
|
||||||
|
case OpARM64NotEqual:
|
||||||
|
return b2i(fc.ne())
|
||||||
|
case OpARM64LessThan:
|
||||||
|
return b2i(fc.lt())
|
||||||
|
case OpARM64LessThanU:
|
||||||
|
return b2i(fc.ult())
|
||||||
|
case OpARM64GreaterThan:
|
||||||
|
return b2i(fc.gt())
|
||||||
|
case OpARM64GreaterThanU:
|
||||||
|
return b2i(fc.ugt())
|
||||||
|
case OpARM64LessEqual:
|
||||||
|
return b2i(fc.le())
|
||||||
|
case OpARM64LessEqualU:
|
||||||
|
return b2i(fc.ule())
|
||||||
|
case OpARM64GreaterEqual:
|
||||||
|
return b2i(fc.ge())
|
||||||
|
case OpARM64GreaterEqualU:
|
||||||
|
return b2i(fc.uge())
|
||||||
|
}
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// logRule logs the use of the rule s. This will only be enabled if
|
// logRule logs the use of the rule s. This will only be enabled if
|
||||||
|
|
@ -1473,3 +1477,170 @@ func sequentialAddresses(x, y *Value, n int64) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// flagConstant represents the result of a compile-time comparison.
|
||||||
|
// The sense of these flags does not necessarily represent the hardware's notion
|
||||||
|
// of a flags register - these are just a compile-time construct.
|
||||||
|
// We happen to match the semantics to those of arm/arm64.
|
||||||
|
// Note that these semantics differ from x86: the carry flag has the opposite
|
||||||
|
// sense on a subtraction!
|
||||||
|
// On amd64, C=1 represents a borrow, e.g. SBB on amd64 does x - y - C.
|
||||||
|
// On arm64, C=0 represents a borrow, e.g. SBC on arm64 does x - y - ^C.
|
||||||
|
// (because it does x + ^y + C).
|
||||||
|
// See https://en.wikipedia.org/wiki/Carry_flag#Vs._borrow_flag
|
||||||
|
type flagConstant uint8
|
||||||
|
|
||||||
|
// N reports whether the result of an operation is negative (high bit set).
|
||||||
|
func (fc flagConstant) N() bool {
|
||||||
|
return fc&1 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Z reports whether the result of an operation is 0.
|
||||||
|
func (fc flagConstant) Z() bool {
|
||||||
|
return fc&2 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// C reports whether an unsigned add overflowed (carry), or an
|
||||||
|
// unsigned subtract did not underflow (borrow).
|
||||||
|
func (fc flagConstant) C() bool {
|
||||||
|
return fc&4 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// V reports whether a signed operation overflowed or underflowed.
|
||||||
|
func (fc flagConstant) V() bool {
|
||||||
|
return fc&8 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fc flagConstant) eq() bool {
|
||||||
|
return fc.Z()
|
||||||
|
}
|
||||||
|
func (fc flagConstant) ne() bool {
|
||||||
|
return !fc.Z()
|
||||||
|
}
|
||||||
|
func (fc flagConstant) lt() bool {
|
||||||
|
return fc.N() != fc.V()
|
||||||
|
}
|
||||||
|
func (fc flagConstant) le() bool {
|
||||||
|
return fc.Z() || fc.lt()
|
||||||
|
}
|
||||||
|
func (fc flagConstant) gt() bool {
|
||||||
|
return !fc.Z() && fc.ge()
|
||||||
|
}
|
||||||
|
func (fc flagConstant) ge() bool {
|
||||||
|
return fc.N() == fc.V()
|
||||||
|
}
|
||||||
|
func (fc flagConstant) ult() bool {
|
||||||
|
return !fc.C()
|
||||||
|
}
|
||||||
|
func (fc flagConstant) ule() bool {
|
||||||
|
return fc.Z() || fc.ult()
|
||||||
|
}
|
||||||
|
func (fc flagConstant) ugt() bool {
|
||||||
|
return !fc.Z() && fc.uge()
|
||||||
|
}
|
||||||
|
func (fc flagConstant) uge() bool {
|
||||||
|
return fc.C()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fc flagConstant) ltNoov() bool {
|
||||||
|
return fc.lt() && !fc.V()
|
||||||
|
}
|
||||||
|
func (fc flagConstant) leNoov() bool {
|
||||||
|
return fc.le() && !fc.V()
|
||||||
|
}
|
||||||
|
func (fc flagConstant) gtNoov() bool {
|
||||||
|
return fc.gt() && !fc.V()
|
||||||
|
}
|
||||||
|
func (fc flagConstant) geNoov() bool {
|
||||||
|
return fc.ge() && !fc.V()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fc flagConstant) String() string {
|
||||||
|
return fmt.Sprintf("N=%v,Z=%v,C=%v,V=%v", fc.N(), fc.Z(), fc.C(), fc.V())
|
||||||
|
}
|
||||||
|
|
||||||
|
type flagConstantBuilder struct {
|
||||||
|
N bool
|
||||||
|
Z bool
|
||||||
|
C bool
|
||||||
|
V bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fcs flagConstantBuilder) encode() flagConstant {
|
||||||
|
var fc flagConstant
|
||||||
|
if fcs.N {
|
||||||
|
fc |= 1
|
||||||
|
}
|
||||||
|
if fcs.Z {
|
||||||
|
fc |= 2
|
||||||
|
}
|
||||||
|
if fcs.C {
|
||||||
|
fc |= 4
|
||||||
|
}
|
||||||
|
if fcs.V {
|
||||||
|
fc |= 8
|
||||||
|
}
|
||||||
|
return fc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: addFlags(x,y) != subFlags(x,-y) in some situations:
|
||||||
|
// - the results of the C flag are different
|
||||||
|
// - the results of the V flag when y==minint are different
|
||||||
|
|
||||||
|
// addFlags64 returns the flags that would be set from computing x+y.
|
||||||
|
func addFlags64(x, y int64) flagConstant {
|
||||||
|
var fcb flagConstantBuilder
|
||||||
|
fcb.Z = x+y == 0
|
||||||
|
fcb.N = x+y < 0
|
||||||
|
fcb.C = uint64(x+y) < uint64(x)
|
||||||
|
fcb.V = x >= 0 && y >= 0 && x+y < 0 || x < 0 && y < 0 && x+y >= 0
|
||||||
|
return fcb.encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// subFlags64 returns the flags that would be set from computing x-y.
|
||||||
|
func subFlags64(x, y int64) flagConstant {
|
||||||
|
var fcb flagConstantBuilder
|
||||||
|
fcb.Z = x-y == 0
|
||||||
|
fcb.N = x-y < 0
|
||||||
|
fcb.C = uint64(y) <= uint64(x) // This code follows the arm carry flag model.
|
||||||
|
fcb.V = x >= 0 && y < 0 && x-y < 0 || x < 0 && y >= 0 && x-y >= 0
|
||||||
|
return fcb.encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// addFlags32 returns the flags that would be set from computing x+y.
|
||||||
|
func addFlags32(x, y int32) flagConstant {
|
||||||
|
var fcb flagConstantBuilder
|
||||||
|
fcb.Z = x+y == 0
|
||||||
|
fcb.N = x+y < 0
|
||||||
|
fcb.C = uint32(x+y) < uint32(x)
|
||||||
|
fcb.V = x >= 0 && y >= 0 && x+y < 0 || x < 0 && y < 0 && x+y >= 0
|
||||||
|
return fcb.encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// subFlags32 returns the flags that would be set from computing x-y.
|
||||||
|
func subFlags32(x, y int32) flagConstant {
|
||||||
|
var fcb flagConstantBuilder
|
||||||
|
fcb.Z = x-y == 0
|
||||||
|
fcb.N = x-y < 0
|
||||||
|
fcb.C = uint32(y) <= uint32(x) // This code follows the arm carry flag model.
|
||||||
|
fcb.V = x >= 0 && y < 0 && x-y < 0 || x < 0 && y >= 0 && x-y >= 0
|
||||||
|
return fcb.encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// logicFlags64 returns flags set to the sign/zeroness of x.
|
||||||
|
// C and V are set to false.
|
||||||
|
func logicFlags64(x int64) flagConstant {
|
||||||
|
var fcb flagConstantBuilder
|
||||||
|
fcb.Z = x == 0
|
||||||
|
fcb.N = x < 0
|
||||||
|
return fcb.encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// logicFlags32 returns flags set to the sign/zeroness of x.
|
||||||
|
// C and V are set to false.
|
||||||
|
func logicFlags32(x int32) flagConstant {
|
||||||
|
var fcb flagConstantBuilder
|
||||||
|
fcb.Z = x == 0
|
||||||
|
fcb.N = x < 0
|
||||||
|
return fcb.encode()
|
||||||
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -27,3 +27,12 @@ func TestMoveSmall(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSubFlags(t *testing.T) {
|
||||||
|
if !subFlags32(0, 1).lt() {
|
||||||
|
t.Errorf("subFlags32(0,1).lt() returned false")
|
||||||
|
}
|
||||||
|
if !subFlags32(0, 1).ult() {
|
||||||
|
t.Errorf("subFlags32(0,1).ult() returned false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,8 @@ func (v *Value) auxString() string {
|
||||||
return fmt.Sprintf(" {%s}", v.Aux.(Op))
|
return fmt.Sprintf(" {%s}", v.Aux.(Op))
|
||||||
case auxS390XCCMask, auxS390XRotateParams:
|
case auxS390XCCMask, auxS390XRotateParams:
|
||||||
return fmt.Sprintf(" {%v}", v.Aux)
|
return fmt.Sprintf(" {%v}", v.Aux)
|
||||||
|
case auxFlagConstant:
|
||||||
|
return fmt.Sprintf("[%s]", flagConstant(v.AuxInt))
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
src/cmd/dist/buildtool.go
vendored
1
src/cmd/dist/buildtool.go
vendored
|
|
@ -115,6 +115,7 @@ var ignorePrefixes = []string{
|
||||||
// These must not be copied into the bootstrap build directory.
|
// These must not be copied into the bootstrap build directory.
|
||||||
var ignoreSuffixes = []string{
|
var ignoreSuffixes = []string{
|
||||||
"_arm64.s",
|
"_arm64.s",
|
||||||
|
"_arm64_test.s",
|
||||||
"_arm64.go",
|
"_arm64.go",
|
||||||
"_riscv64.s",
|
"_riscv64.s",
|
||||||
"_riscv64.go",
|
"_riscv64.go",
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,5 @@ require (
|
||||||
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79
|
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79
|
||||||
golang.org/x/mod v0.3.0
|
golang.org/x/mod v0.3.0
|
||||||
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 // indirect
|
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 // indirect
|
||||||
golang.org/x/tools v0.0.0-20200601175630-2caf76543d99
|
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,8 @@ golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 h1:5B6i6EAiSYyejWfvc5Rc9BbI3
|
||||||
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200601175630-2caf76543d99 h1:deddXmhOJb/bvD/4M/j2AUMrhHeh6GkqykJSCWyTNVk=
|
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054 h1:HHeAlu5H9b71C+Fx0K+1dGgVFN1DM1/wz4aoGOA5qS8=
|
||||||
golang.org/x/tools v0.0.0-20200601175630-2caf76543d99/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
|
|
|
||||||
3
src/cmd/go/testdata/script/generate_env.txt
vendored
3
src/cmd/go/testdata/script/generate_env.txt
vendored
|
|
@ -1,7 +1,8 @@
|
||||||
# Install an env command because Windows and plan9 don't have it.
|
# Install an env command because Windows and plan9 don't have it.
|
||||||
env GOBIN=$WORK/tmp/bin
|
env GOBIN=$WORK/tmp/bin
|
||||||
go install env.go
|
go install env.go
|
||||||
env PATH=$GOBIN${:}$PATH
|
[plan9] env path=$GOBIN${:}$path
|
||||||
|
[!plan9] env PATH=$GOBIN${:}$PATH
|
||||||
|
|
||||||
# Test generators have access to the environment
|
# Test generators have access to the environment
|
||||||
go generate ./printenv.go
|
go generate ./printenv.go
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ grep '{"Version":"v1.0.0","Time":"2018-02-14T00:45:20Z"}' $GOPATH/pkg/mod/cache/
|
||||||
|
|
||||||
# If neither GOMODCACHE or GOPATH are set, GOPATH defaults to the user's $HOME/go, so GOMODCACHE becomes $HOME/go/pkg/mod
|
# If neither GOMODCACHE or GOPATH are set, GOPATH defaults to the user's $HOME/go, so GOMODCACHE becomes $HOME/go/pkg/mod
|
||||||
[windows] env USERPROFILE=$WORK/home # Ensure USERPROFILE is a valid path (rather than /no-home/ so we don't run into the logic that "uninfers" GOPATH in cmd/go/main.go
|
[windows] env USERPROFILE=$WORK/home # Ensure USERPROFILE is a valid path (rather than /no-home/ so we don't run into the logic that "uninfers" GOPATH in cmd/go/main.go
|
||||||
[!windows] env HOME=$WORK/home
|
[plan9] env home=$WORK/home
|
||||||
|
[!windows] [!plan9] env HOME=$WORK/home
|
||||||
env GOMODCACHE=
|
env GOMODCACHE=
|
||||||
env GOPATH=
|
env GOPATH=
|
||||||
go env GOMODCACHE
|
go env GOMODCACHE
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,7 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Package ppc64 implements a PPC64 assembler that assembles Go asm into
|
Package ppc64 implements a PPC64 assembler that assembles Go asm into
|
||||||
the corresponding PPC64 binary instructions as defined by the Power
|
the corresponding PPC64 instructions as defined by the Power ISA 3.0B.
|
||||||
ISA. Since POWER8 is the minimum instruction set used by GOARCHes ppc64le
|
|
||||||
and ppc64, refer to ISA 2.07B or later for details.
|
|
||||||
|
|
||||||
This document provides information on how to write code in Go assembler
|
This document provides information on how to write code in Go assembler
|
||||||
for PPC64, focusing on the differences between Go and PPC64 assembly language.
|
for PPC64, focusing on the differences between Go and PPC64 assembly language.
|
||||||
|
|
@ -16,16 +14,24 @@ updates to the Go assembly language used mnemonics that are mostly similar if no
|
||||||
identical to the PPC64 mneumonics, such as VMX and VSX instructions. Not all detail
|
identical to the PPC64 mneumonics, such as VMX and VSX instructions. Not all detail
|
||||||
is included here; refer to the Power ISA document if interested in more detail.
|
is included here; refer to the Power ISA document if interested in more detail.
|
||||||
|
|
||||||
|
Starting with Go 1.15 the Go objdump supports the -gnu option, which provides a
|
||||||
|
side by side view of the Go assembler and the PPC64 assembler output. This is
|
||||||
|
extremely helpful in determining what final PPC64 assembly is generated from the
|
||||||
|
corresponding Go assembly.
|
||||||
|
|
||||||
In the examples below, the Go assembly is on the left, PPC64 assembly on the right.
|
In the examples below, the Go assembly is on the left, PPC64 assembly on the right.
|
||||||
|
|
||||||
1. Operand ordering
|
1. Operand ordering
|
||||||
|
|
||||||
In Go asm, the last operand (right) is the target operand, but with PPC64 asm,
|
In Go asm, the last operand (right) is the target operand, but with PPC64 asm,
|
||||||
the first operand (left) is the target. In general, the remaining operands are
|
the first operand (left) is the target. The order of the remaining operands is
|
||||||
in the same order except in a few special cases, especially those with 4 operands.
|
not consistent: in general opcodes with 3 operands that perform math or logical
|
||||||
|
operations have their operands in reverse order. Opcodes for vector instructions
|
||||||
|
and those with more than 3 operands usually have operands in the same order except
|
||||||
|
for the target operand, which is first in PPC64 asm and last in Go asm.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
ADD R3, R4, R5 <=> add r5, r3, r4
|
ADD R3, R4, R5 <=> add r5, r4, r3
|
||||||
|
|
||||||
2. Constant operands
|
2. Constant operands
|
||||||
|
|
||||||
|
|
@ -179,6 +185,40 @@ In the examples below, the Go assembly is on the left, PPC64 assembly on the rig
|
||||||
Functions in Go are aligned to 16 bytes, as is the case in all other compilers
|
Functions in Go are aligned to 16 bytes, as is the case in all other compilers
|
||||||
for PPC64.
|
for PPC64.
|
||||||
|
|
||||||
|
6. Shift instructions
|
||||||
|
|
||||||
|
The simple scalar shifts on PPC64 expect a shift count that fits in 5 bits for
|
||||||
|
32-bit values or 6 bit for 64-bit values. If the shift count is a constant value
|
||||||
|
greater than the max then the assembler sets it to the max for that size (31 for
|
||||||
|
32 bit values, 63 for 64 bit values). If the shift count is in a register, then
|
||||||
|
only the low 5 or 6 bits of the register will be used as the shift count. The
|
||||||
|
Go compiler will add appropriate code to compare the shift value to achieve the
|
||||||
|
the correct result, and the assembler does not add extra checking.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
SRAD $8,R3,R4 => sradi r4,r3,8
|
||||||
|
SRD $8,R3,R4 => rldicl r4,r3,56,8
|
||||||
|
SLD $8,R3,R4 => rldicr r4,r3,8,55
|
||||||
|
SRAW $16,R4,R5 => srawi r5,r4,16
|
||||||
|
SRW $40,R4,R5 => rlwinm r5,r4,0,0,31
|
||||||
|
SLW $12,R4,R5 => rlwinm r5,r4,12,0,19
|
||||||
|
|
||||||
|
Some non-simple shifts have operands in the Go assembly which don't map directly
|
||||||
|
onto operands in the PPC64 assembly. When an operand in a shift instruction in the
|
||||||
|
Go assembly is a bit mask, that mask is represented as a start and end bit in the
|
||||||
|
PPC64 assembly instead of a mask. See the ISA for more detail on these types of shifts.
|
||||||
|
Here are a few examples:
|
||||||
|
|
||||||
|
RLWMI $7,R3,$65535,R6 => rlwimi r6,r3,7,16,31
|
||||||
|
RLDMI $0,R4,$7,R6 => rldimi r6,r4,0,61
|
||||||
|
|
||||||
|
More recently, Go opcodes were added which map directly onto the PPC64 opcodes. It is
|
||||||
|
recommended to use the newer opcodes to avoid confusion.
|
||||||
|
|
||||||
|
RLDICL $0,R4,$15,R6 => rldicl r6,r4,0,15
|
||||||
|
RLDICR $0,R4,$15,R6 => rldicr r6.r4,0,15
|
||||||
|
|
||||||
Register naming
|
Register naming
|
||||||
|
|
||||||
1. Special register usage in Go asm
|
1. Special register usage in Go asm
|
||||||
|
|
|
||||||
|
|
@ -722,3 +722,37 @@ func TestIndexMismatch(t *testing.T) {
|
||||||
t.Errorf("did not see expected error message. out:\n%s", out)
|
t.Errorf("did not see expected error message. out:\n%s", out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPErsrc(t *testing.T) {
|
||||||
|
// Test that PE rsrc section is handled correctly (issue 39658).
|
||||||
|
testenv.MustHaveGoBuild(t)
|
||||||
|
|
||||||
|
if runtime.GOARCH != "amd64" || runtime.GOOS != "windows" {
|
||||||
|
t.Skipf("this is a windows/amd64-only test")
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpdir, err := ioutil.TempDir("", "TestPErsrc")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
|
||||||
|
pkgdir := filepath.Join("testdata", "testPErsrc")
|
||||||
|
exe := filepath.Join(tmpdir, "a.exe")
|
||||||
|
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe)
|
||||||
|
cmd.Dir = pkgdir
|
||||||
|
// cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("building failed: %v, output:\n%s", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the binary contains the rsrc data
|
||||||
|
b, err := ioutil.ReadFile(exe)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("reading output failed: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Contains(b, []byte("Hello Gophers!")) {
|
||||||
|
t.Fatalf("binary does not contain expected content")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
19
src/cmd/link/testdata/testPErsrc/main.go
vendored
Normal file
19
src/cmd/link/testdata/testPErsrc/main.go
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Test that a PE rsrc section is handled correctly (issue 39658).
|
||||||
|
//
|
||||||
|
// rsrc.syso is created with:
|
||||||
|
// windres -i a.rc -o rsrc.syso -O coff
|
||||||
|
// on windows-amd64-2016 builder, where a.rc is a text file with
|
||||||
|
// the following content:
|
||||||
|
//
|
||||||
|
// resname RCDATA {
|
||||||
|
// "Hello Gophers!\0",
|
||||||
|
// "This is a test.\0",
|
||||||
|
// }
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func main() {}
|
||||||
BIN
src/cmd/link/testdata/testPErsrc/rsrc.syso
vendored
Normal file
BIN
src/cmd/link/testdata/testPErsrc/rsrc.syso
vendored
Normal file
Binary file not shown.
9
src/cmd/vendor/golang.org/x/tools/go/analysis/doc.go
generated
vendored
9
src/cmd/vendor/golang.org/x/tools/go/analysis/doc.go
generated
vendored
|
|
@ -170,6 +170,15 @@ Diagnostic is defined as:
|
||||||
The optional Category field is a short identifier that classifies the
|
The optional Category field is a short identifier that classifies the
|
||||||
kind of message when an analysis produces several kinds of diagnostic.
|
kind of message when an analysis produces several kinds of diagnostic.
|
||||||
|
|
||||||
|
Many analyses want to associate diagnostics with a severity level.
|
||||||
|
Because Diagnostic does not have a severity level field, an Analyzer's
|
||||||
|
diagnostics effectively all have the same severity level. To separate which
|
||||||
|
diagnostics are high severity and which are low severity, expose multiple
|
||||||
|
Analyzers instead. Analyzers should also be separated when their
|
||||||
|
diagnostics belong in different groups, or could be tagged differently
|
||||||
|
before being shown to the end user. Analyzers should document their severity
|
||||||
|
level to help downstream tools surface diagnostics properly.
|
||||||
|
|
||||||
Most Analyzers inspect typed Go syntax trees, but a few, such as asmdecl
|
Most Analyzers inspect typed Go syntax trees, but a few, such as asmdecl
|
||||||
and buildtag, inspect the raw text of Go source files or even non-Go
|
and buildtag, inspect the raw text of Go source files or even non-Go
|
||||||
files such as assembly. To report a diagnostic against a line of a
|
files such as assembly. To report a diagnostic against a line of a
|
||||||
|
|
|
||||||
2
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stringintconv/string.go
generated
vendored
2
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stringintconv/string.go
generated
vendored
|
|
@ -101,7 +101,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
|
||||||
}
|
}
|
||||||
diag := analysis.Diagnostic{
|
diag := analysis.Diagnostic{
|
||||||
Pos: n.Pos(),
|
Pos: n.Pos(),
|
||||||
Message: fmt.Sprintf("conversion from %s to %s yields a string of one rune", source, target),
|
Message: fmt.Sprintf("conversion from %s to %s yields a string of one rune, not a string of digits (did you mean fmt.Sprint(x)?)", source, target),
|
||||||
SuggestedFixes: []analysis.SuggestedFix{
|
SuggestedFixes: []analysis.SuggestedFix{
|
||||||
{
|
{
|
||||||
Message: "Did you mean to convert a rune to a string?",
|
Message: "Did you mean to convert a rune to a string?",
|
||||||
|
|
|
||||||
2
src/cmd/vendor/modules.txt
vendored
2
src/cmd/vendor/modules.txt
vendored
|
|
@ -45,7 +45,7 @@ golang.org/x/mod/zip
|
||||||
golang.org/x/sys/internal/unsafeheader
|
golang.org/x/sys/internal/unsafeheader
|
||||||
golang.org/x/sys/unix
|
golang.org/x/sys/unix
|
||||||
golang.org/x/sys/windows
|
golang.org/x/sys/windows
|
||||||
# golang.org/x/tools v0.0.0-20200601175630-2caf76543d99
|
# golang.org/x/tools v0.0.0-20200616133436-c1934b75d054
|
||||||
## explicit
|
## explicit
|
||||||
golang.org/x/tools/go/analysis
|
golang.org/x/tools/go/analysis
|
||||||
golang.org/x/tools/go/analysis/internal/analysisflags
|
golang.org/x/tools/go/analysis/internal/analysisflags
|
||||||
|
|
|
||||||
|
|
@ -212,9 +212,6 @@ type decodeState struct {
|
||||||
savedError error
|
savedError error
|
||||||
useNumber bool
|
useNumber bool
|
||||||
disallowUnknownFields bool
|
disallowUnknownFields bool
|
||||||
// safeUnquote is the number of current string literal bytes that don't
|
|
||||||
// need to be unquoted. When negative, no bytes need unquoting.
|
|
||||||
safeUnquote int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// readIndex returns the position of the last byte read.
|
// readIndex returns the position of the last byte read.
|
||||||
|
|
@ -316,27 +313,13 @@ func (d *decodeState) rescanLiteral() {
|
||||||
Switch:
|
Switch:
|
||||||
switch data[i-1] {
|
switch data[i-1] {
|
||||||
case '"': // string
|
case '"': // string
|
||||||
// safeUnquote is initialized at -1, which means that all bytes
|
|
||||||
// checked so far can be unquoted at a later time with no work
|
|
||||||
// at all. When reaching the closing '"', if safeUnquote is
|
|
||||||
// still -1, all bytes can be unquoted with no work. Otherwise,
|
|
||||||
// only those bytes up until the first '\\' or non-ascii rune
|
|
||||||
// can be safely unquoted.
|
|
||||||
safeUnquote := -1
|
|
||||||
for ; i < len(data); i++ {
|
for ; i < len(data); i++ {
|
||||||
if c := data[i]; c == '\\' {
|
switch data[i] {
|
||||||
if safeUnquote < 0 { // first unsafe byte
|
case '\\':
|
||||||
safeUnquote = int(i - d.off)
|
|
||||||
}
|
|
||||||
i++ // escaped char
|
i++ // escaped char
|
||||||
} else if c == '"' {
|
case '"':
|
||||||
d.safeUnquote = safeUnquote
|
|
||||||
i++ // tokenize the closing quote too
|
i++ // tokenize the closing quote too
|
||||||
break Switch
|
break Switch
|
||||||
} else if c >= utf8.RuneSelf {
|
|
||||||
if safeUnquote < 0 { // first unsafe byte
|
|
||||||
safeUnquote = int(i - d.off)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': // number
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': // number
|
||||||
|
|
@ -695,7 +678,7 @@ func (d *decodeState) object(v reflect.Value) error {
|
||||||
start := d.readIndex()
|
start := d.readIndex()
|
||||||
d.rescanLiteral()
|
d.rescanLiteral()
|
||||||
item := d.data[start:d.readIndex()]
|
item := d.data[start:d.readIndex()]
|
||||||
key, ok := d.unquoteBytes(item)
|
key, ok := unquoteBytes(item)
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(phasePanicMsg)
|
panic(phasePanicMsg)
|
||||||
}
|
}
|
||||||
|
|
@ -896,7 +879,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
|
||||||
d.saveError(&UnmarshalTypeError{Value: val, Type: v.Type(), Offset: int64(d.readIndex())})
|
d.saveError(&UnmarshalTypeError{Value: val, Type: v.Type(), Offset: int64(d.readIndex())})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
s, ok := d.unquoteBytes(item)
|
s, ok := unquoteBytes(item)
|
||||||
if !ok {
|
if !ok {
|
||||||
if fromQuoted {
|
if fromQuoted {
|
||||||
return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())
|
return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())
|
||||||
|
|
@ -947,7 +930,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
case '"': // string
|
case '"': // string
|
||||||
s, ok := d.unquoteBytes(item)
|
s, ok := unquoteBytes(item)
|
||||||
if !ok {
|
if !ok {
|
||||||
if fromQuoted {
|
if fromQuoted {
|
||||||
return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())
|
return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())
|
||||||
|
|
@ -1107,7 +1090,7 @@ func (d *decodeState) objectInterface() map[string]interface{} {
|
||||||
start := d.readIndex()
|
start := d.readIndex()
|
||||||
d.rescanLiteral()
|
d.rescanLiteral()
|
||||||
item := d.data[start:d.readIndex()]
|
item := d.data[start:d.readIndex()]
|
||||||
key, ok := d.unquote(item)
|
key, ok := unquote(item)
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(phasePanicMsg)
|
panic(phasePanicMsg)
|
||||||
}
|
}
|
||||||
|
|
@ -1156,7 +1139,7 @@ func (d *decodeState) literalInterface() interface{} {
|
||||||
return c == 't'
|
return c == 't'
|
||||||
|
|
||||||
case '"': // string
|
case '"': // string
|
||||||
s, ok := d.unquote(item)
|
s, ok := unquote(item)
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(phasePanicMsg)
|
panic(phasePanicMsg)
|
||||||
}
|
}
|
||||||
|
|
@ -1199,33 +1182,40 @@ func getu4(s []byte) rune {
|
||||||
|
|
||||||
// unquote converts a quoted JSON string literal s into an actual string t.
|
// unquote converts a quoted JSON string literal s into an actual string t.
|
||||||
// The rules are different than for Go, so cannot use strconv.Unquote.
|
// The rules are different than for Go, so cannot use strconv.Unquote.
|
||||||
// The first byte in s must be '"'.
|
func unquote(s []byte) (t string, ok bool) {
|
||||||
func (d *decodeState) unquote(s []byte) (t string, ok bool) {
|
s, ok = unquoteBytes(s)
|
||||||
s, ok = d.unquoteBytes(s)
|
|
||||||
t = string(s)
|
t = string(s)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decodeState) unquoteBytes(s []byte) (t []byte, ok bool) {
|
func unquoteBytes(s []byte) (t []byte, ok bool) {
|
||||||
// We already know that s[0] == '"'. However, we don't know that the
|
if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' {
|
||||||
// closing quote exists in all cases, such as when the string is nested
|
|
||||||
// via the ",string" option.
|
|
||||||
if len(s) < 2 || s[len(s)-1] != '"' {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s = s[1 : len(s)-1]
|
s = s[1 : len(s)-1]
|
||||||
|
|
||||||
// If there are no unusual characters, no unquoting is needed, so return
|
// Check for unusual characters. If there are none,
|
||||||
// a slice of the original bytes.
|
// then no unquoting is needed, so return a slice of the
|
||||||
r := d.safeUnquote
|
// original bytes.
|
||||||
if r == -1 {
|
r := 0
|
||||||
|
for r < len(s) {
|
||||||
|
c := s[r]
|
||||||
|
if c == '\\' || c == '"' || c < ' ' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if c < utf8.RuneSelf {
|
||||||
|
r++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rr, size := utf8.DecodeRune(s[r:])
|
||||||
|
if rr == utf8.RuneError && size == 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
r += size
|
||||||
|
}
|
||||||
|
if r == len(s) {
|
||||||
return s, true
|
return s, true
|
||||||
}
|
}
|
||||||
// Only perform up to one safe unquote for each re-scanned string
|
|
||||||
// literal. In some edge cases, the decoder unquotes a literal a second
|
|
||||||
// time, even after another literal has been re-scanned. Thus, only the
|
|
||||||
// first unquote can safely use safeUnquote.
|
|
||||||
d.safeUnquote = 0
|
|
||||||
|
|
||||||
b := make([]byte, len(s)+2*utf8.UTFMax)
|
b := make([]byte, len(s)+2*utf8.UTFMax)
|
||||||
w := copy(b, s[0:r])
|
w := copy(b, s[0:r])
|
||||||
|
|
|
||||||
|
|
@ -2472,6 +2472,22 @@ func TestUnmarshalRescanLiteralMangledUnquote(t *testing.T) {
|
||||||
if t1 != t2 {
|
if t1 != t2 {
|
||||||
t.Errorf("Marshal and Unmarshal roundtrip mismatch: want %q got %q", t1, t2)
|
t.Errorf("Marshal and Unmarshal roundtrip mismatch: want %q got %q", t1, t2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See golang.org/issues/39555.
|
||||||
|
input := map[textUnmarshalerString]string{"FOO": "", `"`: ""}
|
||||||
|
|
||||||
|
encoded, err := Marshal(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Marshal unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
var got map[textUnmarshalerString]string
|
||||||
|
if err := Unmarshal(encoded, &got); err != nil {
|
||||||
|
t.Fatalf("Unmarshal unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
want := map[textUnmarshalerString]string{"foo": "", `"`: ""}
|
||||||
|
if !reflect.DeepEqual(want, got) {
|
||||||
|
t.Fatalf("Unexpected roundtrip result:\nwant: %q\ngot: %q", want, got)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalMaxDepth(t *testing.T) {
|
func TestUnmarshalMaxDepth(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,11 @@ func (pos Position) String() string {
|
||||||
// larger, representation.
|
// larger, representation.
|
||||||
//
|
//
|
||||||
// The Pos value for a given file is a number in the range [base, base+size],
|
// The Pos value for a given file is a number in the range [base, base+size],
|
||||||
// where base and size are specified when adding the file to the file set via
|
// where base and size are specified when a file is added to the file set.
|
||||||
// AddFile.
|
// The difference between a Pos value and the corresponding file base
|
||||||
|
// corresponds to the byte offset of that position (represented by the Pos value)
|
||||||
|
// from the beginning of the file. Thus, the file base offset is the Pos value
|
||||||
|
// representing the first byte in the file.
|
||||||
//
|
//
|
||||||
// To create the Pos value for a specific source offset (measured in bytes),
|
// To create the Pos value for a specific source offset (measured in bytes),
|
||||||
// first add the respective file to the current file set using FileSet.AddFile
|
// first add the respective file to the current file set using FileSet.AddFile
|
||||||
|
|
@ -364,6 +367,22 @@ func (f *File) Position(p Pos) (pos Position) {
|
||||||
// Methods of file sets are synchronized; multiple goroutines
|
// Methods of file sets are synchronized; multiple goroutines
|
||||||
// may invoke them concurrently.
|
// may invoke them concurrently.
|
||||||
//
|
//
|
||||||
|
// The byte offsets for each file in a file set are mapped into
|
||||||
|
// distinct (integer) intervals, one interval [base, base+size]
|
||||||
|
// per file. Base represents the first byte in the file, and size
|
||||||
|
// is the corresponding file size. A Pos value is a value in such
|
||||||
|
// an interval. By determining the interval a Pos value belongs
|
||||||
|
// to, the file, its file base, and thus the byte offset (position)
|
||||||
|
// the Pos value is representing can be computed.
|
||||||
|
//
|
||||||
|
// When adding a new file, a file base must be provided. That can
|
||||||
|
// be any integer value that is past the end of any interval of any
|
||||||
|
// file already in the file set. For convenience, FileSet.Base provides
|
||||||
|
// such a value, which is simply the end of the Pos interval of the most
|
||||||
|
// recently added file, plus one. Unless there is a need to extend an
|
||||||
|
// interval later, using the FileSet.Base should be used as argument
|
||||||
|
// for FileSet.AddFile.
|
||||||
|
//
|
||||||
type FileSet struct {
|
type FileSet struct {
|
||||||
mutex sync.RWMutex // protects the file set
|
mutex sync.RWMutex // protects the file set
|
||||||
base int // base offset for the next file
|
base int // base offset for the next file
|
||||||
|
|
|
||||||
|
|
@ -657,7 +657,6 @@ func TestEncodeWrappedImage(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkEncode(b *testing.B) {
|
func BenchmarkEncode(b *testing.B) {
|
||||||
bo := image.Rect(0, 0, 640, 480)
|
|
||||||
rnd := rand.New(rand.NewSource(123))
|
rnd := rand.New(rand.NewSource(123))
|
||||||
|
|
||||||
// Restrict to a 256-color paletted image to avoid quantization path.
|
// Restrict to a 256-color paletted image to avoid quantization path.
|
||||||
|
|
@ -671,10 +670,8 @@ func BenchmarkEncode(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
img := image.NewPaletted(image.Rect(0, 0, 640, 480), palette)
|
img := image.NewPaletted(image.Rect(0, 0, 640, 480), palette)
|
||||||
for y := bo.Min.Y; y < bo.Max.Y; y++ {
|
for i := range img.Pix {
|
||||||
for x := bo.Min.X; x < bo.Max.X; x++ {
|
img.Pix[i] = uint8(rnd.Intn(256))
|
||||||
img.Set(x, y, palette[rnd.Intn(256)])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
b.SetBytes(640 * 480 * 4)
|
b.SetBytes(640 * 480 * 4)
|
||||||
|
|
|
||||||
|
|
@ -511,6 +511,7 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
origReq := req
|
||||||
req = setupRewindBody(req)
|
req = setupRewindBody(req)
|
||||||
|
|
||||||
if altRT := t.alternateRoundTripper(req); altRT != nil {
|
if altRT := t.alternateRoundTripper(req); altRT != nil {
|
||||||
|
|
@ -572,6 +573,7 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) {
|
||||||
resp, err = pconn.roundTrip(treq)
|
resp, err = pconn.roundTrip(treq)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
resp.Request = origReq
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3511,7 +3511,8 @@ func TestRetryRequestsOnError(t *testing.T) {
|
||||||
|
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
res, err := c.Do(tc.req())
|
req := tc.req()
|
||||||
|
res, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if time.Since(t0) < MaxWriteWaitBeforeConnReuse/2 {
|
if time.Since(t0) < MaxWriteWaitBeforeConnReuse/2 {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
|
|
@ -3522,6 +3523,9 @@ func TestRetryRequestsOnError(t *testing.T) {
|
||||||
t.Skipf("connection likely wasn't recycled within %d, interfering with actual test; skipping", MaxWriteWaitBeforeConnReuse)
|
t.Skipf("connection likely wasn't recycled within %d, interfering with actual test; skipping", MaxWriteWaitBeforeConnReuse)
|
||||||
}
|
}
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
|
if res.Request != req {
|
||||||
|
t.Errorf("Response.Request != original request; want identical Request")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ type Call struct {
|
||||||
Args interface{} // The argument to the function (*struct).
|
Args interface{} // The argument to the function (*struct).
|
||||||
Reply interface{} // The reply from the function (*struct).
|
Reply interface{} // The reply from the function (*struct).
|
||||||
Error error // After completion, the error status.
|
Error error // After completion, the error status.
|
||||||
Done chan *Call // Strobes when call is complete.
|
Done chan *Call // Receives *Call when Go is complete.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client represents an RPC Client.
|
// Client represents an RPC Client.
|
||||||
|
|
|
||||||
|
|
@ -824,6 +824,11 @@ var loop1, loop2 Loop
|
||||||
var loopy1, loopy2 Loopy
|
var loopy1, loopy2 Loopy
|
||||||
var cycleMap1, cycleMap2, cycleMap3 map[string]interface{}
|
var cycleMap1, cycleMap2, cycleMap3 map[string]interface{}
|
||||||
|
|
||||||
|
type structWithSelfPtr struct {
|
||||||
|
p *structWithSelfPtr
|
||||||
|
s string
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
loop1 = &loop2
|
loop1 = &loop2
|
||||||
loop2 = &loop1
|
loop2 = &loop1
|
||||||
|
|
@ -880,6 +885,7 @@ var deepEqualTests = []DeepEqualTest{
|
||||||
{[]float64{math.NaN()}, self{}, true},
|
{[]float64{math.NaN()}, self{}, true},
|
||||||
{map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false},
|
{map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false},
|
||||||
{map[float64]float64{math.NaN(): 1}, self{}, true},
|
{map[float64]float64{math.NaN(): 1}, self{}, true},
|
||||||
|
{&structWithSelfPtr{p: &structWithSelfPtr{s: "a"}}, &structWithSelfPtr{p: &structWithSelfPtr{s: "b"}}, false},
|
||||||
|
|
||||||
// Nil vs empty: not the same.
|
// Nil vs empty: not the same.
|
||||||
{[]int{}, []int(nil), false},
|
{[]int{}, []int(nil), false},
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,20 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
if hard(v1, v2) {
|
if hard(v1, v2) {
|
||||||
addr1 := v1.ptr
|
// For a Ptr or Map value, we need to check flagIndir,
|
||||||
addr2 := v2.ptr
|
// which we do by calling the pointer method.
|
||||||
|
// For Slice or Interface, flagIndir is always set,
|
||||||
|
// and using v.ptr suffices.
|
||||||
|
ptrval := func(v Value) unsafe.Pointer {
|
||||||
|
switch v.Kind() {
|
||||||
|
case Ptr, Map:
|
||||||
|
return v.pointer()
|
||||||
|
default:
|
||||||
|
return v.ptr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addr1 := ptrval(v1)
|
||||||
|
addr2 := ptrval(v2)
|
||||||
if uintptr(addr1) > uintptr(addr2) {
|
if uintptr(addr1) > uintptr(addr2) {
|
||||||
// Canonicalize order to reduce number of entries in visited.
|
// Canonicalize order to reduce number of entries in visited.
|
||||||
// Assumes non-moving garbage collector.
|
// Assumes non-moving garbage collector.
|
||||||
|
|
|
||||||
|
|
@ -3068,6 +3068,7 @@ func ifaceIndir(t *rtype) bool {
|
||||||
return t.kind&kindDirectIface == 0
|
return t.kind&kindDirectIface == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: this type must agree with runtime.bitvector.
|
||||||
type bitVector struct {
|
type bitVector struct {
|
||||||
n uint32 // number of bits
|
n uint32 // number of bits
|
||||||
data []byte
|
data []byte
|
||||||
|
|
|
||||||
|
|
@ -589,6 +589,13 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool) {
|
||||||
// Convert v to type typ if v is assignable to a variable
|
// Convert v to type typ if v is assignable to a variable
|
||||||
// of type t in the language spec.
|
// of type t in the language spec.
|
||||||
// See issue 28761.
|
// See issue 28761.
|
||||||
|
if typ.Kind() == Interface {
|
||||||
|
// We must clear the destination before calling assignTo,
|
||||||
|
// in case assignTo writes (with memory barriers) to the
|
||||||
|
// target location used as scratch space. See issue 39541.
|
||||||
|
*(*uintptr)(addr) = 0
|
||||||
|
*(*uintptr)(add(addr, ptrSize, "typ.size == 2*ptrSize")) = 0
|
||||||
|
}
|
||||||
v = v.assignTo("reflect.MakeFunc", typ, addr)
|
v = v.assignTo("reflect.MakeFunc", typ, addr)
|
||||||
|
|
||||||
// We are writing to stack. No write barrier.
|
// We are writing to stack. No write barrier.
|
||||||
|
|
@ -2381,6 +2388,7 @@ func NewAt(typ Type, p unsafe.Pointer) Value {
|
||||||
// assignTo returns a value v that can be assigned directly to typ.
|
// assignTo returns a value v that can be assigned directly to typ.
|
||||||
// It panics if v is not assignable to typ.
|
// It panics if v is not assignable to typ.
|
||||||
// For a conversion to an interface type, target is a suggested scratch space to use.
|
// For a conversion to an interface type, target is a suggested scratch space to use.
|
||||||
|
// target must be initialized memory (or nil).
|
||||||
func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value {
|
func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value {
|
||||||
if v.flag&flagMethod != 0 {
|
if v.flag&flagMethod != 0 {
|
||||||
v = makeMethodValue(context, v)
|
v = makeMethodValue(context, v)
|
||||||
|
|
|
||||||
|
|
@ -12,57 +12,47 @@ import "unicode"
|
||||||
// See https://swtch.com/~rsc/regexp/regexp1.html for inspiration.
|
// See https://swtch.com/~rsc/regexp/regexp1.html for inspiration.
|
||||||
//
|
//
|
||||||
// These aren't really pointers: they're integers, so we can reinterpret them
|
// These aren't really pointers: they're integers, so we can reinterpret them
|
||||||
// this way without using package unsafe. A value l denotes
|
// this way without using package unsafe. A value l.head denotes
|
||||||
// p.inst[l>>1].Out (l&1==0) or .Arg (l&1==1).
|
// p.inst[l.head>>1].Out (l.head&1==0) or .Arg (l.head&1==1).
|
||||||
// l == 0 denotes the empty list, okay because we start every program
|
// head == 0 denotes the empty list, okay because we start every program
|
||||||
// with a fail instruction, so we'll never want to point at its output link.
|
// with a fail instruction, so we'll never want to point at its output link.
|
||||||
type patchList uint32
|
type patchList struct {
|
||||||
|
head, tail uint32
|
||||||
|
}
|
||||||
|
|
||||||
func (l patchList) next(p *Prog) patchList {
|
func makePatchList(n uint32) patchList {
|
||||||
i := &p.Inst[l>>1]
|
return patchList{n, n}
|
||||||
if l&1 == 0 {
|
|
||||||
return patchList(i.Out)
|
|
||||||
}
|
|
||||||
return patchList(i.Arg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l patchList) patch(p *Prog, val uint32) {
|
func (l patchList) patch(p *Prog, val uint32) {
|
||||||
for l != 0 {
|
head := l.head
|
||||||
i := &p.Inst[l>>1]
|
for head != 0 {
|
||||||
if l&1 == 0 {
|
i := &p.Inst[head>>1]
|
||||||
l = patchList(i.Out)
|
if head&1 == 0 {
|
||||||
|
head = i.Out
|
||||||
i.Out = val
|
i.Out = val
|
||||||
} else {
|
} else {
|
||||||
l = patchList(i.Arg)
|
head = i.Arg
|
||||||
i.Arg = val
|
i.Arg = val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l1 patchList) append(p *Prog, l2 patchList) patchList {
|
func (l1 patchList) append(p *Prog, l2 patchList) patchList {
|
||||||
if l1 == 0 {
|
if l1.head == 0 {
|
||||||
return l2
|
return l2
|
||||||
}
|
}
|
||||||
if l2 == 0 {
|
if l2.head == 0 {
|
||||||
return l1
|
return l1
|
||||||
}
|
}
|
||||||
|
|
||||||
last := l1
|
i := &p.Inst[l1.tail>>1]
|
||||||
for {
|
if l1.tail&1 == 0 {
|
||||||
next := last.next(p)
|
i.Out = l2.head
|
||||||
if next == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
last = next
|
|
||||||
}
|
|
||||||
|
|
||||||
i := &p.Inst[last>>1]
|
|
||||||
if last&1 == 0 {
|
|
||||||
i.Out = uint32(l2)
|
|
||||||
} else {
|
} else {
|
||||||
i.Arg = uint32(l2)
|
i.Arg = l2.head
|
||||||
}
|
}
|
||||||
return l1
|
return patchList{l1.head, l2.tail}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A frag represents a compiled program fragment.
|
// A frag represents a compiled program fragment.
|
||||||
|
|
@ -176,7 +166,7 @@ func (c *compiler) inst(op InstOp) frag {
|
||||||
|
|
||||||
func (c *compiler) nop() frag {
|
func (c *compiler) nop() frag {
|
||||||
f := c.inst(InstNop)
|
f := c.inst(InstNop)
|
||||||
f.out = patchList(f.i << 1)
|
f.out = makePatchList(f.i << 1)
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,7 +176,7 @@ func (c *compiler) fail() frag {
|
||||||
|
|
||||||
func (c *compiler) cap(arg uint32) frag {
|
func (c *compiler) cap(arg uint32) frag {
|
||||||
f := c.inst(InstCapture)
|
f := c.inst(InstCapture)
|
||||||
f.out = patchList(f.i << 1)
|
f.out = makePatchList(f.i << 1)
|
||||||
c.p.Inst[f.i].Arg = arg
|
c.p.Inst[f.i].Arg = arg
|
||||||
|
|
||||||
if c.p.NumCap < int(arg)+1 {
|
if c.p.NumCap < int(arg)+1 {
|
||||||
|
|
@ -229,10 +219,10 @@ func (c *compiler) quest(f1 frag, nongreedy bool) frag {
|
||||||
i := &c.p.Inst[f.i]
|
i := &c.p.Inst[f.i]
|
||||||
if nongreedy {
|
if nongreedy {
|
||||||
i.Arg = f1.i
|
i.Arg = f1.i
|
||||||
f.out = patchList(f.i << 1)
|
f.out = makePatchList(f.i << 1)
|
||||||
} else {
|
} else {
|
||||||
i.Out = f1.i
|
i.Out = f1.i
|
||||||
f.out = patchList(f.i<<1 | 1)
|
f.out = makePatchList(f.i<<1 | 1)
|
||||||
}
|
}
|
||||||
f.out = f.out.append(c.p, f1.out)
|
f.out = f.out.append(c.p, f1.out)
|
||||||
return f
|
return f
|
||||||
|
|
@ -243,10 +233,10 @@ func (c *compiler) star(f1 frag, nongreedy bool) frag {
|
||||||
i := &c.p.Inst[f.i]
|
i := &c.p.Inst[f.i]
|
||||||
if nongreedy {
|
if nongreedy {
|
||||||
i.Arg = f1.i
|
i.Arg = f1.i
|
||||||
f.out = patchList(f.i << 1)
|
f.out = makePatchList(f.i << 1)
|
||||||
} else {
|
} else {
|
||||||
i.Out = f1.i
|
i.Out = f1.i
|
||||||
f.out = patchList(f.i<<1 | 1)
|
f.out = makePatchList(f.i<<1 | 1)
|
||||||
}
|
}
|
||||||
f1.out.patch(c.p, f.i)
|
f1.out.patch(c.p, f.i)
|
||||||
return f
|
return f
|
||||||
|
|
@ -259,7 +249,7 @@ func (c *compiler) plus(f1 frag, nongreedy bool) frag {
|
||||||
func (c *compiler) empty(op EmptyOp) frag {
|
func (c *compiler) empty(op EmptyOp) frag {
|
||||||
f := c.inst(InstEmptyWidth)
|
f := c.inst(InstEmptyWidth)
|
||||||
c.p.Inst[f.i].Arg = uint32(op)
|
c.p.Inst[f.i].Arg = uint32(op)
|
||||||
f.out = patchList(f.i << 1)
|
f.out = makePatchList(f.i << 1)
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -273,7 +263,7 @@ func (c *compiler) rune(r []rune, flags Flags) frag {
|
||||||
flags &^= FoldCase
|
flags &^= FoldCase
|
||||||
}
|
}
|
||||||
i.Arg = uint32(flags)
|
i.Arg = uint32(flags)
|
||||||
f.out = patchList(f.i << 1)
|
f.out = makePatchList(f.i << 1)
|
||||||
|
|
||||||
// Special cases for exec machine.
|
// Special cases for exec machine.
|
||||||
switch {
|
switch {
|
||||||
|
|
|
||||||
|
|
@ -6,45 +6,117 @@ package runtime
|
||||||
|
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var tracebackbuf [128]byte
|
const (
|
||||||
|
// Plan 9 environment device
|
||||||
|
envDir = "/env/"
|
||||||
|
// size of buffer to read from a directory
|
||||||
|
dirBufSize = 4096
|
||||||
|
// size of buffer to read an environment variable (may grow)
|
||||||
|
envBufSize = 128
|
||||||
|
// offset of the name field in a 9P directory entry - see syscall.UnmarshalDir()
|
||||||
|
nameOffset = 39
|
||||||
|
)
|
||||||
|
|
||||||
func gogetenv(key string) string {
|
// Goenvs caches the Plan 9 environment variables at start of execution into
|
||||||
var file [128]byte
|
// string array envs, to supply the initial contents for os.Environ.
|
||||||
if len(key) > len(file)-6 {
|
// Subsequent calls to os.Setenv will change this cache, without writing back
|
||||||
return ""
|
// to the (possibly shared) Plan 9 environment, so that Setenv and Getenv
|
||||||
|
// conform to the same Posix semantics as on other operating systems.
|
||||||
|
// For Plan 9 shared environment semantics, instead of Getenv(key) and
|
||||||
|
// Setenv(key, value), one can use ioutil.ReadFile("/env/" + key) and
|
||||||
|
// ioutil.WriteFile("/env/" + key, value, 0666) respectively.
|
||||||
|
//go:nosplit
|
||||||
|
func goenvs() {
|
||||||
|
buf := make([]byte, envBufSize)
|
||||||
|
copy(buf, envDir)
|
||||||
|
dirfd := open(&buf[0], _OREAD, 0)
|
||||||
|
if dirfd < 0 {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
defer closefd(dirfd)
|
||||||
copy(file[:], "/env/")
|
dofiles(dirfd, func(name []byte) {
|
||||||
copy(file[5:], key)
|
name = append(name, 0)
|
||||||
|
buf = buf[:len(envDir)]
|
||||||
fd := open(&file[0], _OREAD, 0)
|
copy(buf, envDir)
|
||||||
if fd < 0 {
|
buf = append(buf, name...)
|
||||||
return ""
|
fd := open(&buf[0], _OREAD, 0)
|
||||||
}
|
if fd < 0 {
|
||||||
n := seek(fd, 0, 2)
|
return
|
||||||
if n <= 0 {
|
}
|
||||||
closefd(fd)
|
defer closefd(fd)
|
||||||
return ""
|
n := len(buf)
|
||||||
}
|
r := 0
|
||||||
|
for {
|
||||||
p := make([]byte, n)
|
r = int(pread(fd, unsafe.Pointer(&buf[0]), int32(n), 0))
|
||||||
|
if r < n {
|
||||||
r := pread(fd, unsafe.Pointer(&p[0]), int32(n), 0)
|
break
|
||||||
closefd(fd)
|
}
|
||||||
if r < 0 {
|
n = int(seek(fd, 0, 2)) + 1
|
||||||
return ""
|
if len(buf) < n {
|
||||||
}
|
buf = make([]byte, n)
|
||||||
|
}
|
||||||
if p[r-1] == 0 {
|
}
|
||||||
r--
|
if r <= 0 {
|
||||||
}
|
r = 0
|
||||||
|
} else if buf[r-1] == 0 {
|
||||||
var s string
|
r--
|
||||||
sp := stringStructOf(&s)
|
}
|
||||||
sp.str = unsafe.Pointer(&p[0])
|
name[len(name)-1] = '='
|
||||||
sp.len = int(r)
|
env := make([]byte, len(name)+r)
|
||||||
return s
|
copy(env, name)
|
||||||
|
copy(env[len(name):], buf[:r])
|
||||||
|
envs = append(envs, string(env))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var _cgo_setenv unsafe.Pointer // pointer to C function
|
// Dofiles reads the directory opened with file descriptor fd, applying function f
|
||||||
var _cgo_unsetenv unsafe.Pointer // pointer to C function
|
// to each filename in it.
|
||||||
|
//go:nosplit
|
||||||
|
func dofiles(dirfd int32, f func([]byte)) {
|
||||||
|
dirbuf := new([dirBufSize]byte)
|
||||||
|
|
||||||
|
var off int64 = 0
|
||||||
|
for {
|
||||||
|
n := pread(dirfd, unsafe.Pointer(&dirbuf[0]), int32(dirBufSize), off)
|
||||||
|
if n <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for b := dirbuf[:n]; len(b) > 0; {
|
||||||
|
var name []byte
|
||||||
|
name, b = gdirname(b)
|
||||||
|
if name == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f(name)
|
||||||
|
}
|
||||||
|
off += int64(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gdirname returns the first filename from a buffer of directory entries,
|
||||||
|
// and a slice containing the remaining directory entries.
|
||||||
|
// If the buffer doesn't start with a valid directory entry, the returned name is nil.
|
||||||
|
//go:nosplit
|
||||||
|
func gdirname(buf []byte) (name []byte, rest []byte) {
|
||||||
|
if 2+nameOffset+2 > len(buf) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
entryLen, buf := gbit16(buf)
|
||||||
|
if entryLen > len(buf) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n, b := gbit16(buf[nameOffset:])
|
||||||
|
if n > len(b) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name = b[:n]
|
||||||
|
rest = buf[entryLen:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gbit16 reads a 16-bit little-endian binary number from b and returns it
|
||||||
|
// with the remaining slice of b.
|
||||||
|
//go:nosplit
|
||||||
|
func gbit16(b []byte) (int, []byte) {
|
||||||
|
return int(b[0]) | int(b[1])<<8, b[2:]
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows
|
// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows plan9
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFixedGOROOT(t *testing.T) {
|
func TestFixedGOROOT(t *testing.T) {
|
||||||
if runtime.GOOS == "plan9" {
|
|
||||||
t.Skipf("skipping plan9, it is inconsistent by allowing GOROOT to be updated by Setenv")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore both the real GOROOT environment variable, and runtime's copies:
|
// Restore both the real GOROOT environment variable, and runtime's copies:
|
||||||
if orig, ok := syscall.Getenv("GOROOT"); ok {
|
if orig, ok := syscall.Getenv("GOROOT"); ok {
|
||||||
defer syscall.Setenv("GOROOT", orig)
|
defer syscall.Setenv("GOROOT", orig)
|
||||||
|
|
|
||||||
|
|
@ -152,14 +152,13 @@ func (s *HashSet) addS_seed(x string, seed uintptr) {
|
||||||
s.add(StringHash(x, seed))
|
s.add(StringHash(x, seed))
|
||||||
}
|
}
|
||||||
func (s *HashSet) check(t *testing.T) {
|
func (s *HashSet) check(t *testing.T) {
|
||||||
const SLOP = 10.0
|
const SLOP = 50.0
|
||||||
collisions := s.n - len(s.m)
|
collisions := s.n - len(s.m)
|
||||||
//fmt.Printf("%d/%d\n", len(s.m), s.n)
|
|
||||||
pairs := int64(s.n) * int64(s.n-1) / 2
|
pairs := int64(s.n) * int64(s.n-1) / 2
|
||||||
expected := float64(pairs) / math.Pow(2.0, float64(hashSize))
|
expected := float64(pairs) / math.Pow(2.0, float64(hashSize))
|
||||||
stddev := math.Sqrt(expected)
|
stddev := math.Sqrt(expected)
|
||||||
if float64(collisions) > expected+SLOP*(3*stddev+1) {
|
if float64(collisions) > expected+SLOP*(3*stddev+1) {
|
||||||
t.Errorf("unexpected number of collisions: got=%d mean=%f stddev=%f", collisions, expected, stddev)
|
t.Errorf("unexpected number of collisions: got=%d mean=%f stddev=%f threshold=%f", collisions, expected, stddev, expected+SLOP*(3*stddev+1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -564,8 +563,11 @@ func avalancheTest1(t *testing.T, k Key) {
|
||||||
|
|
||||||
// All bit rotations of a set of distinct keys
|
// All bit rotations of a set of distinct keys
|
||||||
func TestSmhasherWindowed(t *testing.T) {
|
func TestSmhasherWindowed(t *testing.T) {
|
||||||
|
t.Logf("32 bit keys")
|
||||||
windowed(t, &Int32Key{})
|
windowed(t, &Int32Key{})
|
||||||
|
t.Logf("64 bit keys")
|
||||||
windowed(t, &Int64Key{})
|
windowed(t, &Int64Key{})
|
||||||
|
t.Logf("string keys")
|
||||||
windowed(t, &BytesKey{make([]byte, 128)})
|
windowed(t, &BytesKey{make([]byte, 128)})
|
||||||
}
|
}
|
||||||
func windowed(t *testing.T, k Key) {
|
func windowed(t *testing.T, k Key) {
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@ func netpoll(delay int64) gList {
|
||||||
|
|
||||||
notetsleep(&netpollNote, delay)
|
notetsleep(&netpollNote, delay)
|
||||||
unlock(&netpollStubLock)
|
unlock(&netpollStubLock)
|
||||||
|
// Guard against starvation in case the lock is contended
|
||||||
|
// (eg when running TestNetpollBreak).
|
||||||
|
osyield()
|
||||||
}
|
}
|
||||||
return gList{}
|
return gList{}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -306,9 +306,6 @@ func getRandomData(r []byte) {
|
||||||
extendRandom(r, 0)
|
extendRandom(r, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func goenvs() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func initsig(preinit bool) {
|
func initsig(preinit bool) {
|
||||||
if !preinit {
|
if !preinit {
|
||||||
notify(unsafe.Pointer(funcPC(sigtramp)))
|
notify(unsafe.Pointer(funcPC(sigtramp)))
|
||||||
|
|
|
||||||
|
|
@ -432,6 +432,8 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setg(g.m.gsignal)
|
||||||
|
|
||||||
// If some non-Go code called sigaltstack, adjust.
|
// If some non-Go code called sigaltstack, adjust.
|
||||||
var gsignalStack gsignalStack
|
var gsignalStack gsignalStack
|
||||||
setStack := adjustSignalStack(sig, g.m, &gsignalStack)
|
setStack := adjustSignalStack(sig, g.m, &gsignalStack)
|
||||||
|
|
@ -439,8 +441,6 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
|
||||||
g.m.gsignal.stktopsp = getcallersp()
|
g.m.gsignal.stktopsp = getcallersp()
|
||||||
}
|
}
|
||||||
|
|
||||||
setg(g.m.gsignal)
|
|
||||||
|
|
||||||
if g.stackguard0 == stackFork {
|
if g.stackguard0 == stackFork {
|
||||||
signalDuringFork(sig)
|
signalDuringFork(sig)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -556,6 +556,7 @@ func adjustpointer(adjinfo *adjustinfo, vpp unsafe.Pointer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Information from the compiler about the layout of stack frames.
|
// Information from the compiler about the layout of stack frames.
|
||||||
|
// Note: this type must agree with reflect.bitVector.
|
||||||
type bitvector struct {
|
type bitvector struct {
|
||||||
n int32 // # of bits
|
n int32 // # of bits
|
||||||
bytedata *uint8
|
bytedata *uint8
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
// command runs the test in the current directory and writes the trace
|
// command runs the test in the current directory and writes the trace
|
||||||
// file (trace.out).
|
// file (trace.out).
|
||||||
//
|
//
|
||||||
// go test -trace=test.out
|
// go test -trace=trace.out
|
||||||
//
|
//
|
||||||
// This runtime/trace package provides APIs to add equivalent tracing
|
// This runtime/trace package provides APIs to add equivalent tracing
|
||||||
// support to a standalone program. See the Example that demonstrates
|
// support to a standalone program. See the Example that demonstrates
|
||||||
|
|
|
||||||
|
|
@ -1,122 +0,0 @@
|
||||||
// Copyright 2011 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.
|
|
||||||
|
|
||||||
// Plan 9 environment variables.
|
|
||||||
|
|
||||||
package syscall
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errZeroLengthKey = errors.New("zero length key")
|
|
||||||
errShortWrite = errors.New("i/o count too small")
|
|
||||||
)
|
|
||||||
|
|
||||||
func readenv(key string) (string, error) {
|
|
||||||
fd, err := open("/env/"+key, O_RDONLY)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer Close(fd)
|
|
||||||
l, _ := Seek(fd, 0, 2)
|
|
||||||
Seek(fd, 0, 0)
|
|
||||||
buf := make([]byte, l)
|
|
||||||
n, err := Read(fd, buf)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if n > 0 && buf[n-1] == 0 {
|
|
||||||
buf = buf[:n-1]
|
|
||||||
}
|
|
||||||
return string(buf), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeenv(key, value string) error {
|
|
||||||
fd, err := create("/env/"+key, O_RDWR, 0666)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer Close(fd)
|
|
||||||
b := []byte(value)
|
|
||||||
n, err := Write(fd, b)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if n != len(b) {
|
|
||||||
return errShortWrite
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Getenv(key string) (value string, found bool) {
|
|
||||||
if len(key) == 0 {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
v, err := readenv(key)
|
|
||||||
if err != nil {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
return v, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func Setenv(key, value string) error {
|
|
||||||
if len(key) == 0 {
|
|
||||||
return errZeroLengthKey
|
|
||||||
}
|
|
||||||
err := writeenv(key, value)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Clearenv() {
|
|
||||||
// Creating a new environment group using rfork(RFCENVG) can race
|
|
||||||
// with access to files in /env (e.g. from Setenv or Getenv).
|
|
||||||
// Remove all environment variables in current environment group instead.
|
|
||||||
fd, err := open("/env", O_RDONLY)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer Close(fd)
|
|
||||||
files, err := readdirnames(fd)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, key := range files {
|
|
||||||
Remove("/env/" + key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Unsetenv(key string) error {
|
|
||||||
if len(key) == 0 {
|
|
||||||
return errZeroLengthKey
|
|
||||||
}
|
|
||||||
Remove("/env/" + key)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Environ() []string {
|
|
||||||
fd, err := open("/env", O_RDONLY)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
defer Close(fd)
|
|
||||||
files, err := readdirnames(fd)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ret := make([]string, 0, len(files))
|
|
||||||
|
|
||||||
for _, key := range files {
|
|
||||||
v, err := readenv(key)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ret = append(ret, key+"="+v)
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
@ -2,13 +2,16 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris
|
// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris plan9
|
||||||
|
|
||||||
// Unix environment variables.
|
// Unix environment variables.
|
||||||
|
|
||||||
package syscall
|
package syscall
|
||||||
|
|
||||||
import "sync"
|
import (
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// envOnce guards initialization by copyenv, which populates env.
|
// envOnce guards initialization by copyenv, which populates env.
|
||||||
|
|
@ -100,9 +103,12 @@ func Setenv(key, value string) error {
|
||||||
return EINVAL
|
return EINVAL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < len(value); i++ {
|
// On Plan 9, null is used as a separator, eg in $path.
|
||||||
if value[i] == 0 {
|
if runtime.GOOS != "plan9" {
|
||||||
return EINVAL
|
for i := 0; i < len(value); i++ {
|
||||||
|
if value[i] == 0 {
|
||||||
|
return EINVAL
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
33
test/fixedbugs/issue39541.go
Normal file
33
test/fixedbugs/issue39541.go
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
// run
|
||||||
|
|
||||||
|
// Copyright 2020 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 "reflect"
|
||||||
|
|
||||||
|
func sub(args []reflect.Value) []reflect.Value {
|
||||||
|
type A struct {
|
||||||
|
s int
|
||||||
|
t int
|
||||||
|
}
|
||||||
|
return []reflect.Value{reflect.ValueOf(A{1, 2})}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
f := reflect.MakeFunc(reflect.TypeOf((func() interface{})(nil)), sub).Interface().(func() interface{})
|
||||||
|
c := make(chan bool, 100)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
go func() {
|
||||||
|
for j := 0; j < 10000; j++ {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
c <- true
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
<-c
|
||||||
|
}
|
||||||
|
}
|
||||||
26
test/fixedbugs/issue39651.go
Normal file
26
test/fixedbugs/issue39651.go
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
// run
|
||||||
|
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Test that float -> integer conversion doesn't clobber
|
||||||
|
// flags.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
//go:noinline
|
||||||
|
func f(x, y float64, a, b *bool, r *int64) {
|
||||||
|
*a = x < y // set flags
|
||||||
|
*r = int64(x) // clobber flags
|
||||||
|
*b = x == y // use flags
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var a, b bool
|
||||||
|
var r int64
|
||||||
|
f(1, 1, &a, &b, &r)
|
||||||
|
if a || !b {
|
||||||
|
panic("comparison incorrect")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,22 +12,65 @@ import "fmt"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
type A [2]interface{}
|
type A [2]interface{}
|
||||||
|
type A2 [6]interface{}
|
||||||
type S struct{ x, y interface{} }
|
type S struct{ x, y interface{} }
|
||||||
|
type S2 struct{ x, y, z, a, b, c interface{} }
|
||||||
|
type T1 struct {
|
||||||
|
i interface{}
|
||||||
|
a int64
|
||||||
|
j interface{}
|
||||||
|
}
|
||||||
|
type T2 struct {
|
||||||
|
i interface{}
|
||||||
|
a, b, c int64
|
||||||
|
j interface{}
|
||||||
|
}
|
||||||
|
type T3 struct {
|
||||||
|
i interface{}
|
||||||
|
s string
|
||||||
|
j interface{}
|
||||||
|
}
|
||||||
|
b := []byte{1}
|
||||||
|
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
panic bool
|
panic bool
|
||||||
a, b interface{}
|
a, b interface{}
|
||||||
}{
|
}{
|
||||||
{false, A{1, []byte{1}}, A{2, []byte{1}}},
|
{false, A{1, b}, A{2, b}},
|
||||||
{true, A{[]byte{1}, 1}, A{[]byte{1}, 2}},
|
{true, A{b, 1}, A{b, 2}},
|
||||||
{false, S{1, []byte{1}}, S{2, []byte{1}}},
|
{false, A{1, b}, A{"2", b}},
|
||||||
{true, S{[]byte{1}, 1}, S{[]byte{1}, 2}},
|
{true, A{b, 1}, A{b, "2"}},
|
||||||
{false, A{1, []byte{1}}, A{"2", []byte{1}}},
|
|
||||||
{true, A{[]byte{1}, 1}, A{[]byte{1}, "2"}},
|
{false, A2{1, b}, A2{2, b}},
|
||||||
{false, S{1, []byte{1}}, S{"2", []byte{1}}},
|
{true, A2{b, 1}, A2{b, 2}},
|
||||||
{true, S{[]byte{1}, 1}, S{[]byte{1}, "2"}},
|
{false, A2{1, b}, A2{"2", b}},
|
||||||
|
{true, A2{b, 1}, A2{b, "2"}},
|
||||||
|
|
||||||
|
{false, S{1, b}, S{2, b}},
|
||||||
|
{true, S{b, 1}, S{b, 2}},
|
||||||
|
{false, S{1, b}, S{"2", b}},
|
||||||
|
{true, S{b, 1}, S{b, "2"}},
|
||||||
|
|
||||||
|
{false, S2{x: 1, y: b}, S2{x: 2, y: b}},
|
||||||
|
{true, S2{x: b, y: 1}, S2{x: b, y: 2}},
|
||||||
|
{false, S2{x: 1, y: b}, S2{x: "2", y: b}},
|
||||||
|
{true, S2{x: b, y: 1}, S2{x: b, y: "2"}},
|
||||||
|
|
||||||
|
{true, T1{i: b, a: 1}, T1{i: b, a: 2}},
|
||||||
|
{false, T1{a: 1, j: b}, T1{a: 2, j: b}},
|
||||||
|
{true, T2{i: b, a: 1}, T2{i: b, a: 2}},
|
||||||
|
{false, T2{a: 1, j: b}, T2{a: 2, j: b}},
|
||||||
|
{true, T3{i: b, s: "foo"}, T3{i: b, s: "bar"}},
|
||||||
|
{false, T3{s: "foo", j: b}, T3{s: "bar", j: b}},
|
||||||
|
{true, T3{i: b, s: "fooz"}, T3{i: b, s: "bar"}},
|
||||||
|
{false, T3{s: "fooz", j: b}, T3{s: "bar", j: b}},
|
||||||
} {
|
} {
|
||||||
f := func() {
|
f := func() {
|
||||||
|
defer func() {
|
||||||
|
if recover() != nil {
|
||||||
|
panic(fmt.Sprintf("comparing %#v and %#v panicked", test.a, test.b))
|
||||||
|
}
|
||||||
|
}()
|
||||||
if test.a == test.b {
|
if test.a == test.b {
|
||||||
panic(fmt.Sprintf("values %#v and %#v should not be equal", test.a, test.b))
|
panic(fmt.Sprintf("values %#v and %#v should not be equal", test.a, test.b))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue