[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:
Cherry Zhang 2020-06-19 16:14:40 -04:00
commit 5e526e67e7
70 changed files with 2089 additions and 2929 deletions

View file

@ -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>

View file

@ -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>

View file

@ -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

View file

@ -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 -->

View 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

View file

@ -24,6 +24,7 @@ var filePrefixes = []string{
"issue37479", "issue37479",
"issue37621", "issue37621",
"issue38649", "issue38649",
"issue39534",
} }
func TestGoDefs(t *testing.T) { func TestGoDefs(t *testing.T) {

View file

@ -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)
} }
} }

View file

@ -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 {

View file

@ -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": "",

View file

@ -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:

View file

@ -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:

View file

@ -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)
} }
} }

View file

@ -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

View file

@ -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)

View file

@ -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)
} }

View file

@ -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 {

View 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

View 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

View 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()
}

View file

@ -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

View file

@ -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)

View file

@ -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.

View file

@ -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.

View file

@ -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

View file

@ -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"
} }

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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")
}
}

View file

@ -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 ""
} }

View file

@ -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",

View file

@ -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
) )

View file

@ -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=

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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")
}
}

View 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() {}

Binary file not shown.

View file

@ -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

View file

@ -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?",

View file

@ -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

View file

@ -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])

View file

@ -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) {

View file

@ -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

View 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)

View file

@ -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
} }

View file

@ -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()

View file

@ -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.

View file

@ -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},

View file

@ -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.

View file

@ -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

View file

@ -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)

View file

@ -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 {

View file

@ -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:]
}

View file

@ -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

View file

@ -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)

View file

@ -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) {

View file

@ -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{}
} }

View file

@ -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)))

View file

@ -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)
} }

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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
}
} }
} }

View 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
}
}

View 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")
}
}

View file

@ -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))
} }