[dev.typeparams] Merge branch 'master' into dev.typeparams

Change-Id: Ie8e697895b1d04ab79d35ef5a886f005be209756
This commit is contained in:
Robert Griesemer 2020-11-19 17:01:31 -08:00
commit b1ae0a0646
378 changed files with 18600 additions and 6479 deletions

View file

@ -418,7 +418,7 @@ close(c)
<code>linux/amd64</code>, <code>linux/ppc64le</code>, <code>linux/amd64</code>, <code>linux/ppc64le</code>,
<code>linux/arm64</code>, <code>freebsd/amd64</code>, <code>linux/arm64</code>, <code>freebsd/amd64</code>,
<code>netbsd/amd64</code>, <code>darwin/amd64</code>, <code>netbsd/amd64</code>, <code>darwin/amd64</code>,
and <code>windows/amd64</code>. <code>darwin/arm64</code>, and <code>windows/amd64</code>.
</p> </p>
<h2 id="Runtime_Overheads">Runtime Overhead</h2> <h2 id="Runtime_Overheads">Runtime Overhead</h2>

View file

@ -31,6 +31,29 @@ Do not send CLs removing the interior tags from such phrases.
<h2 id="ports">Ports</h2> <h2 id="ports">Ports</h2>
<h3 id="darwin">Darwin</h3>
<p><!-- golang.org/issue/38485, golang.org/issue/41385, many CLs -->
Go 1.16 adds support of 64-bit ARM architecture on macOS (also known as
Apple Silicon) with <code>GOOS=darwin</code>, <code>GOARCH=arm64</code>.
Like the <code>darwin/amd64</code> port, the <code>darwin/arm64</code>
port supports cgo, internal and external linking, <code>c-archive</code>,
<code>c-shared</code>, and <code>pie</code> build modes, and the race
detector.
</p>
<p>
The iOS port, which was previously <code>darwin/arm64</code>, is now
moved to <code>ios/arm64</code>. <code>GOOS=ios</code> implies the
<code>darwin</code> build tag, just as <code>GOOS=android</code>
implies the <code>linux</code> build tag.
</p>
<p><!-- golang.org/issue/42100, CL 263798 -->
The <code>ios/amd64</code> port is added, targetting the iOS simulator
running on AMD64-based macOS.
</p>
<h3 id="netbsd">NetBSD</h3> <h3 id="netbsd">NetBSD</h3>
<p><!-- golang.org/issue/30824 --> <p><!-- golang.org/issue/30824 -->
@ -59,6 +82,15 @@ Do not send CLs removing the interior tags from such phrases.
<h4 id="modules">Modules</h4> <h4 id="modules">Modules</h4>
<p><!-- golang.org/issue/40728 -->
Build commands like <code>go</code> <code>build</code> and <code>go</code>
<code>test</code> no longer modify <code>go.mod</code> and <code>go.sum</code>
by default. Instead, they report an error if a module requirement or checksum
needs to be added or updated (as if the <code>-mod=readonly</code> flag were
used). Module requirements and sums may be adjusted with <code>go</code>
<code>mod</code> <code>tidy</code> or <code>go</code> <code>get</code>.
</p>
<p><!-- golang.org/issue/40276 --> <p><!-- golang.org/issue/40276 -->
<code>go</code> <code>install</code> now accepts arguments with <code>go</code> <code>install</code> now accepts arguments with
version suffixes (for example, <code>go</code> <code>install</code> version suffixes (for example, <code>go</code> <code>install</code>
@ -71,6 +103,16 @@ Do not send CLs removing the interior tags from such phrases.
TODO: write and link to blog post TODO: write and link to blog post
</p> </p>
<p><!-- golang.org/issue/40276 -->
<code>go</code> <code>install</code>, with or without a version suffix (as
described above), is now the recommended way to build and install packages in
module mode. <code>go</code> <code>get</code> should be used with the
<code>-d</code> flag to adjust the current module's dependencies without
building packages, and use of <code>go</code> <code>get</code> to build and
install packages is deprecated. In a future release, the <code>-d</code> flag
will always be enabled.
</p>
<p><!-- golang.org/issue/24031 --> <p><!-- golang.org/issue/24031 -->
<code>retract</code> directives may now be used in a <code>go.mod</code> file <code>retract</code> directives may now be used in a <code>go.mod</code> file
to indicate that certain published versions of the module should not be used to indicate that certain published versions of the module should not be used
@ -110,6 +152,16 @@ Do not send CLs removing the interior tags from such phrases.
See <code>go</code> <code>help</code> <code>environment</code> for details. See <code>go</code> <code>help</code> <code>environment</code> for details.
</p> </p>
<h4 id="go-get"><code>go</code> <code>get</code></h4>
<p><!-- golang.org/cl/263267 -->
<code>go</code> <code>get</code> <code>example.com/mod@patch</code> now
requires that some version of <code>example.com/mod</code> already be
required by the main module.
(However, <code>go</code> <code>get</code> <code>-u=patch</code> continues
to patch even newly-added dependencies.)
</p>
<h4 id="all-pattern">The <code>all</code> pattern</h4> <h4 id="all-pattern">The <code>all</code> pattern</h4>
<p><!-- golang.org/cl/240623 --> <p><!-- golang.org/cl/240623 -->
@ -131,6 +183,18 @@ Do not send CLs removing the interior tags from such phrases.
being built. being built.
</p> </p>
<h4 id="i-flag">The <code>-i</code> build flag</h4>
<p><!-- golang.org/issue/41696 -->
The <code>-i</code> flag accepted by <code>go</code> <code>build</code>,
<code>go</code> <code>install</code>, and <code>go</code> <code>test</code> is
now deprecated. The <code>-i</code> flag instructs the <code>go</code> command
to install packages imported by packages named on the command line. Since
the build cache was introduced in Go 1.10, the <code>-i</code> flag no longer
has a significant effect on build times, and it causes errors when the install
directory is not writable.
</p>
<h4 id="list-buildid">The <code>list</code> command</h4> <h4 id="list-buildid">The <code>list</code> command</h4>
<p><!-- golang.org/cl/263542 --> <p><!-- golang.org/cl/263542 -->
@ -227,12 +291,52 @@ Do not send CLs removing the interior tags from such phrases.
<p><!-- CL 256897 --> <p><!-- CL 256897 -->
I/O operations on closing or closed TLS connections can now be detected using I/O operations on closing or closed TLS connections can now be detected using
the new <a href="/pkg/net/#ErrClosed">ErrClosed</a> error. A typical use the new <a href="/pkg/net/#ErrClosed">ErrClosed</a> error. A typical use
would be <code>errors.Is(err, net.ErrClosed)</code>. In earlier releases would be <code>errors.Is(err, net.ErrClosed)</code>. In earlier releases
the only way to reliably detect this case was to match the string returned the only way to reliably detect this case was to match the string returned
by the <code>Error</code> method with <code>"tls: use of closed connection"</code>. by the <code>Error</code> method with <code>"tls: use of closed connection"</code>.
</p> </p>
<p><!-- CL 266037 -->
A default deadline is set in <a href="/pkg/crypto/tls/#Conn.Close">Close</a>
before sending the close notify alert, in order to prevent blocking
indefinitely.
</p>
<p><!-- CL 246338 -->
<a href="/pkg/crypto/tls#Conn.HandshakeContext">(*Conn).HandshakeContext</a> was added to
allow the user to control cancellation of an in-progress TLS Handshake.
The context provided is propagated into the
<a href="/pkg/crypto/tls#ClientHelloInfo">ClientHelloInfo</a>
and <a href="/pkg/crypto/tls#CertificateRequestInfo">CertificateRequestInfo</a>
structs and accessible through the new
<a href="/pkg/crypto/tls#ClientHelloInfo.Context">(*ClientHelloInfo).Context</a>
and
<a href="/pkg/crypto/tls#CertificateRequestInfo.Context">
(*CertificateRequestInfo).Context
</a> methods respectively. Canceling the context after the handshake has finished
has no effect.
</p>
<p><!-- CL 239748 -->
Clients now ensure that the server selects
<a href="/pkg/crypto/tls/#ConnectionState.NegotiatedProtocol">
an ALPN protocol</a> from
<a href="/pkg/crypto/tls/#Config.NextProtos">
the list advertised by the client</a>.
</p>
<p><!-- CL 262857 -->
TLS servers will now prefer other AEAD cipher suites (such as ChaCha20Poly1305)
over AES-GCM cipher suites if either the client or server doesn't have AES hardware
support, unless the application set both
<a href="/pkg/crypto/tls/#Config.PreferServerCipherSuites"><code>Config.PreferServerCipherSuites</code></a>
and <a href="/pkg/crypto/tls/#Config.CipherSuites"><code>Config.CipherSuites</code></a>
or there are no other AEAD cipher suites supported.
The client is assumed not to have AES hardware support if it does not signal a
preference for AES-GCM cipher suites.
</p>
<h3 id="crypto/x509"><a href="/pkg/crypto/x509">crypto/x509</a></h3> <h3 id="crypto/x509"><a href="/pkg/crypto/x509">crypto/x509</a></h3>
<p><!-- CL 235078 --> <p><!-- CL 235078 -->
@ -250,6 +354,13 @@ Do not send CLs removing the interior tags from such phrases.
of a malformed certificate. of a malformed certificate.
</p> </p>
<p><!-- CL 233163 -->
A number of additional fields have been added to the
<a href="/pkg/crypto/x509/#CertificateRequest">CertificateRequest</a> type.
These fields are now parsed in <a href="/pkg/crypto/x509/#ParseCertificateRequest">ParseCertificateRequest</a>
and marshalled in <a href="/pkg/crypto/x509/#CreateCertificateRequest">CreateCertificateRequest</a>.
</p>
<h3 id="encoding/json"><a href="/pkg/encoding/json">encoding/json</a></h3> <h3 id="encoding/json"><a href="/pkg/encoding/json">encoding/json</a></h3>
<p><!-- CL 263619 --> <p><!-- CL 263619 -->
@ -367,6 +478,13 @@ Do not send CLs removing the interior tags from such phrases.
Cookies set with <code>SameSiteDefaultMode</code> now behave according to the current Cookies set with <code>SameSiteDefaultMode</code> now behave according to the current
spec (no attribute is set) instead of generating a SameSite key without a value. spec (no attribute is set) instead of generating a SameSite key without a value.
</p> </p>
<p><!-- CL 246338 -->
The <a href="/pkg/net/http/"><code>net/http</code></a> package now uses the new
<a href="/pkg/crypto/tls#Conn.HandshakeContext"><code>(*tls.Conn).HandshakeContext</code></a>
with the <a href="/pkg/net/http/#Request"><code>Request</code></a> context
when performing TLS handshakes in the client or server.
</p>
</dd> </dd>
</dl><!-- net/http --> </dl><!-- net/http -->
@ -378,6 +496,14 @@ Do not send CLs removing the interior tags from such phrases.
</dd> </dd>
</dl><!-- runtime/debug --> </dl><!-- runtime/debug -->
<dl id="syscall"><dt><a href="/pkg/syscall/">syscall</a></dt>
<dd>
<p><!-- CL 261917 -->
<a href="/pkg/syscall/#SysProcAttr"><code>SysProcAttr</code></a> on Windows has a new NoInheritHandles field that disables inheriting handles when creating a new process.
</p>
</dd>
</dl><!-- syscall -->
<dl id="strconv"><dt><a href="/pkg/strconv/">strconv</a></dt> <dl id="strconv"><dt><a href="/pkg/strconv/">strconv</a></dt>
<dd> <dd>
<p><!-- CL 260858 --> <p><!-- CL 260858 -->

View file

@ -0,0 +1,216 @@
// 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 errorstest
import (
"bytes"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"unicode"
)
// A manually modified object file could pass unexpected characters
// into the files generated by cgo.
const magicInput = "abcdefghijklmnopqrstuvwxyz0123"
const magicReplace = "\n//go:cgo_ldflag \"-badflag\"\n//"
const cSymbol = "BadSymbol" + magicInput + "Name"
const cDefSource = "int " + cSymbol + " = 1;"
const cRefSource = "extern int " + cSymbol + "; int F() { return " + cSymbol + "; }"
// goSource is the source code for the trivial Go file we use.
// We will replace TMPDIR with the temporary directory name.
const goSource = `
package main
// #cgo LDFLAGS: TMPDIR/cbad.o TMPDIR/cbad.so
// extern int F();
import "C"
func main() {
println(C.F())
}
`
func TestBadSymbol(t *testing.T) {
dir := t.TempDir()
mkdir := func(base string) string {
ret := filepath.Join(dir, base)
if err := os.Mkdir(ret, 0755); err != nil {
t.Fatal(err)
}
return ret
}
cdir := mkdir("c")
godir := mkdir("go")
makeFile := func(mdir, base, source string) string {
ret := filepath.Join(mdir, base)
if err := ioutil.WriteFile(ret, []byte(source), 0644); err != nil {
t.Fatal(err)
}
return ret
}
cDefFile := makeFile(cdir, "cdef.c", cDefSource)
cRefFile := makeFile(cdir, "cref.c", cRefSource)
ccCmd := cCompilerCmd(t)
cCompile := func(arg, base, src string) string {
out := filepath.Join(cdir, base)
run := append(ccCmd, arg, "-o", out, src)
output, err := exec.Command(run[0], run[1:]...).CombinedOutput()
if err != nil {
t.Log(run)
t.Logf("%s", output)
t.Fatal(err)
}
if err := os.Remove(src); err != nil {
t.Fatal(err)
}
return out
}
// Build a shared library that defines a symbol whose name
// contains magicInput.
cShared := cCompile("-shared", "c.so", cDefFile)
// Build an object file that refers to the symbol whose name
// contains magicInput.
cObj := cCompile("-c", "c.o", cRefFile)
// Rewrite the shared library and the object file, replacing
// magicInput with magicReplace. This will have the effect of
// introducing a symbol whose name looks like a cgo command.
// The cgo tool will use that name when it generates the
// _cgo_import.go file, thus smuggling a magic //go:cgo_ldflag
// pragma into a Go file. We used to not check the pragmas in
// _cgo_import.go.
rewrite := func(from, to string) {
obj, err := ioutil.ReadFile(from)
if err != nil {
t.Fatal(err)
}
if bytes.Count(obj, []byte(magicInput)) == 0 {
t.Fatalf("%s: did not find magic string", from)
}
if len(magicInput) != len(magicReplace) {
t.Fatalf("internal test error: different magic lengths: %d != %d", len(magicInput), len(magicReplace))
}
obj = bytes.ReplaceAll(obj, []byte(magicInput), []byte(magicReplace))
if err := ioutil.WriteFile(to, obj, 0644); err != nil {
t.Fatal(err)
}
}
cBadShared := filepath.Join(godir, "cbad.so")
rewrite(cShared, cBadShared)
cBadObj := filepath.Join(godir, "cbad.o")
rewrite(cObj, cBadObj)
goSourceBadObject := strings.ReplaceAll(goSource, "TMPDIR", godir)
makeFile(godir, "go.go", goSourceBadObject)
makeFile(godir, "go.mod", "module badsym")
// Try to build our little package.
cmd := exec.Command("go", "build", "-ldflags=-v")
cmd.Dir = godir
output, err := cmd.CombinedOutput()
// The build should fail, but we want it to fail because we
// detected the error, not because we passed a bad flag to the
// C linker.
if err == nil {
t.Errorf("go build succeeded unexpectedly")
}
t.Logf("%s", output)
for _, line := range bytes.Split(output, []byte("\n")) {
if bytes.Contains(line, []byte("dynamic symbol")) && bytes.Contains(line, []byte("contains unsupported character")) {
// This is the error from cgo.
continue
}
// We passed -ldflags=-v to see the external linker invocation,
// which should not include -badflag.
if bytes.Contains(line, []byte("-badflag")) {
t.Error("output should not mention -badflag")
}
// Also check for compiler errors, just in case.
// GCC says "unrecognized command line option".
// clang says "unknown argument".
if bytes.Contains(line, []byte("unrecognized")) || bytes.Contains(output, []byte("unknown")) {
t.Error("problem should have been caught before invoking C linker")
}
}
}
func cCompilerCmd(t *testing.T) []string {
cc := []string{goEnv(t, "CC")}
out := goEnv(t, "GOGCCFLAGS")
quote := '\000'
start := 0
lastSpace := true
backslash := false
s := string(out)
for i, c := range s {
if quote == '\000' && unicode.IsSpace(c) {
if !lastSpace {
cc = append(cc, s[start:i])
lastSpace = true
}
} else {
if lastSpace {
start = i
lastSpace = false
}
if quote == '\000' && !backslash && (c == '"' || c == '\'') {
quote = c
backslash = false
} else if !backslash && quote == c {
quote = '\000'
} else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
backslash = true
} else {
backslash = false
}
}
}
if !lastSpace {
cc = append(cc, s[start:])
}
return cc
}
func goEnv(t *testing.T, key string) string {
out, err := exec.Command("go", "env", key).CombinedOutput()
if err != nil {
t.Logf("go env %s\n", key)
t.Logf("%s", out)
t.Fatal(err)
}
return strings.TrimSpace(string(out))
}

View file

@ -62,28 +62,60 @@ import "C"
// compareStatus is used to confirm the contents of the thread // compareStatus is used to confirm the contents of the thread
// specific status files match expectations. // specific status files match expectations.
func compareStatus(filter, expect string) error { func compareStatus(filter, expect string) error {
expected := filter + "\t" + expect expected := filter + expect
pid := syscall.Getpid() pid := syscall.Getpid()
fs, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/task", pid)) fs, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/task", pid))
if err != nil { if err != nil {
return fmt.Errorf("unable to find %d tasks: %v", pid, err) return fmt.Errorf("unable to find %d tasks: %v", pid, err)
} }
expectedProc := fmt.Sprintf("Pid:\t%d", pid)
foundAThread := false
for _, f := range fs { for _, f := range fs {
tf := fmt.Sprintf("/proc/%s/status", f.Name()) tf := fmt.Sprintf("/proc/%s/status", f.Name())
d, err := ioutil.ReadFile(tf) d, err := ioutil.ReadFile(tf)
if err != nil { if err != nil {
return fmt.Errorf("unable to read %q: %v", tf, err) // There are a surprising number of ways this
// can error out on linux. We've seen all of
// the following, so treat any error here as
// equivalent to the "process is gone":
// os.IsNotExist(err),
// "... : no such process",
// "... : bad file descriptor.
continue
} }
lines := strings.Split(string(d), "\n") lines := strings.Split(string(d), "\n")
for _, line := range lines { for _, line := range lines {
// Different kernel vintages pad differently.
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "Pid:\t") {
// On loaded systems, it is possible
// for a TID to be reused really
// quickly. As such, we need to
// validate that the thread status
// info we just read is a task of the
// same process PID as we are
// currently running, and not a
// recently terminated thread
// resurfaced in a different process.
if line != expectedProc {
break
}
// Fall through in the unlikely case
// that filter at some point is
// "Pid:\t".
}
if strings.HasPrefix(line, filter) { if strings.HasPrefix(line, filter) {
if line != expected { if line != expected {
return fmt.Errorf("%s %s (bad)\n", tf, line) return fmt.Errorf("%q got:%q want:%q (bad) [pid=%d file:'%s' %v]\n", tf, line, expected, pid, string(d), expectedProc)
} }
foundAThread = true
break break
} }
} }
} }
if !foundAThread {
return fmt.Errorf("found no thread /proc/<TID>/status files for process %q", expectedProc)
}
return nil return nil
} }
@ -110,34 +142,34 @@ func test1435(t *testing.T) {
fn func() error fn func() error
filter, expect string filter, expect string
}{ }{
{call: "Setegid(1)", fn: func() error { return syscall.Setegid(1) }, filter: "Gid:", expect: "0\t1\t0\t1"}, {call: "Setegid(1)", fn: func() error { return syscall.Setegid(1) }, filter: "Gid:", expect: "\t0\t1\t0\t1"},
{call: "Setegid(0)", fn: func() error { return syscall.Setegid(0) }, filter: "Gid:", expect: "0\t0\t0\t0"}, {call: "Setegid(0)", fn: func() error { return syscall.Setegid(0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"},
{call: "Seteuid(1)", fn: func() error { return syscall.Seteuid(1) }, filter: "Uid:", expect: "0\t1\t0\t1"}, {call: "Seteuid(1)", fn: func() error { return syscall.Seteuid(1) }, filter: "Uid:", expect: "\t0\t1\t0\t1"},
{call: "Setuid(0)", fn: func() error { return syscall.Setuid(0) }, filter: "Uid:", expect: "0\t0\t0\t0"}, {call: "Setuid(0)", fn: func() error { return syscall.Setuid(0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"},
{call: "Setgid(1)", fn: func() error { return syscall.Setgid(1) }, filter: "Gid:", expect: "1\t1\t1\t1"}, {call: "Setgid(1)", fn: func() error { return syscall.Setgid(1) }, filter: "Gid:", expect: "\t1\t1\t1\t1"},
{call: "Setgid(0)", fn: func() error { return syscall.Setgid(0) }, filter: "Gid:", expect: "0\t0\t0\t0"}, {call: "Setgid(0)", fn: func() error { return syscall.Setgid(0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"},
{call: "Setgroups([]int{0,1,2,3})", fn: func() error { return syscall.Setgroups([]int{0, 1, 2, 3}) }, filter: "Groups:", expect: "0 1 2 3 "}, {call: "Setgroups([]int{0,1,2,3})", fn: func() error { return syscall.Setgroups([]int{0, 1, 2, 3}) }, filter: "Groups:", expect: "\t0 1 2 3"},
{call: "Setgroups(nil)", fn: func() error { return syscall.Setgroups(nil) }, filter: "Groups:", expect: " "}, {call: "Setgroups(nil)", fn: func() error { return syscall.Setgroups(nil) }, filter: "Groups:", expect: ""},
{call: "Setgroups([]int{0})", fn: func() error { return syscall.Setgroups([]int{0}) }, filter: "Groups:", expect: "0 "}, {call: "Setgroups([]int{0})", fn: func() error { return syscall.Setgroups([]int{0}) }, filter: "Groups:", expect: "\t0"},
{call: "Setregid(101,0)", fn: func() error { return syscall.Setregid(101, 0) }, filter: "Gid:", expect: "101\t0\t0\t0"}, {call: "Setregid(101,0)", fn: func() error { return syscall.Setregid(101, 0) }, filter: "Gid:", expect: "\t101\t0\t0\t0"},
{call: "Setregid(0,102)", fn: func() error { return syscall.Setregid(0, 102) }, filter: "Gid:", expect: "0\t102\t102\t102"}, {call: "Setregid(0,102)", fn: func() error { return syscall.Setregid(0, 102) }, filter: "Gid:", expect: "\t0\t102\t102\t102"},
{call: "Setregid(0,0)", fn: func() error { return syscall.Setregid(0, 0) }, filter: "Gid:", expect: "0\t0\t0\t0"}, {call: "Setregid(0,0)", fn: func() error { return syscall.Setregid(0, 0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"},
{call: "Setreuid(1,0)", fn: func() error { return syscall.Setreuid(1, 0) }, filter: "Uid:", expect: "1\t0\t0\t0"}, {call: "Setreuid(1,0)", fn: func() error { return syscall.Setreuid(1, 0) }, filter: "Uid:", expect: "\t1\t0\t0\t0"},
{call: "Setreuid(0,2)", fn: func() error { return syscall.Setreuid(0, 2) }, filter: "Uid:", expect: "0\t2\t2\t2"}, {call: "Setreuid(0,2)", fn: func() error { return syscall.Setreuid(0, 2) }, filter: "Uid:", expect: "\t0\t2\t2\t2"},
{call: "Setreuid(0,0)", fn: func() error { return syscall.Setreuid(0, 0) }, filter: "Uid:", expect: "0\t0\t0\t0"}, {call: "Setreuid(0,0)", fn: func() error { return syscall.Setreuid(0, 0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"},
{call: "Setresgid(101,0,102)", fn: func() error { return syscall.Setresgid(101, 0, 102) }, filter: "Gid:", expect: "101\t0\t102\t0"}, {call: "Setresgid(101,0,102)", fn: func() error { return syscall.Setresgid(101, 0, 102) }, filter: "Gid:", expect: "\t101\t0\t102\t0"},
{call: "Setresgid(0,102,101)", fn: func() error { return syscall.Setresgid(0, 102, 101) }, filter: "Gid:", expect: "0\t102\t101\t102"}, {call: "Setresgid(0,102,101)", fn: func() error { return syscall.Setresgid(0, 102, 101) }, filter: "Gid:", expect: "\t0\t102\t101\t102"},
{call: "Setresgid(0,0,0)", fn: func() error { return syscall.Setresgid(0, 0, 0) }, filter: "Gid:", expect: "0\t0\t0\t0"}, {call: "Setresgid(0,0,0)", fn: func() error { return syscall.Setresgid(0, 0, 0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"},
{call: "Setresuid(1,0,2)", fn: func() error { return syscall.Setresuid(1, 0, 2) }, filter: "Uid:", expect: "1\t0\t2\t0"}, {call: "Setresuid(1,0,2)", fn: func() error { return syscall.Setresuid(1, 0, 2) }, filter: "Uid:", expect: "\t1\t0\t2\t0"},
{call: "Setresuid(0,2,1)", fn: func() error { return syscall.Setresuid(0, 2, 1) }, filter: "Uid:", expect: "0\t2\t1\t2"}, {call: "Setresuid(0,2,1)", fn: func() error { return syscall.Setresuid(0, 2, 1) }, filter: "Uid:", expect: "\t0\t2\t1\t2"},
{call: "Setresuid(0,0,0)", fn: func() error { return syscall.Setresuid(0, 0, 0) }, filter: "Uid:", expect: "0\t0\t0\t0"}, {call: "Setresuid(0,0,0)", fn: func() error { return syscall.Setresuid(0, 0, 0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"},
} }
for i, v := range vs { for i, v := range vs {

View file

@ -0,0 +1,15 @@
// 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 cgotest
// typedef struct { } T42495A;
// typedef struct { int x[0]; } T42495B;
import "C"
//export Issue42495A
func Issue42495A(C.T42495A) {}
//export Issue42495B
func Issue42495B(C.T42495B) {}

View file

@ -196,3 +196,17 @@ func TestIssue25756(t *testing.T) {
}) })
} }
} }
func TestMethod(t *testing.T) {
// Exported symbol's method must be live.
goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./method/plugin.go")
goCmd(t, "build", "-o", "method.exe", "./method/main.go")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, "./method.exe")
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, out)
}
}

View file

@ -0,0 +1,26 @@
// 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.
// Issue 42579: methods of symbols exported from plugin must be live.
package main
import (
"plugin"
"reflect"
)
func main() {
p, err := plugin.Open("plugin.so")
if err != nil {
panic(err)
}
x, err := p.Lookup("X")
if err != nil {
panic(err)
}
reflect.ValueOf(x).Elem().MethodByName("M").Call(nil)
}

View file

@ -0,0 +1,13 @@
// 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
func main() {}
type T int
func (T) M() { println("M") }
var X T

View file

@ -695,7 +695,7 @@ func fileEntryLess(x, y string) bool {
} }
// Open opens the named file in the ZIP archive, // Open opens the named file in the ZIP archive,
// using the semantics of io.FS.Open: // using the semantics of fs.FS.Open:
// paths are always slash separated, with no // paths are always slash separated, with no
// leading / or ../ elements. // leading / or ../ elements.
func (r *Reader) Open(name string) (fs.File, error) { func (r *Reader) Open(name string) (fs.File, error) {

View file

@ -30,6 +30,13 @@ func ExampleBuffer_reader() {
// Output: Gophers rule! // Output: Gophers rule!
} }
func ExampleBuffer_Bytes() {
buf := bytes.Buffer{}
buf.Write([]byte{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'})
os.Stdout.Write(buf.Bytes())
// Output: hello world
}
func ExampleBuffer_Grow() { func ExampleBuffer_Grow() {
var b bytes.Buffer var b bytes.Buffer
b.Grow(64) b.Grow(64)

View file

@ -681,38 +681,38 @@ again:
LDADDLH R5, (RSP), R7 // e7036578 LDADDLH R5, (RSP), R7 // e7036578
LDADDLB R5, (R6), R7 // c7006538 LDADDLB R5, (R6), R7 // c7006538
LDADDLB R5, (RSP), R7 // e7036538 LDADDLB R5, (RSP), R7 // e7036538
LDANDAD R5, (R6), R7 // c710a5f8 LDCLRAD R5, (R6), R7 // c710a5f8
LDANDAD R5, (RSP), R7 // e713a5f8 LDCLRAD R5, (RSP), R7 // e713a5f8
LDANDAW R5, (R6), R7 // c710a5b8 LDCLRAW R5, (R6), R7 // c710a5b8
LDANDAW R5, (RSP), R7 // e713a5b8 LDCLRAW R5, (RSP), R7 // e713a5b8
LDANDAH R5, (R6), R7 // c710a578 LDCLRAH R5, (R6), R7 // c710a578
LDANDAH R5, (RSP), R7 // e713a578 LDCLRAH R5, (RSP), R7 // e713a578
LDANDAB R5, (R6), R7 // c710a538 LDCLRAB R5, (R6), R7 // c710a538
LDANDAB R5, (RSP), R7 // e713a538 LDCLRAB R5, (RSP), R7 // e713a538
LDANDALD R5, (R6), R7 // c710e5f8 LDCLRALD R5, (R6), R7 // c710e5f8
LDANDALD R5, (RSP), R7 // e713e5f8 LDCLRALD R5, (RSP), R7 // e713e5f8
LDANDALW R5, (R6), R7 // c710e5b8 LDCLRALW R5, (R6), R7 // c710e5b8
LDANDALW R5, (RSP), R7 // e713e5b8 LDCLRALW R5, (RSP), R7 // e713e5b8
LDANDALH R5, (R6), R7 // c710e578 LDCLRALH R5, (R6), R7 // c710e578
LDANDALH R5, (RSP), R7 // e713e578 LDCLRALH R5, (RSP), R7 // e713e578
LDANDALB R5, (R6), R7 // c710e538 LDCLRALB R5, (R6), R7 // c710e538
LDANDALB R5, (RSP), R7 // e713e538 LDCLRALB R5, (RSP), R7 // e713e538
LDANDD R5, (R6), R7 // c71025f8 LDCLRD R5, (R6), R7 // c71025f8
LDANDD R5, (RSP), R7 // e71325f8 LDCLRD R5, (RSP), R7 // e71325f8
LDANDW R5, (R6), R7 // c71025b8 LDCLRW R5, (R6), R7 // c71025b8
LDANDW R5, (RSP), R7 // e71325b8 LDCLRW R5, (RSP), R7 // e71325b8
LDANDH R5, (R6), R7 // c7102578 LDCLRH R5, (R6), R7 // c7102578
LDANDH R5, (RSP), R7 // e7132578 LDCLRH R5, (RSP), R7 // e7132578
LDANDB R5, (R6), R7 // c7102538 LDCLRB R5, (R6), R7 // c7102538
LDANDB R5, (RSP), R7 // e7132538 LDCLRB R5, (RSP), R7 // e7132538
LDANDLD R5, (R6), R7 // c71065f8 LDCLRLD R5, (R6), R7 // c71065f8
LDANDLD R5, (RSP), R7 // e71365f8 LDCLRLD R5, (RSP), R7 // e71365f8
LDANDLW R5, (R6), R7 // c71065b8 LDCLRLW R5, (R6), R7 // c71065b8
LDANDLW R5, (RSP), R7 // e71365b8 LDCLRLW R5, (RSP), R7 // e71365b8
LDANDLH R5, (R6), R7 // c7106578 LDCLRLH R5, (R6), R7 // c7106578
LDANDLH R5, (RSP), R7 // e7136578 LDCLRLH R5, (RSP), R7 // e7136578
LDANDLB R5, (R6), R7 // c7106538 LDCLRLB R5, (R6), R7 // c7106538
LDANDLB R5, (RSP), R7 // e7136538 LDCLRLB R5, (RSP), R7 // e7136538
LDEORAD R5, (R6), R7 // c720a5f8 LDEORAD R5, (R6), R7 // c720a5f8
LDEORAD R5, (RSP), R7 // e723a5f8 LDEORAD R5, (RSP), R7 // e723a5f8
LDEORAW R5, (R6), R7 // c720a5b8 LDEORAW R5, (R6), R7 // c720a5b8

View file

@ -123,14 +123,14 @@ TEXT errors(SB),$0
LDADDLW R5, (R6), ZR // ERROR "illegal destination register" LDADDLW R5, (R6), ZR // ERROR "illegal destination register"
LDADDLH R5, (R6), ZR // ERROR "illegal destination register" LDADDLH R5, (R6), ZR // ERROR "illegal destination register"
LDADDLB R5, (R6), ZR // ERROR "illegal destination register" LDADDLB R5, (R6), ZR // ERROR "illegal destination register"
LDANDD R5, (R6), ZR // ERROR "illegal destination register" LDCLRD R5, (R6), ZR // ERROR "illegal destination register"
LDANDW R5, (R6), ZR // ERROR "illegal destination register" LDCLRW R5, (R6), ZR // ERROR "illegal destination register"
LDANDH R5, (R6), ZR // ERROR "illegal destination register" LDCLRH R5, (R6), ZR // ERROR "illegal destination register"
LDANDB R5, (R6), ZR // ERROR "illegal destination register" LDCLRB R5, (R6), ZR // ERROR "illegal destination register"
LDANDLD R5, (R6), ZR // ERROR "illegal destination register" LDCLRLD R5, (R6), ZR // ERROR "illegal destination register"
LDANDLW R5, (R6), ZR // ERROR "illegal destination register" LDCLRLW R5, (R6), ZR // ERROR "illegal destination register"
LDANDLH R5, (R6), ZR // ERROR "illegal destination register" LDCLRLH R5, (R6), ZR // ERROR "illegal destination register"
LDANDLB R5, (R6), ZR // ERROR "illegal destination register" LDCLRLB R5, (R6), ZR // ERROR "illegal destination register"
LDEORD R5, (R6), ZR // ERROR "illegal destination register" LDEORD R5, (R6), ZR // ERROR "illegal destination register"
LDEORW R5, (R6), ZR // ERROR "illegal destination register" LDEORW R5, (R6), ZR // ERROR "illegal destination register"
LDEORH R5, (R6), ZR // ERROR "illegal destination register" LDEORH R5, (R6), ZR // ERROR "illegal destination register"
@ -163,22 +163,22 @@ TEXT errors(SB),$0
LDADDLW R5, (R6), RSP // ERROR "illegal destination register" LDADDLW R5, (R6), RSP // ERROR "illegal destination register"
LDADDLH R5, (R6), RSP // ERROR "illegal destination register" LDADDLH R5, (R6), RSP // ERROR "illegal destination register"
LDADDLB R5, (R6), RSP // ERROR "illegal destination register" LDADDLB R5, (R6), RSP // ERROR "illegal destination register"
LDANDAD R5, (R6), RSP // ERROR "illegal destination register" LDCLRAD R5, (R6), RSP // ERROR "illegal destination register"
LDANDAW R5, (R6), RSP // ERROR "illegal destination register" LDCLRAW R5, (R6), RSP // ERROR "illegal destination register"
LDANDAH R5, (R6), RSP // ERROR "illegal destination register" LDCLRAH R5, (R6), RSP // ERROR "illegal destination register"
LDANDAB R5, (R6), RSP // ERROR "illegal destination register" LDCLRAB R5, (R6), RSP // ERROR "illegal destination register"
LDANDALD R5, (R6), RSP // ERROR "illegal destination register" LDCLRALD R5, (R6), RSP // ERROR "illegal destination register"
LDANDALW R5, (R6), RSP // ERROR "illegal destination register" LDCLRALW R5, (R6), RSP // ERROR "illegal destination register"
LDANDALH R5, (R6), RSP // ERROR "illegal destination register" LDCLRALH R5, (R6), RSP // ERROR "illegal destination register"
LDANDALB R5, (R6), RSP // ERROR "illegal destination register" LDCLRALB R5, (R6), RSP // ERROR "illegal destination register"
LDANDD R5, (R6), RSP // ERROR "illegal destination register" LDCLRD R5, (R6), RSP // ERROR "illegal destination register"
LDANDW R5, (R6), RSP // ERROR "illegal destination register" LDCLRW R5, (R6), RSP // ERROR "illegal destination register"
LDANDH R5, (R6), RSP // ERROR "illegal destination register" LDCLRH R5, (R6), RSP // ERROR "illegal destination register"
LDANDB R5, (R6), RSP // ERROR "illegal destination register" LDCLRB R5, (R6), RSP // ERROR "illegal destination register"
LDANDLD R5, (R6), RSP // ERROR "illegal destination register" LDCLRLD R5, (R6), RSP // ERROR "illegal destination register"
LDANDLW R5, (R6), RSP // ERROR "illegal destination register" LDCLRLW R5, (R6), RSP // ERROR "illegal destination register"
LDANDLH R5, (R6), RSP // ERROR "illegal destination register" LDCLRLH R5, (R6), RSP // ERROR "illegal destination register"
LDANDLB R5, (R6), RSP // ERROR "illegal destination register" LDCLRLB R5, (R6), RSP // ERROR "illegal destination register"
LDEORAD R5, (R6), RSP // ERROR "illegal destination register" LDEORAD R5, (R6), RSP // ERROR "illegal destination register"
LDEORAW R5, (R6), RSP // ERROR "illegal destination register" LDEORAW R5, (R6), RSP // ERROR "illegal destination register"
LDEORAH R5, (R6), RSP // ERROR "illegal destination register" LDEORAH R5, (R6), RSP // ERROR "illegal destination register"

View file

@ -282,7 +282,9 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
RLWMI $7, R3, $65535, R6 // 50663c3e RLWMI $7, R3, $65535, R6 // 50663c3e
RLWMICC $7, R3, $65535, R6 // 50663c3f RLWMICC $7, R3, $65535, R6 // 50663c3f
RLWNM $3, R4, $7, R6 // 54861f7e RLWNM $3, R4, $7, R6 // 54861f7e
RLWNM R3, R4, $7, R6 // 5c861f7e
RLWNMCC $3, R4, $7, R6 // 54861f7f RLWNMCC $3, R4, $7, R6 // 54861f7f
RLWNMCC R3, R4, $7, R6 // 5c861f7f
RLDMI $0, R4, $7, R6 // 7886076c RLDMI $0, R4, $7, R6 // 7886076c
RLDMICC $0, R4, $7, R6 // 7886076d RLDMICC $0, R4, $7, R6 // 7886076d
RLDIMI $0, R4, $7, R6 // 788601cc RLDIMI $0, R4, $7, R6 // 788601cc

View file

@ -412,6 +412,8 @@ TEXT main·foo(SB),DUPOK|NOSPLIT,$16-0 // TEXT main.foo(SB), DUPOK|NOSPLIT, $16-
UNDEF // 00000000 UNDEF // 00000000
NOPH // 0700 NOPH // 0700
SYNC // 07e0
// vector add and sub instructions // vector add and sub instructions
VAB V3, V4, V4 // e743400000f3 VAB V3, V4, V4 // e743400000f3
VAH V3, V4, V4 // e743400010f3 VAH V3, V4, V4 // e743400010f3

View file

@ -337,6 +337,8 @@ func dynimport(obj string) {
if s.Version != "" { if s.Version != "" {
targ += "#" + s.Version targ += "#" + s.Version
} }
checkImportSymName(s.Name)
checkImportSymName(targ)
fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, targ, s.Library) fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, targ, s.Library)
} }
lib, _ := f.ImportedLibraries() lib, _ := f.ImportedLibraries()
@ -352,6 +354,7 @@ func dynimport(obj string) {
if len(s) > 0 && s[0] == '_' { if len(s) > 0 && s[0] == '_' {
s = s[1:] s = s[1:]
} }
checkImportSymName(s)
fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s, s, "") fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s, s, "")
} }
lib, _ := f.ImportedLibraries() lib, _ := f.ImportedLibraries()
@ -366,6 +369,8 @@ func dynimport(obj string) {
for _, s := range sym { for _, s := range sym {
ss := strings.Split(s, ":") ss := strings.Split(s, ":")
name := strings.Split(ss[0], "@")[0] name := strings.Split(ss[0], "@")[0]
checkImportSymName(name)
checkImportSymName(ss[0])
fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", name, ss[0], strings.ToLower(ss[1])) fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", name, ss[0], strings.ToLower(ss[1]))
} }
return return
@ -383,6 +388,7 @@ func dynimport(obj string) {
// Go symbols. // Go symbols.
continue continue
} }
checkImportSymName(s.Name)
fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, s.Name, s.Library) fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, s.Name, s.Library)
} }
lib, err := f.ImportedLibraries() lib, err := f.ImportedLibraries()
@ -398,6 +404,23 @@ func dynimport(obj string) {
fatalf("cannot parse %s as ELF, Mach-O, PE or XCOFF", obj) fatalf("cannot parse %s as ELF, Mach-O, PE or XCOFF", obj)
} }
// checkImportSymName checks a symbol name we are going to emit as part
// of a //go:cgo_import_dynamic pragma. These names come from object
// files, so they may be corrupt. We are going to emit them unquoted,
// so while they don't need to be valid symbol names (and in some cases,
// involving symbol versions, they won't be) they must contain only
// graphic characters and must not contain Go comments.
func checkImportSymName(s string) {
for _, c := range s {
if !unicode.IsGraphic(c) || unicode.IsSpace(c) {
fatalf("dynamic symbol %q contains unsupported character", s)
}
}
if strings.Index(s, "//") >= 0 || strings.Index(s, "/*") >= 0 {
fatalf("dynamic symbol %q contains Go comment")
}
}
// Construct a gcc struct matching the gc argument frame. // Construct a gcc struct matching the gc argument frame.
// Assumes that in gcc, char is 1 byte, short 2 bytes, int 4 bytes, long long 8 bytes. // Assumes that in gcc, char is 1 byte, short 2 bytes, int 4 bytes, long long 8 bytes.
// These assumptions are checked by the gccProlog. // These assumptions are checked by the gccProlog.
@ -962,7 +985,15 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
// The results part of the argument structure must be // The results part of the argument structure must be
// initialized to 0 so the write barriers generated by // initialized to 0 so the write barriers generated by
// the assignments to these fields in Go are safe. // the assignments to these fields in Go are safe.
fmt.Fprintf(fgcc, "\t%s %v _cgo_a = {0};\n", ctype, p.packedAttribute()) //
// We use a local static variable to get the zeroed
// value of the argument type. This avoids including
// string.h for memset, and is also robust to C++
// types with constructors. Both GCC and LLVM optimize
// this into just zeroing _cgo_a.
fmt.Fprintf(fgcc, "\ttypedef %s %v _cgo_argtype;\n", ctype, p.packedAttribute())
fmt.Fprintf(fgcc, "\tstatic _cgo_argtype _cgo_zero;\n")
fmt.Fprintf(fgcc, "\t_cgo_argtype _cgo_a = _cgo_zero;\n")
if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) { if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) {
fmt.Fprintf(fgcc, "\t%s r;\n", gccResult) fmt.Fprintf(fgcc, "\t%s r;\n", gccResult)
} }

View file

@ -76,7 +76,7 @@ func storeByType(t *types.Type) obj.As {
return x86.AMOVQ return x86.AMOVQ
} }
} }
panic("bad store type") panic(fmt.Sprintf("bad store type %v", t))
} }
// moveByType returns the reg->reg move instruction of the given type. // moveByType returns the reg->reg move instruction of the given type.
@ -101,7 +101,7 @@ func moveByType(t *types.Type) obj.As {
case 16: case 16:
return x86.AMOVUPS // int128s are in SSE registers return x86.AMOVUPS // int128s are in SSE registers
default: default:
panic(fmt.Sprintf("bad int register width %d:%s", t.Size(), t)) panic(fmt.Sprintf("bad int register width %d:%v", t.Size(), t))
} }
} }
} }

View file

@ -581,6 +581,24 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p2.From.Reg = arm64.REGTMP p2.From.Reg = arm64.REGTMP
p2.To.Type = obj.TYPE_BRANCH p2.To.Type = obj.TYPE_BRANCH
gc.Patch(p2, p) gc.Patch(p2, p)
case ssa.OpARM64LoweredAtomicExchange64Variant,
ssa.OpARM64LoweredAtomicExchange32Variant:
swap := arm64.ASWPALD
if v.Op == ssa.OpARM64LoweredAtomicExchange32Variant {
swap = arm64.ASWPALW
}
r0 := v.Args[0].Reg()
r1 := v.Args[1].Reg()
out := v.Reg0()
// SWPALD Rarg1, (Rarg0), Rout
p := s.Prog(swap)
p.From.Type = obj.TYPE_REG
p.From.Reg = r1
p.To.Type = obj.TYPE_MEM
p.To.Reg = r0
p.RegTo2 = out
case ssa.OpARM64LoweredAtomicAdd64, case ssa.OpARM64LoweredAtomicAdd64,
ssa.OpARM64LoweredAtomicAdd32: ssa.OpARM64LoweredAtomicAdd32:
// LDAXR (Rarg0), Rout // LDAXR (Rarg0), Rout
@ -687,6 +705,56 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p5.To.Type = obj.TYPE_REG p5.To.Type = obj.TYPE_REG
p5.To.Reg = out p5.To.Reg = out
gc.Patch(p2, p5) gc.Patch(p2, p5)
case ssa.OpARM64LoweredAtomicCas64Variant,
ssa.OpARM64LoweredAtomicCas32Variant:
// Rarg0: ptr
// Rarg1: old
// Rarg2: new
// MOV Rarg1, Rtmp
// CASAL Rtmp, (Rarg0), Rarg2
// CMP Rarg1, Rtmp
// CSET EQ, Rout
cas := arm64.ACASALD
cmp := arm64.ACMP
mov := arm64.AMOVD
if v.Op == ssa.OpARM64LoweredAtomicCas32Variant {
cas = arm64.ACASALW
cmp = arm64.ACMPW
mov = arm64.AMOVW
}
r0 := v.Args[0].Reg()
r1 := v.Args[1].Reg()
r2 := v.Args[2].Reg()
out := v.Reg0()
// MOV Rarg1, Rtmp
p := s.Prog(mov)
p.From.Type = obj.TYPE_REG
p.From.Reg = r1
p.To.Type = obj.TYPE_REG
p.To.Reg = arm64.REGTMP
// CASAL Rtmp, (Rarg0), Rarg2
p1 := s.Prog(cas)
p1.From.Type = obj.TYPE_REG
p1.From.Reg = arm64.REGTMP
p1.To.Type = obj.TYPE_MEM
p1.To.Reg = r0
p1.RegTo2 = r2
// CMP Rarg1, Rtmp
p2 := s.Prog(cmp)
p2.From.Type = obj.TYPE_REG
p2.From.Reg = r1
p2.Reg = arm64.REGTMP
// CSET EQ, Rout
p3 := s.Prog(arm64.ACSET)
p3.From.Type = obj.TYPE_REG
p3.From.Reg = arm64.COND_EQ
p3.To.Type = obj.TYPE_REG
p3.To.Reg = out
case ssa.OpARM64LoweredAtomicAnd8, case ssa.OpARM64LoweredAtomicAnd8,
ssa.OpARM64LoweredAtomicAnd32, ssa.OpARM64LoweredAtomicAnd32,
ssa.OpARM64LoweredAtomicOr8, ssa.OpARM64LoweredAtomicOr8,
@ -725,6 +793,63 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.From.Reg = arm64.REGTMP p3.From.Reg = arm64.REGTMP
p3.To.Type = obj.TYPE_BRANCH p3.To.Type = obj.TYPE_BRANCH
gc.Patch(p3, p) gc.Patch(p3, p)
case ssa.OpARM64LoweredAtomicAnd8Variant,
ssa.OpARM64LoweredAtomicAnd32Variant:
atomic_clear := arm64.ALDCLRALW
if v.Op == ssa.OpARM64LoweredAtomicAnd8Variant {
atomic_clear = arm64.ALDCLRALB
}
r0 := v.Args[0].Reg()
r1 := v.Args[1].Reg()
out := v.Reg0()
// MNV Rarg1 Rtemp
p := s.Prog(arm64.AMVN)
p.From.Type = obj.TYPE_REG
p.From.Reg = r1
p.To.Type = obj.TYPE_REG
p.To.Reg = arm64.REGTMP
// LDCLRALW Rtemp, (Rarg0), Rout
p1 := s.Prog(atomic_clear)
p1.From.Type = obj.TYPE_REG
p1.From.Reg = arm64.REGTMP
p1.To.Type = obj.TYPE_MEM
p1.To.Reg = r0
p1.RegTo2 = out
// AND Rarg1, Rout
p2 := s.Prog(arm64.AAND)
p2.From.Type = obj.TYPE_REG
p2.From.Reg = r1
p2.To.Type = obj.TYPE_REG
p2.To.Reg = out
case ssa.OpARM64LoweredAtomicOr8Variant,
ssa.OpARM64LoweredAtomicOr32Variant:
atomic_or := arm64.ALDORALW
if v.Op == ssa.OpARM64LoweredAtomicOr8Variant {
atomic_or = arm64.ALDORALB
}
r0 := v.Args[0].Reg()
r1 := v.Args[1].Reg()
out := v.Reg0()
// LDORALW Rarg1, (Rarg0), Rout
p := s.Prog(atomic_or)
p.From.Type = obj.TYPE_REG
p.From.Reg = r1
p.To.Type = obj.TYPE_MEM
p.To.Reg = r0
p.RegTo2 = out
// ORR Rarg1, Rout
p2 := s.Prog(arm64.AORR)
p2.From.Type = obj.TYPE_REG
p2.From.Reg = r1
p2.To.Type = obj.TYPE_REG
p2.To.Reg = out
case ssa.OpARM64MOVBreg, case ssa.OpARM64MOVBreg,
ssa.OpARM64MOVBUreg, ssa.OpARM64MOVBUreg,
ssa.OpARM64MOVHreg, ssa.OpARM64MOVHreg,

View file

@ -71,6 +71,10 @@ func (p *noder) funcLit(expr *syntax.FuncLit) *Node {
return clo return clo
} }
// typecheckclosure typechecks an OCLOSURE node. It also creates the named
// function associated with the closure.
// TODO: This creation of the named function should probably really be done in a
// separate pass from type-checking.
func typecheckclosure(clo *Node, top int) { func typecheckclosure(clo *Node, top int) {
xfunc := clo.Func.Closure xfunc := clo.Func.Closure
// Set current associated iota value, so iota can be used inside // Set current associated iota value, so iota can be used inside

View file

@ -257,6 +257,8 @@ func symfield(s *types.Sym, typ *types.Type) *Node {
// oldname returns the Node that declares symbol s in the current scope. // oldname returns the Node that declares symbol s in the current scope.
// If no such Node currently exists, an ONONAME Node is returned instead. // If no such Node currently exists, an ONONAME Node is returned instead.
// Automatically creates a new closure variable if the referenced symbol was
// declared in a different (containing) function.
func oldname(s *types.Sym) *Node { func oldname(s *types.Sym) *Node {
n := asNode(s.Def) n := asNode(s.Def)
if n == nil { if n == nil {

View file

@ -8,6 +8,7 @@ import (
"cmd/internal/dwarf" "cmd/internal/dwarf"
"cmd/internal/obj" "cmd/internal/obj"
"cmd/internal/src" "cmd/internal/src"
"fmt"
"strings" "strings"
) )
@ -170,12 +171,32 @@ func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls {
addRange(inlcalls.Calls, start, fnsym.Size, curii, imap) addRange(inlcalls.Calls, start, fnsym.Size, curii, imap)
} }
// Issue 33188: if II foo is a child of II bar, then ensure that
// bar's ranges include the ranges of foo (the loop above will produce
// disjoint ranges).
for k, c := range inlcalls.Calls {
if c.Root {
unifyCallRanges(inlcalls, k)
}
}
// Debugging // Debugging
if Debug_gendwarfinl != 0 { if Debug_gendwarfinl != 0 {
dumpInlCalls(inlcalls) dumpInlCalls(inlcalls)
dumpInlVars(dwVars) dumpInlVars(dwVars)
} }
// Perform a consistency check on inlined routine PC ranges
// produced by unifyCallRanges above. In particular, complain in
// cases where you have A -> B -> C (e.g. C is inlined into B, and
// B is inlined into A) and the ranges for B are not enclosed
// within the ranges for A, or C within B.
for k, c := range inlcalls.Calls {
if c.Root {
checkInlCall(fnsym.Name, inlcalls, fnsym.Size, k, -1)
}
}
return inlcalls return inlcalls
} }
@ -355,3 +376,74 @@ func dumpInlVars(dwvars []*dwarf.Var) {
Ctxt.Logf("V%d: %s CI:%d II:%d IA:%d %s\n", i, dwv.Name, dwv.ChildIndex, dwv.InlIndex-1, ia, typ) Ctxt.Logf("V%d: %s CI:%d II:%d IA:%d %s\n", i, dwv.Name, dwv.ChildIndex, dwv.InlIndex-1, ia, typ)
} }
} }
func rangesContains(par []dwarf.Range, rng dwarf.Range) (bool, string) {
for _, r := range par {
if rng.Start >= r.Start && rng.End <= r.End {
return true, ""
}
}
msg := fmt.Sprintf("range [%d,%d) not contained in {", rng.Start, rng.End)
for _, r := range par {
msg += fmt.Sprintf(" [%d,%d)", r.Start, r.End)
}
msg += " }"
return false, msg
}
func rangesContainsAll(parent, child []dwarf.Range) (bool, string) {
for _, r := range child {
c, m := rangesContains(parent, r)
if !c {
return false, m
}
}
return true, ""
}
// checkInlCall verifies that the PC ranges for inline info 'idx' are
// enclosed/contained within the ranges of its parent inline (or if
// this is a root/toplevel inline, checks that the ranges fall within
// the extent of the top level function). A panic is issued if a
// malformed range is found.
func checkInlCall(funcName string, inlCalls dwarf.InlCalls, funcSize int64, idx, parentIdx int) {
// Callee
ic := inlCalls.Calls[idx]
callee := Ctxt.InlTree.InlinedFunction(ic.InlIndex).Name
calleeRanges := ic.Ranges
// Caller
caller := funcName
parentRanges := []dwarf.Range{dwarf.Range{Start: int64(0), End: funcSize}}
if parentIdx != -1 {
pic := inlCalls.Calls[parentIdx]
caller = Ctxt.InlTree.InlinedFunction(pic.InlIndex).Name
parentRanges = pic.Ranges
}
// Callee ranges contained in caller ranges?
c, m := rangesContainsAll(parentRanges, calleeRanges)
if !c {
Fatalf("** malformed inlined routine range in %s: caller %s callee %s II=%d %s\n", funcName, caller, callee, idx, m)
}
// Now visit kids
for _, k := range ic.Children {
checkInlCall(funcName, inlCalls, funcSize, k, idx)
}
}
// unifyCallRanges ensures that the ranges for a given inline
// transitively include all of the ranges for its child inlines.
func unifyCallRanges(inlcalls dwarf.InlCalls, idx int) {
ic := &inlcalls.Calls[idx]
for _, childIdx := range ic.Children {
// First make sure child ranges are unified.
unifyCallRanges(inlcalls, childIdx)
// Then merge child ranges into ranges for this inline.
cic := inlcalls.Calls[childIdx]
ic.Ranges = dwarf.MergeRanges(ic.Ranges, cic.Ranges)
}
}

View file

@ -419,13 +419,15 @@ func (n *Node) format(s fmt.State, verb rune, mode fmtMode) {
func (n *Node) jconv(s fmt.State, flag FmtFlag) { func (n *Node) jconv(s fmt.State, flag FmtFlag) {
c := flag & FmtShort c := flag & FmtShort
// Useful to see which nodes in an AST printout are actually identical // Useful to see which nodes in a Node Dump/dumplist are actually identical
fmt.Fprintf(s, " p(%p)", n) if Debug_dumpptrs != 0 {
fmt.Fprintf(s, " p(%p)", n)
}
if c == 0 && n.Name != nil && n.Name.Vargen != 0 { if c == 0 && n.Name != nil && n.Name.Vargen != 0 {
fmt.Fprintf(s, " g(%d)", n.Name.Vargen) fmt.Fprintf(s, " g(%d)", n.Name.Vargen)
} }
if c == 0 && n.Name != nil && n.Name.Defn != nil { if Debug_dumpptrs != 0 && c == 0 && n.Name != nil && n.Name.Defn != nil {
// Useful to see where Defn is set and what node it points to // Useful to see where Defn is set and what node it points to
fmt.Fprintf(s, " defn(%p)", n.Name.Defn) fmt.Fprintf(s, " defn(%p)", n.Name.Defn)
} }

View file

@ -302,6 +302,12 @@ func ggloblnod(nam *Node) {
if nam.Name.LibfuzzerExtraCounter() { if nam.Name.LibfuzzerExtraCounter() {
s.Type = objabi.SLIBFUZZER_EXTRA_COUNTER s.Type = objabi.SLIBFUZZER_EXTRA_COUNTER
} }
if nam.Sym.Linkname != "" {
// Make sure linkname'd symbol is non-package. When a symbol is
// both imported and linkname'd, s.Pkg may not set to "_" in
// types.Sym.Linksym because LSym already exists. Set it here.
s.Pkg = "_"
}
} }
func ggloblsym(s *obj.LSym, width int32, flags int16) { func ggloblsym(s *obj.LSym, width int32, flags int16) {

View file

@ -1138,13 +1138,10 @@ func (w *exportWriter) stmt(n *Node) {
w.pos(n.Pos) w.pos(n.Pos)
w.stmtList(n.Ninit) w.stmtList(n.Ninit)
w.exprsOrNil(n.Left, nil) w.exprsOrNil(n.Left, nil)
w.stmtList(n.List) w.caseList(n)
case OCASE: // case OCASE:
w.op(OCASE) // handled by caseList
w.pos(n.Pos)
w.stmtList(n.List)
w.stmtList(n.Nbody)
case OFALL: case OFALL:
w.op(OFALL) w.op(OFALL)
@ -1168,6 +1165,24 @@ func (w *exportWriter) stmt(n *Node) {
} }
} }
func (w *exportWriter) caseList(sw *Node) {
namedTypeSwitch := sw.Op == OSWITCH && sw.Left != nil && sw.Left.Op == OTYPESW && sw.Left.Left != nil
cases := sw.List.Slice()
w.uint64(uint64(len(cases)))
for _, cas := range cases {
if cas.Op != OCASE {
Fatalf("expected OCASE, got %v", cas)
}
w.pos(cas.Pos)
w.stmtList(cas.List)
if namedTypeSwitch {
w.localName(cas.Rlist.First())
}
w.stmtList(cas.Nbody)
}
}
func (w *exportWriter) exprList(list Nodes) { func (w *exportWriter) exprList(list Nodes) {
for _, n := range list.Slice() { for _, n := range list.Slice() {
w.expr(n) w.expr(n)
@ -1232,6 +1247,19 @@ func (w *exportWriter) expr(n *Node) {
w.op(OTYPE) w.op(OTYPE)
w.typ(n.Type) w.typ(n.Type)
case OTYPESW:
w.op(OTYPESW)
w.pos(n.Pos)
var s *types.Sym
if n.Left != nil {
if n.Left.Op != ONONAME {
Fatalf("expected ONONAME, got %v", n.Left)
}
s = n.Left.Sym
}
w.localIdent(s, 0) // declared pseudo-variable, if any
w.exprsOrNil(n.Right, nil)
// case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC: // case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
// should have been resolved by typechecking - handled by default case // should have been resolved by typechecking - handled by default case

View file

@ -784,6 +784,28 @@ func (r *importReader) stmtList() []*Node {
return list return list
} }
func (r *importReader) caseList(sw *Node) []*Node {
namedTypeSwitch := sw.Op == OSWITCH && sw.Left != nil && sw.Left.Op == OTYPESW && sw.Left.Left != nil
cases := make([]*Node, r.uint64())
for i := range cases {
cas := nodl(r.pos(), OCASE, nil, nil)
cas.List.Set(r.stmtList())
if namedTypeSwitch {
// Note: per-case variables will have distinct, dotted
// names after import. That's okay: swt.go only needs
// Sym for diagnostics anyway.
caseVar := newnamel(cas.Pos, r.ident())
declare(caseVar, dclcontext)
cas.Rlist.Set1(caseVar)
caseVar.Name.Defn = sw.Left
}
cas.Nbody.Set(r.stmtList())
cases[i] = cas
}
return cases
}
func (r *importReader) exprList() []*Node { func (r *importReader) exprList() []*Node {
var list []*Node var list []*Node
for { for {
@ -831,6 +853,14 @@ func (r *importReader) node() *Node {
case OTYPE: case OTYPE:
return typenod(r.typ()) return typenod(r.typ())
case OTYPESW:
n := nodl(r.pos(), OTYPESW, nil, nil)
if s := r.ident(); s != nil {
n.Left = npos(n.Pos, newnoname(s))
}
n.Right, _ = r.exprsOrNil()
return n
// case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC: // case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
// unreachable - should have been resolved by typechecking // unreachable - should have been resolved by typechecking
@ -1025,16 +1055,11 @@ func (r *importReader) node() *Node {
n := nodl(r.pos(), op, nil, nil) n := nodl(r.pos(), op, nil, nil)
n.Ninit.Set(r.stmtList()) n.Ninit.Set(r.stmtList())
n.Left, _ = r.exprsOrNil() n.Left, _ = r.exprsOrNil()
n.List.Set(r.stmtList()) n.List.Set(r.caseList(n))
return n return n
case OCASE: // case OCASE:
n := nodl(r.pos(), OCASE, nil, nil) // handled by caseList
n.List.Set(r.exprList())
// TODO(gri) eventually we must declare variables for type switch
// statements (type switch statements are not yet exported)
n.Nbody.Set(r.stmtList())
return n
case OFALL: case OFALL:
n := nodl(r.pos(), OFALL, nil, nil) n := nodl(r.pos(), OFALL, nil, nil)

View file

@ -94,10 +94,11 @@ func typecheckinl(fn *Node) {
typecheckslice(fn.Func.Inl.Body, ctxStmt) typecheckslice(fn.Func.Inl.Body, ctxStmt)
Curfn = savefn Curfn = savefn
// During typechecking, declarations are added to // During expandInline (which imports fn.Func.Inl.Body),
// Curfn.Func.Dcl. Move them to Inl.Dcl for consistency with // declarations are added to fn.Func.Dcl by funcHdr(). Move them
// how local functions behave. (Append because typecheckinl // to fn.Func.Inl.Dcl for consistency with how local functions
// may be called multiple times.) // behave. (Append because typecheckinl may be called multiple
// times.)
fn.Func.Inl.Dcl = append(fn.Func.Inl.Dcl, fn.Func.Dcl...) fn.Func.Inl.Dcl = append(fn.Func.Inl.Dcl, fn.Func.Dcl...)
fn.Func.Dcl = nil fn.Func.Dcl = nil
@ -392,13 +393,9 @@ func (v *hairyVisitor) visit(n *Node) bool {
v.reason = "call to recover" v.reason = "call to recover"
return true return true
case OCALLPART:
// OCALLPART is inlineable, but no extra cost to the budget
case OCLOSURE, case OCLOSURE,
ORANGE, ORANGE,
OSELECT, OSELECT,
OTYPESW,
OGO, OGO,
ODEFER, ODEFER,
ODCLTYPE, // can't print yet ODCLTYPE, // can't print yet
@ -452,9 +449,9 @@ func (v *hairyVisitor) visit(n *Node) bool {
v.visitList(n.Ninit) || v.visitList(n.Nbody) v.visitList(n.Ninit) || v.visitList(n.Nbody)
} }
// Inlcopy and inlcopylist recursively copy the body of a function. // inlcopylist (together with inlcopy) recursively copies a list of nodes, except
// Any name-like node of non-local class is marked for re-export by adding it to // that it keeps the same ONAME, OTYPE, and OLITERAL nodes. It is used for copying
// the exportlist. // the body and dcls of an inlineable function.
func inlcopylist(ll []*Node) []*Node { func inlcopylist(ll []*Node) []*Node {
s := make([]*Node, 0, len(ll)) s := make([]*Node, 0, len(ll))
for _, n := range ll { for _, n := range ll {
@ -893,10 +890,10 @@ func inlParam(t *types.Field, as *Node, inlvars map[*Node]*Node) *Node {
var inlgen int var inlgen int
// If n is a call, and fn is a function with an inlinable body, // If n is a call node (OCALLFUNC or OCALLMETH), and fn is an ONAME node for a
// return an OINLCALL. // function with an inlinable body, return an OINLCALL node that can replace n.
// On return ninit has the parameter assignments, the nbody is the // The returned node's Ninit has the parameter assignments, the Nbody is the
// inlined function body and list, rlist contain the input, output // inlined function body, and (List, Rlist) contain the (input, output)
// parameters. // parameters.
// The result of mkinlcall MUST be assigned back to n, e.g. // The result of mkinlcall MUST be assigned back to n, e.g.
// n.Left = mkinlcall(n.Left, fn, isddd) // n.Left = mkinlcall(n.Left, fn, isddd)
@ -966,6 +963,21 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
ninit := n.Ninit ninit := n.Ninit
// For normal function calls, the function callee expression
// may contain side effects (e.g., added by addinit during
// inlconv2expr or inlconv2list). Make sure to preserve these,
// if necessary (#42703).
if n.Op == OCALLFUNC {
callee := n.Left
for callee.Op == OCONVNOP {
ninit.AppendNodes(&callee.Ninit)
callee = callee.Left
}
if callee.Op != ONAME && callee.Op != OCLOSURE {
Fatalf("unexpected callee expression: %v", callee)
}
}
// Make temp names to use instead of the originals. // Make temp names to use instead of the originals.
inlvars := make(map[*Node]*Node) inlvars := make(map[*Node]*Node)

View file

@ -46,6 +46,7 @@ var (
Debug_closure int Debug_closure int
Debug_compilelater int Debug_compilelater int
debug_dclstack int debug_dclstack int
Debug_dumpptrs int
Debug_libfuzzer int Debug_libfuzzer int
Debug_panic int Debug_panic int
Debug_slice int Debug_slice int
@ -75,6 +76,7 @@ var debugtab = []struct {
{"compilelater", "compile functions as late as possible", &Debug_compilelater}, {"compilelater", "compile functions as late as possible", &Debug_compilelater},
{"disablenil", "disable nil checks", &disable_checknil}, {"disablenil", "disable nil checks", &disable_checknil},
{"dclstack", "run internal dclstack check", &debug_dclstack}, {"dclstack", "run internal dclstack check", &debug_dclstack},
{"dumpptrs", "show Node pointer values in Dump/dumplist output", &Debug_dumpptrs},
{"gcprog", "print dump of GC programs", &Debug_gcprog}, {"gcprog", "print dump of GC programs", &Debug_gcprog},
{"libfuzzer", "coverage instrumentation for libfuzzer", &Debug_libfuzzer}, {"libfuzzer", "coverage instrumentation for libfuzzer", &Debug_libfuzzer},
{"nil", "print information about nil checks", &Debug_checknil}, {"nil", "print information about nil checks", &Debug_checknil},
@ -89,6 +91,7 @@ var debugtab = []struct {
{"dwarfinl", "print information about DWARF inlined function creation", &Debug_gendwarfinl}, {"dwarfinl", "print information about DWARF inlined function creation", &Debug_gendwarfinl},
{"softfloat", "force compiler to emit soft-float code", &Debug_softfloat}, {"softfloat", "force compiler to emit soft-float code", &Debug_softfloat},
{"defer", "print information about defer compilation", &Debug_defer}, {"defer", "print information about defer compilation", &Debug_defer},
{"fieldtrack", "enable fieldtracking", &objabi.Fieldtrack_enabled},
} }
const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>] const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>]
@ -130,7 +133,7 @@ func hidePanic() {
// supportsDynlink reports whether or not the code generator for the given // supportsDynlink reports whether or not the code generator for the given
// architecture supports the -shared and -dynlink flags. // architecture supports the -shared and -dynlink flags.
func supportsDynlink(arch *sys.Arch) bool { func supportsDynlink(arch *sys.Arch) bool {
return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.S390X) return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X)
} }
// timing data for compiler phases // timing data for compiler phases

View file

@ -1591,8 +1591,12 @@ func dumptabs() {
// typ typeOff // pointer to symbol // typ typeOff // pointer to symbol
// } // }
nsym := dname(p.s.Name, "", nil, true) nsym := dname(p.s.Name, "", nil, true)
tsym := dtypesym(p.t)
ot = dsymptrOff(s, ot, nsym) ot = dsymptrOff(s, ot, nsym)
ot = dsymptrOff(s, ot, dtypesym(p.t)) ot = dsymptrOff(s, ot, tsym)
// Plugin exports symbols as interfaces. Mark their types
// as UsedInIface.
tsym.Set(obj.AttrUsedInIface, true)
} }
ggloblsym(s, int32(ot), int16(obj.RODATA)) ggloblsym(s, int32(ot), int16(obj.RODATA))

View file

@ -3458,14 +3458,64 @@ func init() {
s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
return s.newValue1(ssa.OpSelect0, types.Types[TUINT32], v) return s.newValue1(ssa.OpSelect0, types.Types[TUINT32], v)
}, },
sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) sys.AMD64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "Xchg64", addF("runtime/internal/atomic", "Xchg64",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value { func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
v := s.newValue3(ssa.OpAtomicExchange64, types.NewTuple(types.Types[TUINT64], types.TypeMem), args[0], args[1], s.mem()) v := s.newValue3(ssa.OpAtomicExchange64, types.NewTuple(types.Types[TUINT64], types.TypeMem), args[0], args[1], s.mem())
s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
return s.newValue1(ssa.OpSelect0, types.Types[TUINT64], v) return s.newValue1(ssa.OpSelect0, types.Types[TUINT64], v)
}, },
sys.AMD64, sys.ARM64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
type atomicOpEmitter func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType)
makeAtomicGuardedIntrinsicARM64 := func(op0, op1 ssa.Op, typ, rtyp types.EType, emit atomicOpEmitter) intrinsicBuilder {
return func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
// Target Atomic feature is identified by dynamic detection
addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), arm64HasATOMICS, s.sb)
v := s.load(types.Types[TBOOL], addr)
b := s.endBlock()
b.Kind = ssa.BlockIf
b.SetControl(v)
bTrue := s.f.NewBlock(ssa.BlockPlain)
bFalse := s.f.NewBlock(ssa.BlockPlain)
bEnd := s.f.NewBlock(ssa.BlockPlain)
b.AddEdgeTo(bTrue)
b.AddEdgeTo(bFalse)
b.Likely = ssa.BranchLikely
// We have atomic instructions - use it directly.
s.startBlock(bTrue)
emit(s, n, args, op1, typ)
s.endBlock().AddEdgeTo(bEnd)
// Use original instruction sequence.
s.startBlock(bFalse)
emit(s, n, args, op0, typ)
s.endBlock().AddEdgeTo(bEnd)
// Merge results.
s.startBlock(bEnd)
if rtyp == TNIL {
return nil
} else {
return s.variable(n, types.Types[rtyp])
}
}
}
atomicXchgXaddEmitterARM64 := func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType) {
v := s.newValue3(op, types.NewTuple(types.Types[typ], types.TypeMem), args[0], args[1], s.mem())
s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
s.vars[n] = s.newValue1(ssa.OpSelect0, types.Types[typ], v)
}
addF("runtime/internal/atomic", "Xchg",
makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicExchange32, ssa.OpAtomicExchange32Variant, TUINT32, TUINT32, atomicXchgXaddEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "Xchg64",
makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicExchange64, ssa.OpAtomicExchange64Variant, TUINT64, TUINT64, atomicXchgXaddEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "Xadd", addF("runtime/internal/atomic", "Xadd",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value { func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
@ -3482,46 +3532,11 @@ func init() {
}, },
sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
makeXaddARM64 := func(op0 ssa.Op, op1 ssa.Op, ty types.EType) func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
return func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
// Target Atomic feature is identified by dynamic detection
addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), arm64HasATOMICS, s.sb)
v := s.load(types.Types[TBOOL], addr)
b := s.endBlock()
b.Kind = ssa.BlockIf
b.SetControl(v)
bTrue := s.f.NewBlock(ssa.BlockPlain)
bFalse := s.f.NewBlock(ssa.BlockPlain)
bEnd := s.f.NewBlock(ssa.BlockPlain)
b.AddEdgeTo(bTrue)
b.AddEdgeTo(bFalse)
b.Likely = ssa.BranchUnlikely // most machines don't have Atomics nowadays
// We have atomic instructions - use it directly.
s.startBlock(bTrue)
v0 := s.newValue3(op1, types.NewTuple(types.Types[ty], types.TypeMem), args[0], args[1], s.mem())
s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v0)
s.vars[n] = s.newValue1(ssa.OpSelect0, types.Types[ty], v0)
s.endBlock().AddEdgeTo(bEnd)
// Use original instruction sequence.
s.startBlock(bFalse)
v1 := s.newValue3(op0, types.NewTuple(types.Types[ty], types.TypeMem), args[0], args[1], s.mem())
s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v1)
s.vars[n] = s.newValue1(ssa.OpSelect0, types.Types[ty], v1)
s.endBlock().AddEdgeTo(bEnd)
// Merge results.
s.startBlock(bEnd)
return s.variable(n, types.Types[ty])
}
}
addF("runtime/internal/atomic", "Xadd", addF("runtime/internal/atomic", "Xadd",
makeXaddARM64(ssa.OpAtomicAdd32, ssa.OpAtomicAdd32Variant, TUINT32), makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAdd32, ssa.OpAtomicAdd32Variant, TUINT32, TUINT32, atomicXchgXaddEmitterARM64),
sys.ARM64) sys.ARM64)
addF("runtime/internal/atomic", "Xadd64", addF("runtime/internal/atomic", "Xadd64",
makeXaddARM64(ssa.OpAtomicAdd64, ssa.OpAtomicAdd64Variant, TUINT64), makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAdd64, ssa.OpAtomicAdd64Variant, TUINT64, TUINT64, atomicXchgXaddEmitterARM64),
sys.ARM64) sys.ARM64)
addF("runtime/internal/atomic", "Cas", addF("runtime/internal/atomic", "Cas",
@ -3530,14 +3545,14 @@ func init() {
s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
return s.newValue1(ssa.OpSelect0, types.Types[TBOOL], v) return s.newValue1(ssa.OpSelect0, types.Types[TBOOL], v)
}, },
sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) sys.AMD64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "Cas64", addF("runtime/internal/atomic", "Cas64",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value { func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
v := s.newValue4(ssa.OpAtomicCompareAndSwap64, types.NewTuple(types.Types[TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem()) v := s.newValue4(ssa.OpAtomicCompareAndSwap64, types.NewTuple(types.Types[TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem())
s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
return s.newValue1(ssa.OpSelect0, types.Types[TBOOL], v) return s.newValue1(ssa.OpSelect0, types.Types[TBOOL], v)
}, },
sys.AMD64, sys.ARM64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "CasRel", addF("runtime/internal/atomic", "CasRel",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value { func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
v := s.newValue4(ssa.OpAtomicCompareAndSwap32, types.NewTuple(types.Types[TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem()) v := s.newValue4(ssa.OpAtomicCompareAndSwap32, types.NewTuple(types.Types[TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem())
@ -3546,18 +3561,31 @@ func init() {
}, },
sys.PPC64) sys.PPC64)
atomicCasEmitterARM64 := func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType) {
v := s.newValue4(op, types.NewTuple(types.Types[TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem())
s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
s.vars[n] = s.newValue1(ssa.OpSelect0, types.Types[typ], v)
}
addF("runtime/internal/atomic", "Cas",
makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicCompareAndSwap32, ssa.OpAtomicCompareAndSwap32Variant, TUINT32, TBOOL, atomicCasEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "Cas64",
makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicCompareAndSwap64, ssa.OpAtomicCompareAndSwap64Variant, TUINT64, TBOOL, atomicCasEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "And8", addF("runtime/internal/atomic", "And8",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value { func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
s.vars[&memVar] = s.newValue3(ssa.OpAtomicAnd8, types.TypeMem, args[0], args[1], s.mem()) s.vars[&memVar] = s.newValue3(ssa.OpAtomicAnd8, types.TypeMem, args[0], args[1], s.mem())
return nil return nil
}, },
sys.AMD64, sys.ARM64, sys.MIPS, sys.PPC64, sys.S390X) sys.AMD64, sys.MIPS, sys.PPC64, sys.S390X)
addF("runtime/internal/atomic", "And", addF("runtime/internal/atomic", "And",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value { func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
s.vars[&memVar] = s.newValue3(ssa.OpAtomicAnd32, types.TypeMem, args[0], args[1], s.mem()) s.vars[&memVar] = s.newValue3(ssa.OpAtomicAnd32, types.TypeMem, args[0], args[1], s.mem())
return nil return nil
}, },
sys.AMD64, sys.ARM64, sys.MIPS, sys.PPC64, sys.S390X) sys.AMD64, sys.MIPS, sys.PPC64, sys.S390X)
addF("runtime/internal/atomic", "Or8", addF("runtime/internal/atomic", "Or8",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value { func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr8, types.TypeMem, args[0], args[1], s.mem()) s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr8, types.TypeMem, args[0], args[1], s.mem())
@ -3569,7 +3597,24 @@ func init() {
s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr32, types.TypeMem, args[0], args[1], s.mem()) s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr32, types.TypeMem, args[0], args[1], s.mem())
return nil return nil
}, },
sys.AMD64, sys.ARM64, sys.MIPS, sys.PPC64, sys.S390X) sys.AMD64, sys.MIPS, sys.PPC64, sys.S390X)
atomicAndOrEmitterARM64 := func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType) {
s.vars[&memVar] = s.newValue3(op, types.TypeMem, args[0], args[1], s.mem())
}
addF("runtime/internal/atomic", "And8",
makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAnd8, ssa.OpAtomicAnd8Variant, TNIL, TNIL, atomicAndOrEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "And",
makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAnd32, ssa.OpAtomicAnd32Variant, TNIL, TNIL, atomicAndOrEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "Or8",
makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicOr8, ssa.OpAtomicOr8Variant, TNIL, TNIL, atomicAndOrEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "Or",
makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicOr32, ssa.OpAtomicOr32Variant, TNIL, TNIL, atomicAndOrEmitterARM64),
sys.ARM64)
alias("runtime/internal/atomic", "Loadint64", "runtime/internal/atomic", "Load64", all...) alias("runtime/internal/atomic", "Loadint64", "runtime/internal/atomic", "Load64", all...)
alias("runtime/internal/atomic", "Xaddint64", "runtime/internal/atomic", "Xadd64", all...) alias("runtime/internal/atomic", "Xaddint64", "runtime/internal/atomic", "Xadd64", all...)

View file

@ -344,14 +344,22 @@ func (n *Node) CanBeAnSSASym() {
// Name holds Node fields used only by named nodes (ONAME, OTYPE, OPACK, OLABEL, some OLITERAL). // Name holds Node fields used only by named nodes (ONAME, OTYPE, OPACK, OLABEL, some OLITERAL).
type Name struct { type Name struct {
Pack *Node // real package for import . names Pack *Node // real package for import . names
Pkg *types.Pkg // pkg for OPACK nodes Pkg *types.Pkg // pkg for OPACK nodes
Defn *Node // initializing assignment // For a local variable (not param) or extern, the initializing assignment (OAS or OAS2).
Curfn *Node // function for local variables // For a closure var, the ONAME node of the outer captured variable
Param *Param // additional fields for ONAME, OTYPE Defn *Node
Decldepth int32 // declaration loop depth, increased for every loop or label // The ODCLFUNC node (for a static function/method or a closure) in which
Vargen int32 // unique name for ONAME within a function. Function outputs are numbered starting at one. // local variable or param is declared.
flags bitset16 Curfn *Node
Param *Param // additional fields for ONAME, OTYPE
Decldepth int32 // declaration loop depth, increased for every loop or label
// Unique number for ONAME nodes within a function. Function outputs
// (results) are numbered starting at one, followed by function inputs
// (parameters), and then local variables. Vargen is used to distinguish
// local variables/params with the same name.
Vargen int32
flags bitset16
} }
const ( const (
@ -608,10 +616,16 @@ func (p *Param) SetEmbedFiles(list []string) {
// Func holds Node fields used only with function-like nodes. // Func holds Node fields used only with function-like nodes.
type Func struct { type Func struct {
Shortname *types.Sym Shortname *types.Sym
Enter Nodes // for example, allocate and initialize memory for escaping parameters // Extra entry code for the function. For example, allocate and initialize
Exit Nodes // memory for escaping parameters. However, just for OCLOSURE, Enter is a
Cvars Nodes // closure params // list of ONAME nodes of captured variables
Dcl []*Node // autodcl for this func/closure Enter Nodes
Exit Nodes
// ONAME nodes for closure params, each should have closurevar set
Cvars Nodes
// ONAME nodes for all params/locals for this func/closure, does NOT
// include closurevars until transformclosure runs.
Dcl []*Node
// Parents records the parent scope of each scope within a // Parents records the parent scope of each scope within a
// function. The root scope (0) has no parent, so the i'th // function. The root scope (0) has no parent, so the i'th
@ -630,7 +644,7 @@ type Func struct {
DebugInfo *ssa.FuncDebug DebugInfo *ssa.FuncDebug
Ntype *Node // signature Ntype *Node // signature
Top int // top context (ctxCallee, etc) Top int // top context (ctxCallee, etc)
Closure *Node // OCLOSURE <-> ODCLFUNC Closure *Node // OCLOSURE <-> ODCLFUNC (see header comment above)
Nname *Node // The ONAME node associated with an ODCLFUNC (both have same Type) Nname *Node // The ONAME node associated with an ODCLFUNC (both have same Type)
lsym *obj.LSym lsym *obj.LSym
@ -680,6 +694,8 @@ const (
funcWrapper // is method wrapper funcWrapper // is method wrapper
funcNeedctxt // function uses context register (has closure variables) funcNeedctxt // function uses context register (has closure variables)
funcReflectMethod // function calls reflect.Type.Method or MethodByName funcReflectMethod // function calls reflect.Type.Method or MethodByName
// true if closure inside a function; false if a simple function or a
// closure in a global variable initialization
funcIsHiddenClosure funcIsHiddenClosure
funcHasDefer // contains a defer statement funcHasDefer // contains a defer statement
funcNilCheckDisabled // disable nil checks when compiling this function funcNilCheckDisabled // disable nil checks when compiling this function
@ -731,8 +747,10 @@ const (
OXXX Op = iota OXXX Op = iota
// names // names
ONAME // var or func name ONAME // var or func name
ONONAME // unnamed arg or return value: f(int, string) (int, error) { etc } // Unnamed arg or return value: f(int, string) (int, error) { etc }
// Also used for a qualified package identifier that hasn't been resolved yet.
ONONAME
OTYPE // type name OTYPE // type name
OPACK // import OPACK // import
OLITERAL // literal OLITERAL // literal
@ -752,14 +770,18 @@ const (
OSTR2BYTES // Type(Left) (Type is []byte, Left is a string) OSTR2BYTES // Type(Left) (Type is []byte, Left is a string)
OSTR2BYTESTMP // Type(Left) (Type is []byte, Left is a string, ephemeral) OSTR2BYTESTMP // Type(Left) (Type is []byte, Left is a string, ephemeral)
OSTR2RUNES // Type(Left) (Type is []rune, Left is a string) OSTR2RUNES // Type(Left) (Type is []rune, Left is a string)
OAS // Left = Right or (if Colas=true) Left := Right // Left = Right or (if Colas=true) Left := Right
OAS2 // List = Rlist (x, y, z = a, b, c) // If Colas, then Ninit includes a DCL node for Left.
OAS2DOTTYPE // List = Right (x, ok = I.(int)) OAS
OAS2FUNC // List = Right (x, y = f()) // List = Rlist (x, y, z = a, b, c) or (if Colas=true) List := Rlist
OAS2MAPR // List = Right (x, ok = m["foo"]) // If Colas, then Ninit includes DCL nodes for List
OAS2RECV // List = Right (x, ok = <-c) OAS2
OASOP // Left Etype= Right (x += y) OAS2DOTTYPE // List = Right (x, ok = I.(int))
OCALL // Left(List) (function call, method call or type conversion) OAS2FUNC // List = Right (x, y = f())
OAS2MAPR // List = Right (x, ok = m["foo"])
OAS2RECV // List = Right (x, ok = <-c)
OASOP // Left Etype= Right (x += y)
OCALL // Left(List) (function call, method call or type conversion)
// OCALLFUNC, OCALLMETH, and OCALLINTER have the same structure. // OCALLFUNC, OCALLMETH, and OCALLINTER have the same structure.
// Prior to walk, they are: Left(List), where List is all regular arguments. // Prior to walk, they are: Left(List), where List is all regular arguments.

View file

@ -6,7 +6,6 @@ package gc
import ( import (
"cmd/compile/internal/types" "cmd/compile/internal/types"
"cmd/internal/objabi"
"fmt" "fmt"
"strings" "strings"
) )
@ -2065,11 +2064,6 @@ func typecheck1(n *Node, top int) (res *Node) {
n.Type = nil n.Type = nil
return n return n
case OCASE:
ok |= ctxStmt
typecheckslice(n.List.Slice(), ctxExpr)
typecheckslice(n.Nbody.Slice(), ctxStmt)
case ODCLFUNC: case ODCLFUNC:
ok |= ctxStmt ok |= ctxStmt
typecheckfunc(n) typecheckfunc(n)
@ -2447,15 +2441,6 @@ func derefall(t *types.Type) *types.Type {
return t return t
} }
type typeSymKey struct {
t *types.Type
s *types.Sym
}
// dotField maps (*types.Type, *types.Sym) pairs to the corresponding struct field (*types.Type with Etype==TFIELD).
// It is a cache for use during usefield in walk.go, only enabled when field tracking.
var dotField = map[typeSymKey]*types.Field{}
func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field { func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field {
s := n.Sym s := n.Sym
@ -2486,9 +2471,6 @@ func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field {
} }
n.Xoffset = f1.Offset n.Xoffset = f1.Offset
n.Type = f1.Type n.Type = f1.Type
if objabi.Fieldtrack_enabled > 0 {
dotField[typeSymKey{t.Orig, s}] = f1
}
if t.IsInterface() { if t.IsInterface() {
if n.Left.Type.IsPtr() { if n.Left.Type.IsPtr() {
n.Left = nod(ODEREF, n.Left, nil) // implicitstar n.Left = nod(ODEREF, n.Left, nil) // implicitstar
@ -2497,6 +2479,8 @@ func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field {
} }
n.Op = ODOTINTER n.Op = ODOTINTER
} else {
n.SetOpt(f1)
} }
return f1 return f1

View file

@ -3734,10 +3734,13 @@ func usefield(n *Node) {
if t.IsPtr() { if t.IsPtr() {
t = t.Elem() t = t.Elem()
} }
field := dotField[typeSymKey{t.Orig, n.Sym}] field := n.Opt().(*types.Field)
if field == nil { if field == nil {
Fatalf("usefield %v %v without paramfld", n.Left.Type, n.Sym) Fatalf("usefield %v %v without paramfld", n.Left.Type, n.Sym)
} }
if field.Sym != n.Sym || field.Offset != n.Xoffset {
Fatalf("field inconsistency: %v,%v != %v,%v", field.Sym, field.Offset, n.Sym, n.Xoffset)
}
if !strings.Contains(field.Note, "go:\"track\"") { if !strings.Contains(field.Note, "go:\"track\"") {
return return
} }

View file

@ -1781,6 +1781,9 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
pp := s.Call(v) pp := s.Call(v)
pp.To.Reg = ppc64.REG_LR pp.To.Reg = ppc64.REG_LR
// Insert a hint this is not a subroutine return.
pp.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: 1})
if gc.Ctxt.Flag_shared { if gc.Ctxt.Flag_shared {
// When compiling Go into PIC, the function we just // When compiling Go into PIC, the function we just
// called via pointer might have been implemented in // called via pointer might have been implemented in

View file

@ -188,6 +188,18 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
{Type: obj.TYPE_REG, Reg: r2}, {Type: obj.TYPE_REG, Reg: r2},
}) })
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: r1} p.To = obj.Addr{Type: obj.TYPE_REG, Reg: r1}
case ssa.OpS390XRISBGZ:
r1 := v.Reg()
r2 := v.Args[0].Reg()
i := v.Aux.(s390x.RotateParams)
p := s.Prog(v.Op.Asm())
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: int64(i.Start)}
p.SetRestArgs([]obj.Addr{
{Type: obj.TYPE_CONST, Offset: int64(i.End)},
{Type: obj.TYPE_CONST, Offset: int64(i.Amount)},
{Type: obj.TYPE_REG, Reg: r2},
})
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: r1}
case ssa.OpS390XADD, ssa.OpS390XADDW, case ssa.OpS390XADD, ssa.OpS390XADDW,
ssa.OpS390XSUB, ssa.OpS390XSUBW, ssa.OpS390XSUB, ssa.OpS390XSUBW,
ssa.OpS390XAND, ssa.OpS390XANDW, ssa.OpS390XAND, ssa.OpS390XANDW,
@ -360,7 +372,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
case ssa.OpS390XSLDconst, ssa.OpS390XSLWconst, case ssa.OpS390XSLDconst, ssa.OpS390XSLWconst,
ssa.OpS390XSRDconst, ssa.OpS390XSRWconst, ssa.OpS390XSRDconst, ssa.OpS390XSRWconst,
ssa.OpS390XSRADconst, ssa.OpS390XSRAWconst, ssa.OpS390XSRADconst, ssa.OpS390XSRAWconst,
ssa.OpS390XRLLGconst, ssa.OpS390XRLLconst: ssa.OpS390XRLLconst:
p := s.Prog(v.Op.Asm()) p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_CONST p.From.Type = obj.TYPE_CONST
p.From.Offset = v.AuxInt p.From.Offset = v.AuxInt

View file

@ -196,6 +196,9 @@ func expandCalls(f *Func) {
} }
if leaf.Op == OpIData { if leaf.Op == OpIData {
leafType = removeTrivialWrapperTypes(leaf.Type) leafType = removeTrivialWrapperTypes(leaf.Type)
if leafType.IsEmptyInterface() {
leafType = typ.BytePtr
}
} }
aux := selector.Aux aux := selector.Aux
auxInt := selector.AuxInt + offset auxInt := selector.AuxInt + offset

View file

@ -543,17 +543,24 @@
(AtomicStore64 ...) => (STLR ...) (AtomicStore64 ...) => (STLR ...)
(AtomicStorePtrNoWB ...) => (STLR ...) (AtomicStorePtrNoWB ...) => (STLR ...)
(AtomicExchange(32|64) ...) => (LoweredAtomicExchange(32|64) ...) (AtomicExchange(32|64) ...) => (LoweredAtomicExchange(32|64) ...)
(AtomicAdd(32|64) ...) => (LoweredAtomicAdd(32|64) ...) (AtomicAdd(32|64) ...) => (LoweredAtomicAdd(32|64) ...)
(AtomicCompareAndSwap(32|64) ...) => (LoweredAtomicCas(32|64) ...) (AtomicCompareAndSwap(32|64) ...) => (LoweredAtomicCas(32|64) ...)
(AtomicAdd(32|64)Variant ...) => (LoweredAtomicAdd(32|64)Variant ...)
(AtomicExchange(32|64)Variant ...) => (LoweredAtomicExchange(32|64)Variant ...)
(AtomicCompareAndSwap(32|64)Variant ...) => (LoweredAtomicCas(32|64)Variant ...)
// Currently the updated value is not used, but we need a register to temporarily hold it. // Currently the updated value is not used, but we need a register to temporarily hold it.
(AtomicAnd8 ptr val mem) => (Select1 (LoweredAtomicAnd8 ptr val mem)) (AtomicAnd8 ptr val mem) => (Select1 (LoweredAtomicAnd8 ptr val mem))
(AtomicAnd32 ptr val mem) => (Select1 (LoweredAtomicAnd32 ptr val mem)) (AtomicAnd32 ptr val mem) => (Select1 (LoweredAtomicAnd32 ptr val mem))
(AtomicOr8 ptr val mem) => (Select1 (LoweredAtomicOr8 ptr val mem)) (AtomicOr8 ptr val mem) => (Select1 (LoweredAtomicOr8 ptr val mem))
(AtomicOr32 ptr val mem) => (Select1 (LoweredAtomicOr32 ptr val mem)) (AtomicOr32 ptr val mem) => (Select1 (LoweredAtomicOr32 ptr val mem))
(AtomicAdd(32|64)Variant ...) => (LoweredAtomicAdd(32|64)Variant ...) (AtomicAnd8Variant ptr val mem) => (Select1 (LoweredAtomicAnd8Variant ptr val mem))
(AtomicAnd32Variant ptr val mem) => (Select1 (LoweredAtomicAnd32Variant ptr val mem))
(AtomicOr8Variant ptr val mem) => (Select1 (LoweredAtomicOr8Variant ptr val mem))
(AtomicOr32Variant ptr val mem) => (Select1 (LoweredAtomicOr32Variant ptr val mem))
// Write barrier. // Write barrier.
(WB ...) => (LoweredWB ...) (WB ...) => (LoweredWB ...)

View file

@ -621,6 +621,12 @@ func init() {
{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, {name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, {name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
// atomic exchange variant.
// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>. auxint must be zero.
// SWPALD Rarg1, (Rarg0), Rout
{name: "LoweredAtomicExchange64Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
{name: "LoweredAtomicExchange32Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
// atomic add. // atomic add.
// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>. auxint must be zero. // *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>. auxint must be zero.
// LDAXR (Rarg0), Rout // LDAXR (Rarg0), Rout
@ -654,6 +660,21 @@ func init() {
{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, {name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, {name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
// atomic compare and swap variant.
// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. auxint must be zero.
// if *arg0 == arg1 {
// *arg0 = arg2
// return (true, memory)
// } else {
// return (false, memory)
// }
// MOV Rarg1, Rtmp
// CASAL Rtmp, (Rarg0), Rarg2
// CMP Rarg1, Rtmp
// CSET EQ, Rout
{name: "LoweredAtomicCas64Variant", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
{name: "LoweredAtomicCas32Variant", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
// atomic and/or. // atomic and/or.
// *arg0 &= (|=) arg1. arg2=mem. returns <new content of *arg0, memory>. auxint must be zero. // *arg0 &= (|=) arg1. arg2=mem. returns <new content of *arg0, memory>. auxint must be zero.
// LDAXR (Rarg0), Rout // LDAXR (Rarg0), Rout
@ -665,6 +686,20 @@ func init() {
{name: "LoweredAtomicOr8", argLength: 3, reg: gpxchg, resultNotInArgs: true, asm: "ORR", typ: "(UInt8,Mem)", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, {name: "LoweredAtomicOr8", argLength: 3, reg: gpxchg, resultNotInArgs: true, asm: "ORR", typ: "(UInt8,Mem)", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
{name: "LoweredAtomicOr32", argLength: 3, reg: gpxchg, resultNotInArgs: true, asm: "ORR", typ: "(UInt32,Mem)", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, {name: "LoweredAtomicOr32", argLength: 3, reg: gpxchg, resultNotInArgs: true, asm: "ORR", typ: "(UInt32,Mem)", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
// atomic and/or variant.
// *arg0 &= (|=) arg1. arg2=mem. returns <new content of *arg0, memory>. auxint must be zero.
// AND:
// MNV Rarg1, Rtemp
// LDANDALB Rtemp, (Rarg0), Rout
// AND Rarg1, Rout
// OR:
// LDORALB Rarg1, (Rarg0), Rout
// ORR Rarg1, Rout
{name: "LoweredAtomicAnd8Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, typ: "(UInt8,Mem)", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
{name: "LoweredAtomicAnd32Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, typ: "(UInt32,Mem)", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
{name: "LoweredAtomicOr8Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, typ: "(UInt8,Mem)", faultOnNilArg0: true, hasSideEffects: true},
{name: "LoweredAtomicOr32Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, typ: "(UInt32,Mem)", faultOnNilArg0: true, hasSideEffects: true},
// LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier // LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier
// It saves all GP registers if necessary, // It saves all GP registers if necessary,
// but clobbers R30 (LR) because it's a call. // but clobbers R30 (LR) because it's a call.

View file

@ -96,17 +96,17 @@
(Rsh8Ux16 <t> x y) => (CMOVZ (SRL <t> (ZeroExt8to32 x) (ZeroExt16to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt16to32 y))) (Rsh8Ux16 <t> x y) => (CMOVZ (SRL <t> (ZeroExt8to32 x) (ZeroExt16to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt16to32 y)))
(Rsh8Ux8 <t> x y) => (CMOVZ (SRL <t> (ZeroExt8to32 x) (ZeroExt8to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt8to32 y))) (Rsh8Ux8 <t> x y) => (CMOVZ (SRL <t> (ZeroExt8to32 x) (ZeroExt8to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt8to32 y)))
(Rsh32x32 x y) => (SRA x ( CMOVZ <typ.UInt32> y (MOVWconst [-1]) (SGTUconst [32] y))) (Rsh32x32 x y) => (SRA x ( CMOVZ <typ.UInt32> y (MOVWconst [31]) (SGTUconst [32] y)))
(Rsh32x16 x y) => (SRA x ( CMOVZ <typ.UInt32> (ZeroExt16to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt16to32 y)))) (Rsh32x16 x y) => (SRA x ( CMOVZ <typ.UInt32> (ZeroExt16to32 y) (MOVWconst [31]) (SGTUconst [32] (ZeroExt16to32 y))))
(Rsh32x8 x y) => (SRA x ( CMOVZ <typ.UInt32> (ZeroExt8to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt8to32 y)))) (Rsh32x8 x y) => (SRA x ( CMOVZ <typ.UInt32> (ZeroExt8to32 y) (MOVWconst [31]) (SGTUconst [32] (ZeroExt8to32 y))))
(Rsh16x32 x y) => (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> y (MOVWconst [-1]) (SGTUconst [32] y))) (Rsh16x32 x y) => (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> y (MOVWconst [31]) (SGTUconst [32] y)))
(Rsh16x16 x y) => (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> (ZeroExt16to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt16to32 y)))) (Rsh16x16 x y) => (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> (ZeroExt16to32 y) (MOVWconst [31]) (SGTUconst [32] (ZeroExt16to32 y))))
(Rsh16x8 x y) => (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> (ZeroExt8to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt8to32 y)))) (Rsh16x8 x y) => (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> (ZeroExt8to32 y) (MOVWconst [31]) (SGTUconst [32] (ZeroExt8to32 y))))
(Rsh8x32 x y) => (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> y (MOVWconst [-1]) (SGTUconst [32] y))) (Rsh8x32 x y) => (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> y (MOVWconst [31]) (SGTUconst [32] y)))
(Rsh8x16 x y) => (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> (ZeroExt16to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt16to32 y)))) (Rsh8x16 x y) => (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> (ZeroExt16to32 y) (MOVWconst [31]) (SGTUconst [32] (ZeroExt16to32 y))))
(Rsh8x8 x y) => (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> (ZeroExt8to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt8to32 y)))) (Rsh8x8 x y) => (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> (ZeroExt8to32 y) (MOVWconst [31]) (SGTUconst [32] (ZeroExt8to32 y))))
// rotates // rotates
(RotateLeft8 <t> x (MOVWconst [c])) => (Or8 (Lsh8x32 <t> x (MOVWconst [c&7])) (Rsh8Ux32 <t> x (MOVWconst [-c&7]))) (RotateLeft8 <t> x (MOVWconst [c])) => (Or8 (Lsh8x32 <t> x (MOVWconst [c&7])) (Rsh8Ux32 <t> x (MOVWconst [-c&7])))
@ -567,10 +567,9 @@
(XOR x (MOVWconst [c])) => (XORconst [c] x) (XOR x (MOVWconst [c])) => (XORconst [c] x)
(NOR x (MOVWconst [c])) => (NORconst [c] x) (NOR x (MOVWconst [c])) => (NORconst [c] x)
(SRA x (MOVWconst [c])) && c >= 32 => (SRAconst x [31]) (SLL x (MOVWconst [c])) => (SLLconst x [c&31])
(SLL x (MOVWconst [c])) => (SLLconst x [c]) (SRL x (MOVWconst [c])) => (SRLconst x [c&31])
(SRL x (MOVWconst [c])) => (SRLconst x [c]) (SRA x (MOVWconst [c])) => (SRAconst x [c&31])
(SRA x (MOVWconst [c])) => (SRAconst x [c])
(SGT (MOVWconst [c]) x) => (SGTconst [c] x) (SGT (MOVWconst [c]) x) => (SGTconst [c] x)
(SGTU (MOVWconst [c]) x) => (SGTUconst [c] x) (SGTU (MOVWconst [c]) x) => (SGTUconst [c] x)

View file

@ -185,11 +185,11 @@ func init() {
// shifts // shifts
{name: "SLL", argLength: 2, reg: gp21, asm: "SLL"}, // arg0 << arg1, shift amount is mod 32 {name: "SLL", argLength: 2, reg: gp21, asm: "SLL"}, // arg0 << arg1, shift amount is mod 32
{name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32"}, // arg0 << auxInt {name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32"}, // arg0 << auxInt, shift amount must be 0 through 31 inclusive
{name: "SRL", argLength: 2, reg: gp21, asm: "SRL"}, // arg0 >> arg1, unsigned, shift amount is mod 32 {name: "SRL", argLength: 2, reg: gp21, asm: "SRL"}, // arg0 >> arg1, unsigned, shift amount is mod 32
{name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32"}, // arg0 >> auxInt, unsigned {name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32"}, // arg0 >> auxInt, shift amount must be 0 through 31 inclusive
{name: "SRA", argLength: 2, reg: gp21, asm: "SRA"}, // arg0 >> arg1, signed, shift amount is mod 32 {name: "SRA", argLength: 2, reg: gp21, asm: "SRA"}, // arg0 >> arg1, signed, shift amount is mod 32
{name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32"}, // arg0 >> auxInt, signed {name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32"}, // arg0 >> auxInt, signed, shift amount must be 0 through 31 inclusive
{name: "CLZ", argLength: 1, reg: gp11, asm: "CLZ"}, {name: "CLZ", argLength: 1, reg: gp11, asm: "CLZ"},

View file

@ -643,8 +643,18 @@
// equivalent to the leftmost 32 bits being set. // equivalent to the leftmost 32 bits being set.
// TODO(mundaym): modify the assembler to accept 64-bit values // TODO(mundaym): modify the assembler to accept 64-bit values
// and use isU32Bit(^c). // and use isU32Bit(^c).
(AND x (MOVDconst [c])) && is32Bit(c) && c < 0 => (ANDconst [c] x) (AND x (MOVDconst [c]))
(AND x (MOVDconst [c])) && is32Bit(c) && c >= 0 => (MOVWZreg (ANDWconst <typ.UInt32> [int32(c)] x)) && s390x.NewRotateParams(0, 63, 0).OutMerge(uint64(c)) != nil
=> (RISBGZ x {*s390x.NewRotateParams(0, 63, 0).OutMerge(uint64(c))})
(AND x (MOVDconst [c]))
&& is32Bit(c)
&& c < 0
=> (ANDconst [c] x)
(AND x (MOVDconst [c]))
&& is32Bit(c)
&& c >= 0
=> (MOVWZreg (ANDWconst <typ.UInt32> [int32(c)] x))
(ANDW x (MOVDconst [c])) => (ANDWconst [int32(c)] x) (ANDW x (MOVDconst [c])) => (ANDWconst [int32(c)] x)
((AND|ANDW)const [c] ((AND|ANDW)const [d] x)) => ((AND|ANDW)const [c&d] x) ((AND|ANDW)const [c] ((AND|ANDW)const [d] x)) => ((AND|ANDW)const [c&d] x)
@ -653,14 +663,20 @@
((OR|XOR)W x (MOVDconst [c])) => ((OR|XOR)Wconst [int32(c)] x) ((OR|XOR)W x (MOVDconst [c])) => ((OR|XOR)Wconst [int32(c)] x)
// Constant shifts. // Constant shifts.
(S(LD|RD|RAD|LW|RW|RAW) x (MOVDconst [c])) (S(LD|RD|RAD) x (MOVDconst [c])) => (S(LD|RD|RAD)const x [int8(c&63)])
=> (S(LD|RD|RAD|LW|RW|RAW)const x [int8(c&63)]) (S(LW|RW|RAW) x (MOVDconst [c])) && c&32 == 0 => (S(LW|RW|RAW)const x [int8(c&31)])
(S(LW|RW) _ (MOVDconst [c])) && c&32 != 0 => (MOVDconst [0])
(SRAW x (MOVDconst [c])) && c&32 != 0 => (SRAWconst x [31])
// Shifts only use the rightmost 6 bits of the shift value. // Shifts only use the rightmost 6 bits of the shift value.
(S(LD|RD|RAD|LW|RW|RAW) x (RISBGZ y {r}))
&& r.Amount == 0
&& r.OutMask()&63 == 63
=> (S(LD|RD|RAD|LW|RW|RAW) x y)
(S(LD|RD|RAD|LW|RW|RAW) x (AND (MOVDconst [c]) y)) (S(LD|RD|RAD|LW|RW|RAW) x (AND (MOVDconst [c]) y))
=> (S(LD|RD|RAD|LW|RW|RAW) x (ANDWconst <typ.UInt32> [int32(c&63)] y)) => (S(LD|RD|RAD|LW|RW|RAW) x (ANDWconst <typ.UInt32> [int32(c&63)] y))
(S(LD|RD|RAD|LW|RW|RAW) x (ANDWconst [c] y)) && c&63 == 63 (S(LD|RD|RAD|LW|RW|RAW) x (ANDWconst [c] y)) && c&63 == 63
=> (S(LD|RD|RAD|LW|RW|RAW) x y) => (S(LD|RD|RAD|LW|RW|RAW) x y)
(SLD x (MOV(W|H|B|WZ|HZ|BZ)reg y)) => (SLD x y) (SLD x (MOV(W|H|B|WZ|HZ|BZ)reg y)) => (SLD x y)
(SRD x (MOV(W|H|B|WZ|HZ|BZ)reg y)) => (SRD x y) (SRD x (MOV(W|H|B|WZ|HZ|BZ)reg y)) => (SRD x y)
(SRAD x (MOV(W|H|B|WZ|HZ|BZ)reg y)) => (SRAD x y) (SRAD x (MOV(W|H|B|WZ|HZ|BZ)reg y)) => (SRAD x y)
@ -668,17 +684,13 @@
(SRW x (MOV(W|H|B|WZ|HZ|BZ)reg y)) => (SRW x y) (SRW x (MOV(W|H|B|WZ|HZ|BZ)reg y)) => (SRW x y)
(SRAW x (MOV(W|H|B|WZ|HZ|BZ)reg y)) => (SRAW x y) (SRAW x (MOV(W|H|B|WZ|HZ|BZ)reg y)) => (SRAW x y)
// Constant rotate generation // Match rotate by constant.
(RLL x (MOVDconst [c])) => (RLLconst x [int8(c&31)]) (RLLG x (MOVDconst [c])) => (RISBGZ x {s390x.NewRotateParams(0, 63, int8(c&63))})
(RLLG x (MOVDconst [c])) => (RLLGconst x [int8(c&63)]) (RLL x (MOVDconst [c])) => (RLLconst x [int8(c&31)])
(ADD (SLDconst x [c]) (SRDconst x [d])) && d == 64-c => (RLLGconst [c] x) // Match rotate by constant pattern.
( OR (SLDconst x [c]) (SRDconst x [d])) && d == 64-c => (RLLGconst [c] x) ((ADD|OR|XOR) (SLDconst x [c]) (SRDconst x [64-c])) => (RISBGZ x {s390x.NewRotateParams(0, 63, c)})
(XOR (SLDconst x [c]) (SRDconst x [d])) && d == 64-c => (RLLGconst [c] x) ((ADD|OR|XOR)W (SLWconst x [c]) (SRWconst x [32-c])) => (RLLconst x [c])
(ADDW (SLWconst x [c]) (SRWconst x [d])) && d == 32-c => (RLLconst [c] x)
( ORW (SLWconst x [c]) (SRWconst x [d])) && d == 32-c => (RLLconst [c] x)
(XORW (SLWconst x [c]) (SRWconst x [d])) && d == 32-c => (RLLconst [c] x)
// Signed 64-bit comparison with immediate. // Signed 64-bit comparison with immediate.
(CMP x (MOVDconst [c])) && is32Bit(c) => (CMPconst x [int32(c)]) (CMP x (MOVDconst [c])) && is32Bit(c) => (CMPconst x [int32(c)])
@ -692,15 +704,97 @@
(CMP(W|WU) x (MOVDconst [c])) => (CMP(W|WU)const x [int32(c)]) (CMP(W|WU) x (MOVDconst [c])) => (CMP(W|WU)const x [int32(c)])
(CMP(W|WU) (MOVDconst [c]) x) => (InvertFlags (CMP(W|WU)const x [int32(c)])) (CMP(W|WU) (MOVDconst [c]) x) => (InvertFlags (CMP(W|WU)const x [int32(c)]))
// Match (x >> c) << d to 'rotate then insert selected bits [into zero]'.
(SLDconst (SRDconst x [c]) [d]) => (RISBGZ x {s390x.NewRotateParams(max8(0, c-d), 63-d, (d-c)&63)})
// Match (x << c) >> d to 'rotate then insert selected bits [into zero]'.
(SRDconst (SLDconst x [c]) [d]) => (RISBGZ x {s390x.NewRotateParams(d, min8(63, 63-c+d), (c-d)&63)})
// Absorb input zero extension into 'rotate then insert selected bits [into zero]'.
(RISBGZ (MOVWZreg x) {r}) && r.InMerge(0xffffffff) != nil => (RISBGZ x {*r.InMerge(0xffffffff)})
(RISBGZ (MOVHZreg x) {r}) && r.InMerge(0x0000ffff) != nil => (RISBGZ x {*r.InMerge(0x0000ffff)})
(RISBGZ (MOVBZreg x) {r}) && r.InMerge(0x000000ff) != nil => (RISBGZ x {*r.InMerge(0x000000ff)})
// Absorb 'rotate then insert selected bits [into zero]' into zero extension.
(MOVWZreg (RISBGZ x {r})) && r.OutMerge(0xffffffff) != nil => (RISBGZ x {*r.OutMerge(0xffffffff)})
(MOVHZreg (RISBGZ x {r})) && r.OutMerge(0x0000ffff) != nil => (RISBGZ x {*r.OutMerge(0x0000ffff)})
(MOVBZreg (RISBGZ x {r})) && r.OutMerge(0x000000ff) != nil => (RISBGZ x {*r.OutMerge(0x000000ff)})
// Absorb shift into 'rotate then insert selected bits [into zero]'.
//
// Any unsigned shift can be represented as a rotate and mask operation:
//
// x << c => RotateLeft64(x, c) & (^uint64(0) << c)
// x >> c => RotateLeft64(x, -c) & (^uint64(0) >> c)
//
// Therefore when a shift is used as the input to a rotate then insert
// selected bits instruction we can merge the two together. We just have
// to be careful that the resultant mask is representable (non-zero and
// contiguous). For example, assuming that x is variable and c, y and m
// are constants, a shift followed by a rotate then insert selected bits
// could be represented as:
//
// RotateLeft64(RotateLeft64(x, c) & (^uint64(0) << c), y) & m
//
// We can split the rotation by y into two, one rotate for x and one for
// the mask:
//
// RotateLeft64(RotateLeft64(x, c), y) & (RotateLeft64(^uint64(0) << c, y)) & m
//
// The rotations of x by c followed by y can then be combined:
//
// RotateLeft64(x, c+y) & (RotateLeft64(^uint64(0) << c, y)) & m
// ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// rotate mask
//
// To perform this optimization we therefore just need to check that it
// is valid to merge the shift mask (^(uint64(0)<<c)) into the selected
// bits mask (i.e. that the resultant mask is non-zero and contiguous).
//
(RISBGZ (SLDconst x [c]) {r}) && r.InMerge(^uint64(0)<<c) != nil => (RISBGZ x {(*r.InMerge(^uint64(0)<<c)).RotateLeft(c)})
(RISBGZ (SRDconst x [c]) {r}) && r.InMerge(^uint64(0)>>c) != nil => (RISBGZ x {(*r.InMerge(^uint64(0)>>c)).RotateLeft(-c)})
// Absorb 'rotate then insert selected bits [into zero]' into left shift.
(SLDconst (RISBGZ x {r}) [c])
&& s390x.NewRotateParams(0, 63-c, c).InMerge(r.OutMask()) != nil
=> (RISBGZ x {(*s390x.NewRotateParams(0, 63-c, c).InMerge(r.OutMask())).RotateLeft(r.Amount)})
// Absorb 'rotate then insert selected bits [into zero]' into right shift.
(SRDconst (RISBGZ x {r}) [c])
&& s390x.NewRotateParams(c, 63, -c&63).InMerge(r.OutMask()) != nil
=> (RISBGZ x {(*s390x.NewRotateParams(c, 63, -c&63).InMerge(r.OutMask())).RotateLeft(r.Amount)})
// Merge 'rotate then insert selected bits [into zero]' instructions together.
(RISBGZ (RISBGZ x {y}) {z})
&& z.InMerge(y.OutMask()) != nil
=> (RISBGZ x {(*z.InMerge(y.OutMask())).RotateLeft(y.Amount)})
// Convert RISBGZ into 64-bit shift (helps CSE).
(RISBGZ x {r}) && r.End == 63 && r.Start == -r.Amount&63 => (SRDconst x [-r.Amount&63])
(RISBGZ x {r}) && r.Start == 0 && r.End == 63-r.Amount => (SLDconst x [r.Amount])
// Optimize single bit isolation when it is known to be equivalent to
// the most significant bit due to mask produced by arithmetic shift.
// Simply isolate the most significant bit itself and place it in the
// correct position.
//
// Example: (int64(x) >> 63) & 0x8 -> RISBGZ $60, $60, $4, Rsrc, Rdst
(RISBGZ (SRADconst x [c]) {r})
&& r.Start == r.End // single bit selected
&& (r.Start+r.Amount)&63 <= c // equivalent to most significant bit of x
=> (RISBGZ x {s390x.NewRotateParams(r.Start, r.Start, -r.Start&63)})
// Canonicalize the order of arguments to comparisons - helps with CSE. // Canonicalize the order of arguments to comparisons - helps with CSE.
((CMP|CMPW|CMPU|CMPWU) x y) && x.ID > y.ID => (InvertFlags ((CMP|CMPW|CMPU|CMPWU) y x)) ((CMP|CMPW|CMPU|CMPWU) x y) && x.ID > y.ID => (InvertFlags ((CMP|CMPW|CMPU|CMPWU) y x))
// Using MOV{W,H,B}Zreg instead of AND is cheaper. // Use sign/zero extend instead of RISBGZ.
(AND x (MOVDconst [0xFF])) => (MOVBZreg x) (RISBGZ x {r}) && r == s390x.NewRotateParams(56, 63, 0) => (MOVBZreg x)
(AND x (MOVDconst [0xFFFF])) => (MOVHZreg x) (RISBGZ x {r}) && r == s390x.NewRotateParams(48, 63, 0) => (MOVHZreg x)
(AND x (MOVDconst [0xFFFFFFFF])) => (MOVWZreg x) (RISBGZ x {r}) && r == s390x.NewRotateParams(32, 63, 0) => (MOVWZreg x)
(ANDWconst [0xFF] x) => (MOVBZreg x)
(ANDWconst [0xFFFF] x) => (MOVHZreg x) // Use sign/zero extend instead of ANDW.
(ANDWconst [0x00ff] x) => (MOVBZreg x)
(ANDWconst [0xffff] x) => (MOVHZreg x)
// Strength reduce multiplication to the sum (or difference) of two powers of two. // Strength reduce multiplication to the sum (or difference) of two powers of two.
// //
@ -773,21 +867,22 @@
// detect attempts to set/clear the sign bit // detect attempts to set/clear the sign bit
// may need to be reworked when NIHH/OIHH are added // may need to be reworked when NIHH/OIHH are added
(SRDconst [1] (SLDconst [1] (LGDR <t> x))) => (LGDR <t> (LPDFR <x.Type> x)) (RISBGZ (LGDR <t> x) {r}) && r == s390x.NewRotateParams(1, 63, 0) => (LGDR <t> (LPDFR <x.Type> x))
(LDGR <t> (SRDconst [1] (SLDconst [1] x))) => (LPDFR (LDGR <t> x)) (LDGR <t> (RISBGZ x {r})) && r == s390x.NewRotateParams(1, 63, 0) => (LPDFR (LDGR <t> x))
(AND (MOVDconst [^(-1<<63)]) (LGDR <t> x)) => (LGDR <t> (LPDFR <x.Type> x)) (OR (MOVDconst [-1<<63]) (LGDR <t> x)) => (LGDR <t> (LNDFR <x.Type> x))
(LDGR <t> (AND (MOVDconst [^(-1<<63)]) x)) => (LPDFR (LDGR <t> x)) (LDGR <t> (OR (MOVDconst [-1<<63]) x)) => (LNDFR (LDGR <t> x))
(OR (MOVDconst [-1<<63]) (LGDR <t> x)) => (LGDR <t> (LNDFR <x.Type> x))
(LDGR <t> (OR (MOVDconst [-1<<63]) x)) => (LNDFR (LDGR <t> x))
// detect attempts to set the sign bit with load // detect attempts to set the sign bit with load
(LDGR <t> x:(ORload <t1> [off] {sym} (MOVDconst [-1<<63]) ptr mem)) && x.Uses == 1 && clobber(x) => @x.Block (LNDFR <t> (LDGR <t> (MOVDload <t1> [off] {sym} ptr mem))) (LDGR <t> x:(ORload <t1> [off] {sym} (MOVDconst [-1<<63]) ptr mem)) && x.Uses == 1 && clobber(x) => @x.Block (LNDFR <t> (LDGR <t> (MOVDload <t1> [off] {sym} ptr mem)))
// detect copysign // detect copysign
(OR (SLDconst [63] (SRDconst [63] (LGDR x))) (LGDR (LPDFR <t> y))) => (LGDR (CPSDR <t> y x)) (OR (RISBGZ (LGDR x) {r}) (LGDR (LPDFR <t> y)))
(OR (SLDconst [63] (SRDconst [63] (LGDR x))) (MOVDconst [c])) && c & -1<<63 == 0 => (LGDR (CPSDR <x.Type> (FMOVDconst <x.Type> [math.Float64frombits(uint64(c))]) x)) && r == s390x.NewRotateParams(0, 0, 0)
(OR (AND (MOVDconst [-1<<63]) (LGDR x)) (LGDR (LPDFR <t> y))) => (LGDR (CPSDR <t> y x)) => (LGDR (CPSDR <t> y x))
(OR (AND (MOVDconst [-1<<63]) (LGDR x)) (MOVDconst [c])) && c & -1<<63 == 0 => (LGDR (CPSDR <x.Type> (FMOVDconst <x.Type> [math.Float64frombits(uint64(c))]) x)) (OR (RISBGZ (LGDR x) {r}) (MOVDconst [c]))
&& c >= 0
&& r == s390x.NewRotateParams(0, 0, 0)
=> (LGDR (CPSDR <x.Type> (FMOVDconst <x.Type> [math.Float64frombits(uint64(c))]) x))
(CPSDR y (FMOVDconst [c])) && !math.Signbit(c) => (LPDFR y) (CPSDR y (FMOVDconst [c])) && !math.Signbit(c) => (LPDFR y)
(CPSDR y (FMOVDconst [c])) && math.Signbit(c) => (LNDFR y) (CPSDR y (FMOVDconst [c])) && math.Signbit(c) => (LNDFR y)
@ -966,6 +1061,9 @@
(CMPWconst (ANDWconst _ [m]) [n]) && int32(m) >= 0 && int32(m) < int32(n) => (FlagLT) (CMPWconst (ANDWconst _ [m]) [n]) && int32(m) >= 0 && int32(m) < int32(n) => (FlagLT)
(CMPWUconst (ANDWconst _ [m]) [n]) && uint32(m) < uint32(n) => (FlagLT) (CMPWUconst (ANDWconst _ [m]) [n]) && uint32(m) < uint32(n) => (FlagLT)
(CMPconst (RISBGZ x {r}) [c]) && c > 0 && r.OutMask() < uint64(c) => (FlagLT)
(CMPUconst (RISBGZ x {r}) [c]) && r.OutMask() < uint64(uint32(c)) => (FlagLT)
// Constant compare-and-branch with immediate. // Constant compare-and-branch with immediate.
(CGIJ {c} (MOVDconst [x]) [y] yes no) && c&s390x.Equal != 0 && int64(x) == int64(y) => (First yes no) (CGIJ {c} (MOVDconst [x]) [y] yes no) && c&s390x.Equal != 0 && int64(x) == int64(y) => (First yes no)
(CGIJ {c} (MOVDconst [x]) [y] yes no) && c&s390x.Less != 0 && int64(x) < int64(y) => (First yes no) (CGIJ {c} (MOVDconst [x]) [y] yes no) && c&s390x.Less != 0 && int64(x) < int64(y) => (First yes no)

View file

@ -331,25 +331,26 @@ func init() {
{name: "LTEBR", argLength: 1, reg: fp1flags, asm: "LTEBR", typ: "Flags"}, // arg0 compare to 0, f32 {name: "LTEBR", argLength: 1, reg: fp1flags, asm: "LTEBR", typ: "Flags"}, // arg0 compare to 0, f32
{name: "SLD", argLength: 2, reg: sh21, asm: "SLD"}, // arg0 << arg1, shift amount is mod 64 {name: "SLD", argLength: 2, reg: sh21, asm: "SLD"}, // arg0 << arg1, shift amount is mod 64
{name: "SLW", argLength: 2, reg: sh21, asm: "SLW"}, // arg0 << arg1, shift amount is mod 32 {name: "SLW", argLength: 2, reg: sh21, asm: "SLW"}, // arg0 << arg1, shift amount is mod 64
{name: "SLDconst", argLength: 1, reg: gp11, asm: "SLD", aux: "Int8"}, // arg0 << auxint, shift amount 0-63 {name: "SLDconst", argLength: 1, reg: gp11, asm: "SLD", aux: "Int8"}, // arg0 << auxint, shift amount 0-63
{name: "SLWconst", argLength: 1, reg: gp11, asm: "SLW", aux: "Int8"}, // arg0 << auxint, shift amount 0-31 {name: "SLWconst", argLength: 1, reg: gp11, asm: "SLW", aux: "Int8"}, // arg0 << auxint, shift amount 0-31
{name: "SRD", argLength: 2, reg: sh21, asm: "SRD"}, // unsigned arg0 >> arg1, shift amount is mod 64 {name: "SRD", argLength: 2, reg: sh21, asm: "SRD"}, // unsigned arg0 >> arg1, shift amount is mod 64
{name: "SRW", argLength: 2, reg: sh21, asm: "SRW"}, // unsigned uint32(arg0) >> arg1, shift amount is mod 32 {name: "SRW", argLength: 2, reg: sh21, asm: "SRW"}, // unsigned uint32(arg0) >> arg1, shift amount is mod 64
{name: "SRDconst", argLength: 1, reg: gp11, asm: "SRD", aux: "Int8"}, // unsigned arg0 >> auxint, shift amount 0-63 {name: "SRDconst", argLength: 1, reg: gp11, asm: "SRD", aux: "Int8"}, // unsigned arg0 >> auxint, shift amount 0-63
{name: "SRWconst", argLength: 1, reg: gp11, asm: "SRW", aux: "Int8"}, // unsigned uint32(arg0) >> auxint, shift amount 0-31 {name: "SRWconst", argLength: 1, reg: gp11, asm: "SRW", aux: "Int8"}, // unsigned uint32(arg0) >> auxint, shift amount 0-31
// Arithmetic shifts clobber flags. // Arithmetic shifts clobber flags.
{name: "SRAD", argLength: 2, reg: sh21, asm: "SRAD", clobberFlags: true}, // signed arg0 >> arg1, shift amount is mod 64 {name: "SRAD", argLength: 2, reg: sh21, asm: "SRAD", clobberFlags: true}, // signed arg0 >> arg1, shift amount is mod 64
{name: "SRAW", argLength: 2, reg: sh21, asm: "SRAW", clobberFlags: true}, // signed int32(arg0) >> arg1, shift amount is mod 32 {name: "SRAW", argLength: 2, reg: sh21, asm: "SRAW", clobberFlags: true}, // signed int32(arg0) >> arg1, shift amount is mod 64
{name: "SRADconst", argLength: 1, reg: gp11, asm: "SRAD", aux: "Int8", clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-63 {name: "SRADconst", argLength: 1, reg: gp11, asm: "SRAD", aux: "Int8", clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-63
{name: "SRAWconst", argLength: 1, reg: gp11, asm: "SRAW", aux: "Int8", clobberFlags: true}, // signed int32(arg0) >> auxint, shift amount 0-31 {name: "SRAWconst", argLength: 1, reg: gp11, asm: "SRAW", aux: "Int8", clobberFlags: true}, // signed int32(arg0) >> auxint, shift amount 0-31
{name: "RLLG", argLength: 2, reg: sh21, asm: "RLLG"}, // arg0 rotate left arg1, rotate amount 0-63 // Rotate instructions.
{name: "RLL", argLength: 2, reg: sh21, asm: "RLL"}, // arg0 rotate left arg1, rotate amount 0-31 // Note: no RLLGconst - use RISBGZ instead.
{name: "RLLGconst", argLength: 1, reg: gp11, asm: "RLLG", aux: "Int8"}, // arg0 rotate left auxint, rotate amount 0-63 {name: "RLLG", argLength: 2, reg: sh21, asm: "RLLG"}, // arg0 rotate left arg1, rotate amount 0-63
{name: "RLLconst", argLength: 1, reg: gp11, asm: "RLL", aux: "Int8"}, // arg0 rotate left auxint, rotate amount 0-31 {name: "RLL", argLength: 2, reg: sh21, asm: "RLL"}, // arg0 rotate left arg1, rotate amount 0-31
{name: "RLLconst", argLength: 1, reg: gp11, asm: "RLL", aux: "Int8"}, // arg0 rotate left auxint, rotate amount 0-31
// Rotate then (and|or|xor|insert) selected bits instructions. // Rotate then (and|or|xor|insert) selected bits instructions.
// //
@ -371,6 +372,7 @@ func init() {
// +-------------+-------+-----+--------+-----------------------+-----------------------+-----------------------+ // +-------------+-------+-----+--------+-----------------------+-----------------------+-----------------------+
// //
{name: "RXSBG", argLength: 2, reg: gp21, asm: "RXSBG", resultInArg0: true, aux: "S390XRotateParams", clobberFlags: true}, // rotate then xor selected bits {name: "RXSBG", argLength: 2, reg: gp21, asm: "RXSBG", resultInArg0: true, aux: "S390XRotateParams", clobberFlags: true}, // rotate then xor selected bits
{name: "RISBGZ", argLength: 1, reg: gp11, asm: "RISBGZ", aux: "S390XRotateParams", clobberFlags: true}, // rotate then insert selected bits [into zero]
// unary ops // unary ops
{name: "NEG", argLength: 1, reg: gp11, asm: "NEG", clobberFlags: true}, // -arg0 {name: "NEG", argLength: 1, reg: gp11, asm: "NEG", clobberFlags: true}, // -arg0
@ -547,9 +549,9 @@ func init() {
// Atomic bitwise operations. // Atomic bitwise operations.
// Note: 'floor' operations round the pointer down to the nearest word boundary // Note: 'floor' operations round the pointer down to the nearest word boundary
// which reflects how they are used in the runtime. // which reflects how they are used in the runtime.
{name: "LAN", argLength: 3, reg: gpstore, asm: "LAN", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *arg0 &= arg1. arg2 = mem. {name: "LAN", argLength: 3, reg: gpstore, asm: "LAN", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *arg0 &= arg1. arg2 = mem.
{name: "LANfloor", argLength: 3, reg: gpstorelab, asm: "LAN", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *(floor(arg0, 4)) &= arg1. arg2 = mem. {name: "LANfloor", argLength: 3, reg: gpstorelab, asm: "LAN", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *(floor(arg0, 4)) &= arg1. arg2 = mem.
{name: "LAO", argLength: 3, reg: gpstore, asm: "LAO", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *arg0 |= arg1. arg2 = mem. {name: "LAO", argLength: 3, reg: gpstore, asm: "LAO", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *arg0 |= arg1. arg2 = mem.
{name: "LAOfloor", argLength: 3, reg: gpstorelab, asm: "LAO", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *(floor(arg0, 4)) |= arg1. arg2 = mem. {name: "LAOfloor", argLength: 3, reg: gpstorelab, asm: "LAO", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *(floor(arg0, 4)) |= arg1. arg2 = mem.
// Compare and swap. // Compare and swap.

View file

@ -574,8 +574,16 @@ var genericOps = []opData{
// These variants have the same semantics as above atomic operations. // These variants have the same semantics as above atomic operations.
// But they are used for generating more efficient code on certain modern machines, with run-time CPU feature detection. // But they are used for generating more efficient code on certain modern machines, with run-time CPU feature detection.
// Currently, they are used on ARM64 only. // Currently, they are used on ARM64 only.
{name: "AtomicAdd32Variant", argLength: 3, typ: "(UInt32,Mem)", hasSideEffects: true}, // Do *arg0 += arg1. arg2=memory. Returns sum and new memory. {name: "AtomicAdd32Variant", argLength: 3, typ: "(UInt32,Mem)", hasSideEffects: true}, // Do *arg0 += arg1. arg2=memory. Returns sum and new memory.
{name: "AtomicAdd64Variant", argLength: 3, typ: "(UInt64,Mem)", hasSideEffects: true}, // Do *arg0 += arg1. arg2=memory. Returns sum and new memory. {name: "AtomicAdd64Variant", argLength: 3, typ: "(UInt64,Mem)", hasSideEffects: true}, // Do *arg0 += arg1. arg2=memory. Returns sum and new memory.
{name: "AtomicExchange32Variant", argLength: 3, typ: "(UInt32,Mem)", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Returns old contents of *arg0 and new memory.
{name: "AtomicExchange64Variant", argLength: 3, typ: "(UInt64,Mem)", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Returns old contents of *arg0 and new memory.
{name: "AtomicCompareAndSwap32Variant", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true}, // if *arg0==arg1, then set *arg0=arg2. Returns true if store happens and new memory.
{name: "AtomicCompareAndSwap64Variant", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true}, // if *arg0==arg1, then set *arg0=arg2. Returns true if store happens and new memory.
{name: "AtomicAnd8Variant", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 &= arg1. arg2=memory. Returns memory.
{name: "AtomicAnd32Variant", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 &= arg1. arg2=memory. Returns memory.
{name: "AtomicOr8Variant", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 |= arg1. arg2=memory. Returns memory.
{name: "AtomicOr32Variant", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 |= arg1. arg2=memory. Returns memory.
// Clobber experiment op // Clobber experiment op
{name: "Clobber", argLength: 0, typ: "Void", aux: "SymOff", symEffect: "None"}, // write an invalid pointer value to the given pointer slot of a stack variable {name: "Clobber", argLength: 0, typ: "Void", aux: "SymOff", symEffect: "None"}, // write an invalid pointer value to the given pointer slot of a stack variable

View file

@ -1581,16 +1581,24 @@ const (
OpARM64STLRW OpARM64STLRW
OpARM64LoweredAtomicExchange64 OpARM64LoweredAtomicExchange64
OpARM64LoweredAtomicExchange32 OpARM64LoweredAtomicExchange32
OpARM64LoweredAtomicExchange64Variant
OpARM64LoweredAtomicExchange32Variant
OpARM64LoweredAtomicAdd64 OpARM64LoweredAtomicAdd64
OpARM64LoweredAtomicAdd32 OpARM64LoweredAtomicAdd32
OpARM64LoweredAtomicAdd64Variant OpARM64LoweredAtomicAdd64Variant
OpARM64LoweredAtomicAdd32Variant OpARM64LoweredAtomicAdd32Variant
OpARM64LoweredAtomicCas64 OpARM64LoweredAtomicCas64
OpARM64LoweredAtomicCas32 OpARM64LoweredAtomicCas32
OpARM64LoweredAtomicCas64Variant
OpARM64LoweredAtomicCas32Variant
OpARM64LoweredAtomicAnd8 OpARM64LoweredAtomicAnd8
OpARM64LoweredAtomicAnd32 OpARM64LoweredAtomicAnd32
OpARM64LoweredAtomicOr8 OpARM64LoweredAtomicOr8
OpARM64LoweredAtomicOr32 OpARM64LoweredAtomicOr32
OpARM64LoweredAtomicAnd8Variant
OpARM64LoweredAtomicAnd32Variant
OpARM64LoweredAtomicOr8Variant
OpARM64LoweredAtomicOr32Variant
OpARM64LoweredWB OpARM64LoweredWB
OpARM64LoweredPanicBoundsA OpARM64LoweredPanicBoundsA
OpARM64LoweredPanicBoundsB OpARM64LoweredPanicBoundsB
@ -2277,9 +2285,9 @@ const (
OpS390XSRAWconst OpS390XSRAWconst
OpS390XRLLG OpS390XRLLG
OpS390XRLL OpS390XRLL
OpS390XRLLGconst
OpS390XRLLconst OpS390XRLLconst
OpS390XRXSBG OpS390XRXSBG
OpS390XRISBGZ
OpS390XNEG OpS390XNEG
OpS390XNEGW OpS390XNEGW
OpS390XNOT OpS390XNOT
@ -2881,6 +2889,14 @@ const (
OpAtomicOr32 OpAtomicOr32
OpAtomicAdd32Variant OpAtomicAdd32Variant
OpAtomicAdd64Variant OpAtomicAdd64Variant
OpAtomicExchange32Variant
OpAtomicExchange64Variant
OpAtomicCompareAndSwap32Variant
OpAtomicCompareAndSwap64Variant
OpAtomicAnd8Variant
OpAtomicAnd32Variant
OpAtomicOr8Variant
OpAtomicOr32Variant
OpClobber OpClobber
) )
@ -20994,6 +21010,38 @@ var opcodeTable = [...]opInfo{
}, },
}, },
}, },
{
name: "LoweredAtomicExchange64Variant",
argLen: 3,
resultNotInArgs: true,
faultOnNilArg0: true,
hasSideEffects: true,
reg: regInfo{
inputs: []inputInfo{
{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
},
outputs: []outputInfo{
{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
},
},
},
{
name: "LoweredAtomicExchange32Variant",
argLen: 3,
resultNotInArgs: true,
faultOnNilArg0: true,
hasSideEffects: true,
reg: regInfo{
inputs: []inputInfo{
{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
},
outputs: []outputInfo{
{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
},
},
},
{ {
name: "LoweredAtomicAdd64", name: "LoweredAtomicAdd64",
argLen: 3, argLen: 3,
@ -21098,6 +21146,44 @@ var opcodeTable = [...]opInfo{
}, },
}, },
}, },
{
name: "LoweredAtomicCas64Variant",
argLen: 4,
resultNotInArgs: true,
clobberFlags: true,
faultOnNilArg0: true,
hasSideEffects: true,
unsafePoint: true,
reg: regInfo{
inputs: []inputInfo{
{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
{2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
},
outputs: []outputInfo{
{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
},
},
},
{
name: "LoweredAtomicCas32Variant",
argLen: 4,
resultNotInArgs: true,
clobberFlags: true,
faultOnNilArg0: true,
hasSideEffects: true,
unsafePoint: true,
reg: regInfo{
inputs: []inputInfo{
{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
{2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
},
outputs: []outputInfo{
{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
},
},
},
{ {
name: "LoweredAtomicAnd8", name: "LoweredAtomicAnd8",
argLen: 3, argLen: 3,
@ -21170,6 +21256,72 @@ var opcodeTable = [...]opInfo{
}, },
}, },
}, },
{
name: "LoweredAtomicAnd8Variant",
argLen: 3,
resultNotInArgs: true,
faultOnNilArg0: true,
hasSideEffects: true,
unsafePoint: true,
reg: regInfo{
inputs: []inputInfo{
{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
},
outputs: []outputInfo{
{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
},
},
},
{
name: "LoweredAtomicAnd32Variant",
argLen: 3,
resultNotInArgs: true,
faultOnNilArg0: true,
hasSideEffects: true,
unsafePoint: true,
reg: regInfo{
inputs: []inputInfo{
{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
},
outputs: []outputInfo{
{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
},
},
},
{
name: "LoweredAtomicOr8Variant",
argLen: 3,
resultNotInArgs: true,
faultOnNilArg0: true,
hasSideEffects: true,
reg: regInfo{
inputs: []inputInfo{
{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
},
outputs: []outputInfo{
{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
},
},
},
{
name: "LoweredAtomicOr32Variant",
argLen: 3,
resultNotInArgs: true,
faultOnNilArg0: true,
hasSideEffects: true,
reg: regInfo{
inputs: []inputInfo{
{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
},
outputs: []outputInfo{
{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
},
},
},
{ {
name: "LoweredWB", name: "LoweredWB",
auxType: auxSym, auxType: auxSym,
@ -30587,20 +30739,6 @@ var opcodeTable = [...]opInfo{
}, },
}, },
}, },
{
name: "RLLGconst",
auxType: auxInt8,
argLen: 1,
asm: s390x.ARLLG,
reg: regInfo{
inputs: []inputInfo{
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
},
outputs: []outputInfo{
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
},
},
},
{ {
name: "RLLconst", name: "RLLconst",
auxType: auxInt8, auxType: auxInt8,
@ -30632,6 +30770,21 @@ var opcodeTable = [...]opInfo{
}, },
}, },
}, },
{
name: "RISBGZ",
auxType: auxS390XRotateParams,
argLen: 1,
clobberFlags: true,
asm: s390x.ARISBGZ,
reg: regInfo{
inputs: []inputInfo{
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
},
outputs: []outputInfo{
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
},
},
},
{ {
name: "NEG", name: "NEG",
argLen: 1, argLen: 1,
@ -35874,6 +36027,54 @@ var opcodeTable = [...]opInfo{
hasSideEffects: true, hasSideEffects: true,
generic: true, generic: true,
}, },
{
name: "AtomicExchange32Variant",
argLen: 3,
hasSideEffects: true,
generic: true,
},
{
name: "AtomicExchange64Variant",
argLen: 3,
hasSideEffects: true,
generic: true,
},
{
name: "AtomicCompareAndSwap32Variant",
argLen: 4,
hasSideEffects: true,
generic: true,
},
{
name: "AtomicCompareAndSwap64Variant",
argLen: 4,
hasSideEffects: true,
generic: true,
},
{
name: "AtomicAnd8Variant",
argLen: 3,
hasSideEffects: true,
generic: true,
},
{
name: "AtomicAnd32Variant",
argLen: 3,
hasSideEffects: true,
generic: true,
},
{
name: "AtomicOr8Variant",
argLen: 3,
hasSideEffects: true,
generic: true,
},
{
name: "AtomicOr32Variant",
argLen: 3,
hasSideEffects: true,
generic: true,
},
{ {
name: "Clobber", name: "Clobber",
auxType: auxSymOff, auxType: auxSymOff,

View file

@ -1082,7 +1082,7 @@ func addLocalInductiveFacts(ft *factsTable, b *Block) {
return nil return nil
} }
pred, child := b.Preds[1].b, b pred, child := b.Preds[1].b, b
for ; pred != nil; pred = uniquePred(pred) { for ; pred != nil; pred, child = uniquePred(pred), pred {
if pred.Kind != BlockIf { if pred.Kind != BlockIf {
continue continue
} }

View file

@ -1427,10 +1427,11 @@ func DecodePPC64RotateMask(sauxint int64) (rotate, mb, me int64, mask uint64) {
return return
} }
// This verifies that the mask occupies the // This verifies that the mask is a set of
// rightmost bits. // consecutive bits including the least
// significant bit.
func isPPC64ValidShiftMask(v int64) bool { func isPPC64ValidShiftMask(v int64) bool {
if ((v + 1) & v) == 0 { if (v != 0) && ((v+1)&v) == 0 {
return true return true
} }
return false return false

View file

@ -426,20 +426,36 @@ func rewriteValueARM64(v *Value) bool {
return true return true
case OpAtomicAnd32: case OpAtomicAnd32:
return rewriteValueARM64_OpAtomicAnd32(v) return rewriteValueARM64_OpAtomicAnd32(v)
case OpAtomicAnd32Variant:
return rewriteValueARM64_OpAtomicAnd32Variant(v)
case OpAtomicAnd8: case OpAtomicAnd8:
return rewriteValueARM64_OpAtomicAnd8(v) return rewriteValueARM64_OpAtomicAnd8(v)
case OpAtomicAnd8Variant:
return rewriteValueARM64_OpAtomicAnd8Variant(v)
case OpAtomicCompareAndSwap32: case OpAtomicCompareAndSwap32:
v.Op = OpARM64LoweredAtomicCas32 v.Op = OpARM64LoweredAtomicCas32
return true return true
case OpAtomicCompareAndSwap32Variant:
v.Op = OpARM64LoweredAtomicCas32Variant
return true
case OpAtomicCompareAndSwap64: case OpAtomicCompareAndSwap64:
v.Op = OpARM64LoweredAtomicCas64 v.Op = OpARM64LoweredAtomicCas64
return true return true
case OpAtomicCompareAndSwap64Variant:
v.Op = OpARM64LoweredAtomicCas64Variant
return true
case OpAtomicExchange32: case OpAtomicExchange32:
v.Op = OpARM64LoweredAtomicExchange32 v.Op = OpARM64LoweredAtomicExchange32
return true return true
case OpAtomicExchange32Variant:
v.Op = OpARM64LoweredAtomicExchange32Variant
return true
case OpAtomicExchange64: case OpAtomicExchange64:
v.Op = OpARM64LoweredAtomicExchange64 v.Op = OpARM64LoweredAtomicExchange64
return true return true
case OpAtomicExchange64Variant:
v.Op = OpARM64LoweredAtomicExchange64Variant
return true
case OpAtomicLoad32: case OpAtomicLoad32:
v.Op = OpARM64LDARW v.Op = OpARM64LDARW
return true return true
@ -454,8 +470,12 @@ func rewriteValueARM64(v *Value) bool {
return true return true
case OpAtomicOr32: case OpAtomicOr32:
return rewriteValueARM64_OpAtomicOr32(v) return rewriteValueARM64_OpAtomicOr32(v)
case OpAtomicOr32Variant:
return rewriteValueARM64_OpAtomicOr32Variant(v)
case OpAtomicOr8: case OpAtomicOr8:
return rewriteValueARM64_OpAtomicOr8(v) return rewriteValueARM64_OpAtomicOr8(v)
case OpAtomicOr8Variant:
return rewriteValueARM64_OpAtomicOr8Variant(v)
case OpAtomicStore32: case OpAtomicStore32:
v.Op = OpARM64STLRW v.Op = OpARM64STLRW
return true return true
@ -21363,6 +21383,25 @@ func rewriteValueARM64_OpAtomicAnd32(v *Value) bool {
return true return true
} }
} }
func rewriteValueARM64_OpAtomicAnd32Variant(v *Value) bool {
v_2 := v.Args[2]
v_1 := v.Args[1]
v_0 := v.Args[0]
b := v.Block
typ := &b.Func.Config.Types
// match: (AtomicAnd32Variant ptr val mem)
// result: (Select1 (LoweredAtomicAnd32Variant ptr val mem))
for {
ptr := v_0
val := v_1
mem := v_2
v.reset(OpSelect1)
v0 := b.NewValue0(v.Pos, OpARM64LoweredAtomicAnd32Variant, types.NewTuple(typ.UInt32, types.TypeMem))
v0.AddArg3(ptr, val, mem)
v.AddArg(v0)
return true
}
}
func rewriteValueARM64_OpAtomicAnd8(v *Value) bool { func rewriteValueARM64_OpAtomicAnd8(v *Value) bool {
v_2 := v.Args[2] v_2 := v.Args[2]
v_1 := v.Args[1] v_1 := v.Args[1]
@ -21382,6 +21421,25 @@ func rewriteValueARM64_OpAtomicAnd8(v *Value) bool {
return true return true
} }
} }
func rewriteValueARM64_OpAtomicAnd8Variant(v *Value) bool {
v_2 := v.Args[2]
v_1 := v.Args[1]
v_0 := v.Args[0]
b := v.Block
typ := &b.Func.Config.Types
// match: (AtomicAnd8Variant ptr val mem)
// result: (Select1 (LoweredAtomicAnd8Variant ptr val mem))
for {
ptr := v_0
val := v_1
mem := v_2
v.reset(OpSelect1)
v0 := b.NewValue0(v.Pos, OpARM64LoweredAtomicAnd8Variant, types.NewTuple(typ.UInt8, types.TypeMem))
v0.AddArg3(ptr, val, mem)
v.AddArg(v0)
return true
}
}
func rewriteValueARM64_OpAtomicOr32(v *Value) bool { func rewriteValueARM64_OpAtomicOr32(v *Value) bool {
v_2 := v.Args[2] v_2 := v.Args[2]
v_1 := v.Args[1] v_1 := v.Args[1]
@ -21401,6 +21459,25 @@ func rewriteValueARM64_OpAtomicOr32(v *Value) bool {
return true return true
} }
} }
func rewriteValueARM64_OpAtomicOr32Variant(v *Value) bool {
v_2 := v.Args[2]
v_1 := v.Args[1]
v_0 := v.Args[0]
b := v.Block
typ := &b.Func.Config.Types
// match: (AtomicOr32Variant ptr val mem)
// result: (Select1 (LoweredAtomicOr32Variant ptr val mem))
for {
ptr := v_0
val := v_1
mem := v_2
v.reset(OpSelect1)
v0 := b.NewValue0(v.Pos, OpARM64LoweredAtomicOr32Variant, types.NewTuple(typ.UInt32, types.TypeMem))
v0.AddArg3(ptr, val, mem)
v.AddArg(v0)
return true
}
}
func rewriteValueARM64_OpAtomicOr8(v *Value) bool { func rewriteValueARM64_OpAtomicOr8(v *Value) bool {
v_2 := v.Args[2] v_2 := v.Args[2]
v_1 := v.Args[1] v_1 := v.Args[1]
@ -21420,6 +21497,25 @@ func rewriteValueARM64_OpAtomicOr8(v *Value) bool {
return true return true
} }
} }
func rewriteValueARM64_OpAtomicOr8Variant(v *Value) bool {
v_2 := v.Args[2]
v_1 := v.Args[1]
v_0 := v.Args[0]
b := v.Block
typ := &b.Func.Config.Types
// match: (AtomicOr8Variant ptr val mem)
// result: (Select1 (LoweredAtomicOr8Variant ptr val mem))
for {
ptr := v_0
val := v_1
mem := v_2
v.reset(OpSelect1)
v0 := b.NewValue0(v.Pos, OpARM64LoweredAtomicOr8Variant, types.NewTuple(typ.UInt8, types.TypeMem))
v0.AddArg3(ptr, val, mem)
v.AddArg(v0)
return true
}
}
func rewriteValueARM64_OpAvg64u(v *Value) bool { func rewriteValueARM64_OpAvg64u(v *Value) bool {
v_1 := v.Args[1] v_1 := v.Args[1]
v_0 := v.Args[0] v_0 := v.Args[0]

View file

@ -4431,7 +4431,7 @@ func rewriteValueMIPS_OpMIPSSLL(v *Value) bool {
v_1 := v.Args[1] v_1 := v.Args[1]
v_0 := v.Args[0] v_0 := v.Args[0]
// match: (SLL x (MOVWconst [c])) // match: (SLL x (MOVWconst [c]))
// result: (SLLconst x [c]) // result: (SLLconst x [c&31])
for { for {
x := v_0 x := v_0
if v_1.Op != OpMIPSMOVWconst { if v_1.Op != OpMIPSMOVWconst {
@ -4439,7 +4439,7 @@ func rewriteValueMIPS_OpMIPSSLL(v *Value) bool {
} }
c := auxIntToInt32(v_1.AuxInt) c := auxIntToInt32(v_1.AuxInt)
v.reset(OpMIPSSLLconst) v.reset(OpMIPSSLLconst)
v.AuxInt = int32ToAuxInt(c) v.AuxInt = int32ToAuxInt(c & 31)
v.AddArg(x) v.AddArg(x)
return true return true
} }
@ -4465,24 +4465,7 @@ func rewriteValueMIPS_OpMIPSSRA(v *Value) bool {
v_1 := v.Args[1] v_1 := v.Args[1]
v_0 := v.Args[0] v_0 := v.Args[0]
// match: (SRA x (MOVWconst [c])) // match: (SRA x (MOVWconst [c]))
// cond: c >= 32 // result: (SRAconst x [c&31])
// result: (SRAconst x [31])
for {
x := v_0
if v_1.Op != OpMIPSMOVWconst {
break
}
c := auxIntToInt32(v_1.AuxInt)
if !(c >= 32) {
break
}
v.reset(OpMIPSSRAconst)
v.AuxInt = int32ToAuxInt(31)
v.AddArg(x)
return true
}
// match: (SRA x (MOVWconst [c]))
// result: (SRAconst x [c])
for { for {
x := v_0 x := v_0
if v_1.Op != OpMIPSMOVWconst { if v_1.Op != OpMIPSMOVWconst {
@ -4490,7 +4473,7 @@ func rewriteValueMIPS_OpMIPSSRA(v *Value) bool {
} }
c := auxIntToInt32(v_1.AuxInt) c := auxIntToInt32(v_1.AuxInt)
v.reset(OpMIPSSRAconst) v.reset(OpMIPSSRAconst)
v.AuxInt = int32ToAuxInt(c) v.AuxInt = int32ToAuxInt(c & 31)
v.AddArg(x) v.AddArg(x)
return true return true
} }
@ -4516,7 +4499,7 @@ func rewriteValueMIPS_OpMIPSSRL(v *Value) bool {
v_1 := v.Args[1] v_1 := v.Args[1]
v_0 := v.Args[0] v_0 := v.Args[0]
// match: (SRL x (MOVWconst [c])) // match: (SRL x (MOVWconst [c]))
// result: (SRLconst x [c]) // result: (SRLconst x [c&31])
for { for {
x := v_0 x := v_0
if v_1.Op != OpMIPSMOVWconst { if v_1.Op != OpMIPSMOVWconst {
@ -4524,7 +4507,7 @@ func rewriteValueMIPS_OpMIPSSRL(v *Value) bool {
} }
c := auxIntToInt32(v_1.AuxInt) c := auxIntToInt32(v_1.AuxInt)
v.reset(OpMIPSSRLconst) v.reset(OpMIPSSRLconst)
v.AuxInt = int32ToAuxInt(c) v.AuxInt = int32ToAuxInt(c & 31)
v.AddArg(x) v.AddArg(x)
return true return true
} }
@ -5714,7 +5697,7 @@ func rewriteValueMIPS_OpRsh16x16(v *Value) bool {
b := v.Block b := v.Block
typ := &b.Func.Config.Types typ := &b.Func.Config.Types
// match: (Rsh16x16 x y) // match: (Rsh16x16 x y)
// result: (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> (ZeroExt16to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt16to32 y)))) // result: (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> (ZeroExt16to32 y) (MOVWconst [31]) (SGTUconst [32] (ZeroExt16to32 y))))
for { for {
x := v_0 x := v_0
y := v_1 y := v_1
@ -5725,7 +5708,7 @@ func rewriteValueMIPS_OpRsh16x16(v *Value) bool {
v2 := b.NewValue0(v.Pos, OpZeroExt16to32, typ.UInt32) v2 := b.NewValue0(v.Pos, OpZeroExt16to32, typ.UInt32)
v2.AddArg(y) v2.AddArg(y)
v3 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32) v3 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32)
v3.AuxInt = int32ToAuxInt(-1) v3.AuxInt = int32ToAuxInt(31)
v4 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool) v4 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool)
v4.AuxInt = int32ToAuxInt(32) v4.AuxInt = int32ToAuxInt(32)
v4.AddArg(v2) v4.AddArg(v2)
@ -5740,7 +5723,7 @@ func rewriteValueMIPS_OpRsh16x32(v *Value) bool {
b := v.Block b := v.Block
typ := &b.Func.Config.Types typ := &b.Func.Config.Types
// match: (Rsh16x32 x y) // match: (Rsh16x32 x y)
// result: (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> y (MOVWconst [-1]) (SGTUconst [32] y))) // result: (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> y (MOVWconst [31]) (SGTUconst [32] y)))
for { for {
x := v_0 x := v_0
y := v_1 y := v_1
@ -5749,7 +5732,7 @@ func rewriteValueMIPS_OpRsh16x32(v *Value) bool {
v0.AddArg(x) v0.AddArg(x)
v1 := b.NewValue0(v.Pos, OpMIPSCMOVZ, typ.UInt32) v1 := b.NewValue0(v.Pos, OpMIPSCMOVZ, typ.UInt32)
v2 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32) v2 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32)
v2.AuxInt = int32ToAuxInt(-1) v2.AuxInt = int32ToAuxInt(31)
v3 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool) v3 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool)
v3.AuxInt = int32ToAuxInt(32) v3.AuxInt = int32ToAuxInt(32)
v3.AddArg(y) v3.AddArg(y)
@ -5811,7 +5794,7 @@ func rewriteValueMIPS_OpRsh16x8(v *Value) bool {
b := v.Block b := v.Block
typ := &b.Func.Config.Types typ := &b.Func.Config.Types
// match: (Rsh16x8 x y) // match: (Rsh16x8 x y)
// result: (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> (ZeroExt8to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt8to32 y)))) // result: (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> (ZeroExt8to32 y) (MOVWconst [31]) (SGTUconst [32] (ZeroExt8to32 y))))
for { for {
x := v_0 x := v_0
y := v_1 y := v_1
@ -5822,7 +5805,7 @@ func rewriteValueMIPS_OpRsh16x8(v *Value) bool {
v2 := b.NewValue0(v.Pos, OpZeroExt8to32, typ.UInt32) v2 := b.NewValue0(v.Pos, OpZeroExt8to32, typ.UInt32)
v2.AddArg(y) v2.AddArg(y)
v3 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32) v3 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32)
v3.AuxInt = int32ToAuxInt(-1) v3.AuxInt = int32ToAuxInt(31)
v4 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool) v4 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool)
v4.AuxInt = int32ToAuxInt(32) v4.AuxInt = int32ToAuxInt(32)
v4.AddArg(v2) v4.AddArg(v2)
@ -5947,7 +5930,7 @@ func rewriteValueMIPS_OpRsh32x16(v *Value) bool {
b := v.Block b := v.Block
typ := &b.Func.Config.Types typ := &b.Func.Config.Types
// match: (Rsh32x16 x y) // match: (Rsh32x16 x y)
// result: (SRA x ( CMOVZ <typ.UInt32> (ZeroExt16to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt16to32 y)))) // result: (SRA x ( CMOVZ <typ.UInt32> (ZeroExt16to32 y) (MOVWconst [31]) (SGTUconst [32] (ZeroExt16to32 y))))
for { for {
x := v_0 x := v_0
y := v_1 y := v_1
@ -5956,7 +5939,7 @@ func rewriteValueMIPS_OpRsh32x16(v *Value) bool {
v1 := b.NewValue0(v.Pos, OpZeroExt16to32, typ.UInt32) v1 := b.NewValue0(v.Pos, OpZeroExt16to32, typ.UInt32)
v1.AddArg(y) v1.AddArg(y)
v2 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32) v2 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32)
v2.AuxInt = int32ToAuxInt(-1) v2.AuxInt = int32ToAuxInt(31)
v3 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool) v3 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool)
v3.AuxInt = int32ToAuxInt(32) v3.AuxInt = int32ToAuxInt(32)
v3.AddArg(v1) v3.AddArg(v1)
@ -5971,14 +5954,14 @@ func rewriteValueMIPS_OpRsh32x32(v *Value) bool {
b := v.Block b := v.Block
typ := &b.Func.Config.Types typ := &b.Func.Config.Types
// match: (Rsh32x32 x y) // match: (Rsh32x32 x y)
// result: (SRA x ( CMOVZ <typ.UInt32> y (MOVWconst [-1]) (SGTUconst [32] y))) // result: (SRA x ( CMOVZ <typ.UInt32> y (MOVWconst [31]) (SGTUconst [32] y)))
for { for {
x := v_0 x := v_0
y := v_1 y := v_1
v.reset(OpMIPSSRA) v.reset(OpMIPSSRA)
v0 := b.NewValue0(v.Pos, OpMIPSCMOVZ, typ.UInt32) v0 := b.NewValue0(v.Pos, OpMIPSCMOVZ, typ.UInt32)
v1 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32) v1 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32)
v1.AuxInt = int32ToAuxInt(-1) v1.AuxInt = int32ToAuxInt(31)
v2 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool) v2 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool)
v2.AuxInt = int32ToAuxInt(32) v2.AuxInt = int32ToAuxInt(32)
v2.AddArg(y) v2.AddArg(y)
@ -6032,7 +6015,7 @@ func rewriteValueMIPS_OpRsh32x8(v *Value) bool {
b := v.Block b := v.Block
typ := &b.Func.Config.Types typ := &b.Func.Config.Types
// match: (Rsh32x8 x y) // match: (Rsh32x8 x y)
// result: (SRA x ( CMOVZ <typ.UInt32> (ZeroExt8to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt8to32 y)))) // result: (SRA x ( CMOVZ <typ.UInt32> (ZeroExt8to32 y) (MOVWconst [31]) (SGTUconst [32] (ZeroExt8to32 y))))
for { for {
x := v_0 x := v_0
y := v_1 y := v_1
@ -6041,7 +6024,7 @@ func rewriteValueMIPS_OpRsh32x8(v *Value) bool {
v1 := b.NewValue0(v.Pos, OpZeroExt8to32, typ.UInt32) v1 := b.NewValue0(v.Pos, OpZeroExt8to32, typ.UInt32)
v1.AddArg(y) v1.AddArg(y)
v2 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32) v2 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32)
v2.AuxInt = int32ToAuxInt(-1) v2.AuxInt = int32ToAuxInt(31)
v3 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool) v3 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool)
v3.AuxInt = int32ToAuxInt(32) v3.AuxInt = int32ToAuxInt(32)
v3.AddArg(v1) v3.AddArg(v1)
@ -6177,7 +6160,7 @@ func rewriteValueMIPS_OpRsh8x16(v *Value) bool {
b := v.Block b := v.Block
typ := &b.Func.Config.Types typ := &b.Func.Config.Types
// match: (Rsh8x16 x y) // match: (Rsh8x16 x y)
// result: (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> (ZeroExt16to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt16to32 y)))) // result: (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> (ZeroExt16to32 y) (MOVWconst [31]) (SGTUconst [32] (ZeroExt16to32 y))))
for { for {
x := v_0 x := v_0
y := v_1 y := v_1
@ -6188,7 +6171,7 @@ func rewriteValueMIPS_OpRsh8x16(v *Value) bool {
v2 := b.NewValue0(v.Pos, OpZeroExt16to32, typ.UInt32) v2 := b.NewValue0(v.Pos, OpZeroExt16to32, typ.UInt32)
v2.AddArg(y) v2.AddArg(y)
v3 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32) v3 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32)
v3.AuxInt = int32ToAuxInt(-1) v3.AuxInt = int32ToAuxInt(31)
v4 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool) v4 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool)
v4.AuxInt = int32ToAuxInt(32) v4.AuxInt = int32ToAuxInt(32)
v4.AddArg(v2) v4.AddArg(v2)
@ -6203,7 +6186,7 @@ func rewriteValueMIPS_OpRsh8x32(v *Value) bool {
b := v.Block b := v.Block
typ := &b.Func.Config.Types typ := &b.Func.Config.Types
// match: (Rsh8x32 x y) // match: (Rsh8x32 x y)
// result: (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> y (MOVWconst [-1]) (SGTUconst [32] y))) // result: (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> y (MOVWconst [31]) (SGTUconst [32] y)))
for { for {
x := v_0 x := v_0
y := v_1 y := v_1
@ -6212,7 +6195,7 @@ func rewriteValueMIPS_OpRsh8x32(v *Value) bool {
v0.AddArg(x) v0.AddArg(x)
v1 := b.NewValue0(v.Pos, OpMIPSCMOVZ, typ.UInt32) v1 := b.NewValue0(v.Pos, OpMIPSCMOVZ, typ.UInt32)
v2 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32) v2 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32)
v2.AuxInt = int32ToAuxInt(-1) v2.AuxInt = int32ToAuxInt(31)
v3 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool) v3 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool)
v3.AuxInt = int32ToAuxInt(32) v3.AuxInt = int32ToAuxInt(32)
v3.AddArg(y) v3.AddArg(y)
@ -6274,7 +6257,7 @@ func rewriteValueMIPS_OpRsh8x8(v *Value) bool {
b := v.Block b := v.Block
typ := &b.Func.Config.Types typ := &b.Func.Config.Types
// match: (Rsh8x8 x y) // match: (Rsh8x8 x y)
// result: (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> (ZeroExt8to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt8to32 y)))) // result: (SRA (SignExt16to32 x) ( CMOVZ <typ.UInt32> (ZeroExt8to32 y) (MOVWconst [31]) (SGTUconst [32] (ZeroExt8to32 y))))
for { for {
x := v_0 x := v_0
y := v_1 y := v_1
@ -6285,7 +6268,7 @@ func rewriteValueMIPS_OpRsh8x8(v *Value) bool {
v2 := b.NewValue0(v.Pos, OpZeroExt8to32, typ.UInt32) v2 := b.NewValue0(v.Pos, OpZeroExt8to32, typ.UInt32)
v2.AddArg(y) v2.AddArg(y)
v3 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32) v3 := b.NewValue0(v.Pos, OpMIPSMOVWconst, typ.UInt32)
v3.AuxInt = int32ToAuxInt(-1) v3.AuxInt = int32ToAuxInt(31)
v4 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool) v4 := b.NewValue0(v.Pos, OpMIPSSGTUconst, typ.Bool)
v4.AuxInt = int32ToAuxInt(32) v4.AuxInt = int32ToAuxInt(32)
v4.AddArg(v2) v4.AddArg(v2)

File diff suppressed because it is too large Load diff

View file

@ -1580,8 +1580,7 @@ var cgoEnabled = map[string]bool{
// List of platforms which are supported but not complete yet. These get // List of platforms which are supported but not complete yet. These get
// filtered out of cgoEnabled for 'dist list'. See golang.org/issue/28944 // filtered out of cgoEnabled for 'dist list'. See golang.org/issue/28944
var incomplete = map[string]bool{ var incomplete = map[string]bool{
"linux/sparc64": true, "linux/sparc64": true,
"openbsd/mips64": true,
} }
func needCC() bool { func needCC() bool {

View file

@ -1021,7 +1021,7 @@ func (t *tester) supportedBuildmode(mode string) bool {
case "pie": case "pie":
switch pair { switch pair {
case "aix/ppc64", case "aix/ppc64",
"linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x", "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x",
"android-amd64", "android-arm", "android-arm64", "android-386": "android-amd64", "android-arm", "android-arm64", "android-386":
return true return true
case "darwin-amd64", "darwin-arm64": case "darwin-amd64", "darwin-arm64":
@ -1095,7 +1095,6 @@ func (t *tester) cgoTest(dt *distTest) error {
pair := gohostos + "-" + goarch pair := gohostos + "-" + goarch
switch pair { switch pair {
case "darwin-amd64", "darwin-arm64", case "darwin-amd64", "darwin-arm64",
"openbsd-386", "openbsd-amd64",
"windows-386", "windows-amd64": "windows-386", "windows-amd64":
// test linkmode=external, but __thread not supported, so skip testtls. // test linkmode=external, but __thread not supported, so skip testtls.
if !t.extLink() { if !t.extLink() {
@ -1118,7 +1117,8 @@ func (t *tester) cgoTest(dt *distTest) error {
"dragonfly-amd64", "dragonfly-amd64",
"freebsd-386", "freebsd-amd64", "freebsd-arm", "freebsd-386", "freebsd-amd64", "freebsd-arm",
"linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x", "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x",
"netbsd-386", "netbsd-amd64": "netbsd-386", "netbsd-amd64",
"openbsd-386", "openbsd-amd64", "openbsd-arm", "openbsd-arm64", "openbsd-mips64":
cmd := t.addCmd(dt, "misc/cgo/test", t.goTest()) cmd := t.addCmd(dt, "misc/cgo/test", t.goTest())
cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=external") cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=external")
@ -1444,7 +1444,6 @@ func (t *tester) testDirTest(dt *distTest, shard, shards int) error {
// cgoPackages is the standard packages that use cgo. // cgoPackages is the standard packages that use cgo.
var cgoPackages = []string{ var cgoPackages = []string{
"crypto/x509",
"net", "net",
"os/user", "os/user",
} }

View file

@ -6,8 +6,8 @@ require (
github.com/google/pprof v0.0.0-20201007051231-1066cbb265c7 github.com/google/pprof v0.0.0-20201007051231-1066cbb265c7
github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340 // indirect github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340 // indirect
golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449 golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 // indirect golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65 // indirect
golang.org/x/tools v0.0.0-20201014170642-d1624618ad65 golang.org/x/tools v0.0.0-20201110201400-7099162a900a
) )

View file

@ -12,26 +12,28 @@ golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff/go.mod h1:flIaEI6LNU6xOCD5P
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449 h1:xUIPaMhvROX9dhPvRCenIJtU78+lbEenGbgqB5hfHCQ= golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449 h1:xUIPaMhvROX9dhPvRCenIJtU78+lbEenGbgqB5hfHCQ=
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 h1:a/mKvvZr9Jcc8oKfcmgzyp7OwF73JPWsQLvH1z2Kxck= golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65 h1:Qo9oJ566/Sq7N4hrGftVXs8GI2CXBCuOd4S2wHE/e0M=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65/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/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
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-20201014170642-d1624618ad65 h1:q80OtYaeeySe8Kqg0vjXehHwj5fUTqe3xOvnbi5w3Gg= golang.org/x/tools v0.0.0-20201110201400-7099162a900a h1:5E6TPwSBG74zT8xSrVc8W59K4ch4NFobVTnh2BYzHyU=
golang.org/x/tools v0.0.0-20201014170642-d1624618ad65/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201110201400-7099162a900a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
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-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=

View file

@ -49,10 +49,11 @@
// modules modules, module versions, and more // modules modules, module versions, and more
// module-get module-aware go get // module-get module-aware go get
// module-auth module authentication using go.sum // module-auth module authentication using go.sum
// module-private module configuration for non-public modules
// packages package lists and patterns // packages package lists and patterns
// private configuration for downloading non-public code
// testflag testing flags // testflag testing flags
// testfunc testing functions // testfunc testing functions
// vcs controlling version control with GOVCS
// //
// Use "go help <topic>" for more information about that topic. // Use "go help <topic>" for more information about that topic.
// //
@ -71,7 +72,7 @@
// //
// Usage: // Usage:
// //
// go build [-o output] [-i] [build flags] [packages] // go build [-o output] [build flags] [packages]
// //
// Build compiles the packages named by the import paths, // Build compiles the packages named by the import paths,
// along with their dependencies, but it does not install the results. // along with their dependencies, but it does not install the results.
@ -98,6 +99,7 @@
// will be written to that directory. // will be written to that directory.
// //
// The -i flag installs the packages that are dependencies of the target. // The -i flag installs the packages that are dependencies of the target.
// The -i flag is deprecated. Compiled packages are cached automatically.
// //
// The build flags are shared by the build, clean, get, install, list, run, // The build flags are shared by the build, clean, get, install, list, run,
// and test commands: // and test commands:
@ -617,15 +619,15 @@
// dependency should be removed entirely, downgrading or removing modules // dependency should be removed entirely, downgrading or removing modules
// depending on it as needed. // depending on it as needed.
// //
// The version suffix @latest explicitly requests the latest minor release of the // The version suffix @latest explicitly requests the latest minor release of
// module named by the given path. The suffix @upgrade is like @latest but // the module named by the given path. The suffix @upgrade is like @latest but
// will not downgrade a module if it is already required at a revision or // will not downgrade a module if it is already required at a revision or
// pre-release version newer than the latest released version. The suffix // pre-release version newer than the latest released version. The suffix
// @patch requests the latest patch release: the latest released version // @patch requests the latest patch release: the latest released version
// with the same major and minor version numbers as the currently required // with the same major and minor version numbers as the currently required
// version. Like @upgrade, @patch will not downgrade a module already required // version. Like @upgrade, @patch will not downgrade a module already required
// at a newer version. If the path is not already required, @upgrade and @patch // at a newer version. If the path is not already required, @upgrade is
// are equivalent to @latest. // equivalent to @latest, and @patch is disallowed.
// //
// Although get defaults to using the latest version of the module containing // Although get defaults to using the latest version of the module containing
// a named package, it does not use the latest version of that module's // a named package, it does not use the latest version of that module's
@ -672,6 +674,17 @@
// The second step is to download (if needed), build, and install // The second step is to download (if needed), build, and install
// the named packages. // the named packages.
// //
// The -d flag instructs get to skip this step, downloading source code
// needed to build the named packages and their dependencies, but not
// building or installing.
//
// Building and installing packages with get is deprecated. In a future release,
// the -d flag will be enabled by default, and 'go get' will be only be used to
// adjust dependencies of the current module. To install a package using
// dependencies from the current module, use 'go install'. To install a package
// ignoring the current module, use 'go install' with an @version suffix like
// "@latest" after each argument.
//
// If an argument names a module but not a package (because there is no // If an argument names a module but not a package (because there is no
// Go source code in the module's root directory), then the install step // Go source code in the module's root directory), then the install step
// is skipped for that argument, instead of causing a build failure. // is skipped for that argument, instead of causing a build failure.
@ -683,10 +696,6 @@
// adds the latest golang.org/x/perf and then installs the commands in that // adds the latest golang.org/x/perf and then installs the commands in that
// latest version. // latest version.
// //
// The -d flag instructs get to download the source code needed to build
// the named packages, including downloading necessary dependencies,
// but not to build and install them.
//
// With no package arguments, 'go get' applies to Go package in the // With no package arguments, 'go get' applies to Go package in the
// current directory, if any. In particular, 'go get -u' and // current directory, if any. In particular, 'go get -u' and
// 'go get -u=patch' update all the dependencies of that package. // 'go get -u=patch' update all the dependencies of that package.
@ -709,7 +718,7 @@
// //
// Usage: // Usage:
// //
// go install [-i] [build flags] [packages] // go install [build flags] [packages]
// //
// Install compiles and installs the packages named by the import paths. // Install compiles and installs the packages named by the import paths.
// //
@ -750,6 +759,7 @@
// other packages are built and cached but not installed. // other packages are built and cached but not installed.
// //
// The -i flag installs the dependencies of the named packages as well. // The -i flag installs the dependencies of the named packages as well.
// The -i flag is deprecated. Compiled packages are cached automatically.
// //
// For more about the build flags, see 'go help build'. // For more about the build flags, see 'go help build'.
// For more about specifying packages, see 'go help packages'. // For more about specifying packages, see 'go help packages'.
@ -1145,7 +1155,7 @@
// //
// The -retract=version and -dropretract=version flags add and drop a // The -retract=version and -dropretract=version flags add and drop a
// retraction on the given version. The version may be a single version // retraction on the given version. The version may be a single version
// like "v1.2.3" or a closed interval like "[v1.1.0-v1.1.9]". Note that // like "v1.2.3" or a closed interval like "[v1.1.0,v1.1.9]". Note that
// -retract=version is a no-op if that retraction already exists. // -retract=version is a no-op if that retraction already exists.
// //
// The -require, -droprequire, -exclude, -dropexclude, -replace, // The -require, -droprequire, -exclude, -dropexclude, -replace,
@ -1220,12 +1230,17 @@
// //
// go mod init [module] // go mod init [module]
// //
// Init initializes and writes a new go.mod to the current directory, // Init initializes and writes a new go.mod file in the current directory, in
// in effect creating a new module rooted at the current directory. // effect creating a new module rooted at the current directory. The go.mod file
// The file go.mod must not already exist. // must not already exist.
// If possible, init will guess the module path from import comments //
// (see 'go help importpath') or from version control configuration. // Init accepts one optional argument, the module path for the new module. If the
// To override this guess, supply the module path as an argument. // module path argument is omitted, init will attempt to infer the module path
// using import comments in .go files, vendoring tool configuration files (like
// Gopkg.lock), and the current directory (if in GOPATH).
//
// If a configuration file for a vendoring tool is present, init will attempt to
// import module requirements from it.
// //
// //
// Add missing and remove unused modules // Add missing and remove unused modules
@ -1451,6 +1466,7 @@
// -i // -i
// Install packages that are dependencies of the test. // Install packages that are dependencies of the test.
// Do not run the test. // Do not run the test.
// The -i flag is deprecated. Compiled packages are cached automatically.
// //
// -json // -json
// Convert test output to JSON suitable for automated processing. // Convert test output to JSON suitable for automated processing.
@ -1799,7 +1815,7 @@
// Comma-separated list of glob patterns (in the syntax of Go's path.Match) // Comma-separated list of glob patterns (in the syntax of Go's path.Match)
// of module path prefixes that should always be fetched directly // of module path prefixes that should always be fetched directly
// or that should not be compared against the checksum database. // or that should not be compared against the checksum database.
// See 'go help module-private'. // See 'go help private'.
// GOROOT // GOROOT
// The root of the go tree. // The root of the go tree.
// GOSUMDB // GOSUMDB
@ -1905,6 +1921,8 @@
// If module-aware mode is disabled, GOMOD will be the empty string. // If module-aware mode is disabled, GOMOD will be the empty string.
// GOTOOLDIR // GOTOOLDIR
// The directory where the go tools (compile, cover, doc, etc...) are installed. // The directory where the go tools (compile, cover, doc, etc...) are installed.
// GOVERSION
// The version of the installed Go tree, as reported by runtime.Version.
// //
// //
// File types // File types
@ -1976,7 +1994,7 @@
// like in Go imports: // like in Go imports:
// //
// require ( // require (
// new/thing v2.3.4 // new/thing/v2 v2.3.4
// old/thing v1.2.3 // old/thing v1.2.3
// ) // )
// //
@ -2869,7 +2887,7 @@
// followed by a pipe character, indicating it is safe to fall back on any error. // followed by a pipe character, indicating it is safe to fall back on any error.
// //
// The GOPRIVATE and GONOPROXY environment variables allow bypassing // The GOPRIVATE and GONOPROXY environment variables allow bypassing
// the proxy for selected modules. See 'go help module-private' for details. // the proxy for selected modules. See 'go help private' for details.
// //
// No matter the source of the modules, the go command checks downloads against // No matter the source of the modules, the go command checks downloads against
// known checksums, to detect unexpected changes in the content of any specific // known checksums, to detect unexpected changes in the content of any specific
@ -2989,52 +3007,7 @@
// accepted, at the cost of giving up the security guarantee of verified repeatable // accepted, at the cost of giving up the security guarantee of verified repeatable
// downloads for all modules. A better way to bypass the checksum database // downloads for all modules. A better way to bypass the checksum database
// for specific modules is to use the GOPRIVATE or GONOSUMDB environment // for specific modules is to use the GOPRIVATE or GONOSUMDB environment
// variables. See 'go help module-private' for details. // variables. See 'go help private' for details.
//
// The 'go env -w' command (see 'go help env') can be used to set these variables
// for future go command invocations.
//
//
// Module configuration for non-public modules
//
// The go command defaults to downloading modules from the public Go module
// mirror at proxy.golang.org. It also defaults to validating downloaded modules,
// regardless of source, against the public Go checksum database at sum.golang.org.
// These defaults work well for publicly available source code.
//
// The GOPRIVATE environment variable controls which modules the go command
// considers to be private (not available publicly) and should therefore not use the
// proxy or checksum database. The variable is a comma-separated list of
// glob patterns (in the syntax of Go's path.Match) of module path prefixes.
// For example,
//
// GOPRIVATE=*.corp.example.com,rsc.io/private
//
// causes the go command to treat as private any module with a path prefix
// matching either pattern, including git.corp.example.com/xyzzy, rsc.io/private,
// and rsc.io/private/quux.
//
// The GOPRIVATE environment variable may be used by other tools as well to
// identify non-public modules. For example, an editor could use GOPRIVATE
// to decide whether to hyperlink a package import to a godoc.org page.
//
// For fine-grained control over module download and validation, the GONOPROXY
// and GONOSUMDB environment variables accept the same kind of glob list
// and override GOPRIVATE for the specific decision of whether to use the proxy
// and checksum database, respectively.
//
// For example, if a company ran a module proxy serving private modules,
// users would configure go using:
//
// GOPRIVATE=*.corp.example.com
// GOPROXY=proxy.example.com
// GONOPROXY=none
//
// This would tell the go command and other tools that modules beginning with
// a corp.example.com subdomain are private but that the company proxy should
// be used for downloading both public and private modules, because
// GONOPROXY has been set to a pattern that won't match any modules,
// overriding GOPRIVATE.
// //
// The 'go env -w' command (see 'go help env') can be used to set these variables // The 'go env -w' command (see 'go help env') can be used to set these variables
// for future go command invocations. // for future go command invocations.
@ -3124,6 +3097,56 @@
// by the go tool, as are directories named "testdata". // by the go tool, as are directories named "testdata".
// //
// //
// Configuration for downloading non-public code
//
// The go command defaults to downloading modules from the public Go module
// mirror at proxy.golang.org. It also defaults to validating downloaded modules,
// regardless of source, against the public Go checksum database at sum.golang.org.
// These defaults work well for publicly available source code.
//
// The GOPRIVATE environment variable controls which modules the go command
// considers to be private (not available publicly) and should therefore not use the
// proxy or checksum database. The variable is a comma-separated list of
// glob patterns (in the syntax of Go's path.Match) of module path prefixes.
// For example,
//
// GOPRIVATE=*.corp.example.com,rsc.io/private
//
// causes the go command to treat as private any module with a path prefix
// matching either pattern, including git.corp.example.com/xyzzy, rsc.io/private,
// and rsc.io/private/quux.
//
// The GOPRIVATE environment variable may be used by other tools as well to
// identify non-public modules. For example, an editor could use GOPRIVATE
// to decide whether to hyperlink a package import to a godoc.org page.
//
// For fine-grained control over module download and validation, the GONOPROXY
// and GONOSUMDB environment variables accept the same kind of glob list
// and override GOPRIVATE for the specific decision of whether to use the proxy
// and checksum database, respectively.
//
// For example, if a company ran a module proxy serving private modules,
// users would configure go using:
//
// GOPRIVATE=*.corp.example.com
// GOPROXY=proxy.example.com
// GONOPROXY=none
//
// This would tell the go command and other tools that modules beginning with
// a corp.example.com subdomain are private but that the company proxy should
// be used for downloading both public and private modules, because
// GONOPROXY has been set to a pattern that won't match any modules,
// overriding GOPRIVATE.
//
// The GOPRIVATE variable is also used to define the "public" and "private"
// patterns for the GOVCS variable; see 'go help vcs'. For that usage,
// GOPRIVATE applies even in GOPATH mode. In that case, it matches import paths
// instead of module paths.
//
// The 'go env -w' command (see 'go help env') can be used to set these variables
// for future go command invocations.
//
//
// Testing flags // Testing flags
// //
// The 'go test' command takes both flags that apply to 'go test' itself // The 'go test' command takes both flags that apply to 'go test' itself
@ -3416,4 +3439,77 @@
// See the documentation of the testing package for more information. // See the documentation of the testing package for more information.
// //
// //
// Controlling version control with GOVCS
//
// The 'go get' command can run version control commands like git
// to download imported code. This functionality is critical to the decentralized
// Go package ecosystem, in which code can be imported from any server,
// but it is also a potential security problem, if a malicious server finds a
// way to cause the invoked version control command to run unintended code.
//
// To balance the functionality and security concerns, the 'go get' command
// by default will only use git and hg to download code from public servers.
// But it will use any known version control system (bzr, fossil, git, hg, svn)
// to download code from private servers, defined as those hosting packages
// matching the GOPRIVATE variable (see 'go help private'). The rationale behind
// allowing only Git and Mercurial is that these two systems have had the most
// attention to issues of being run as clients of untrusted servers. In contrast,
// Bazaar, Fossil, and Subversion have primarily been used in trusted,
// authenticated environments and are not as well scrutinized as attack surfaces.
//
// The version control command restrictions only apply when using direct version
// control access to download code. When downloading modules from a proxy,
// 'go get' uses the proxy protocol instead, which is always permitted.
// By default, the 'go get' command uses the Go module mirror (proxy.golang.org)
// for public packages and only falls back to version control for private
// packages or when the mirror refuses to serve a public package (typically for
// legal reasons). Therefore, clients can still access public code served from
// Bazaar, Fossil, or Subversion repositories by default, because those downloads
// use the Go module mirror, which takes on the security risk of running the
// version control commands, using a custom sandbox.
//
// The GOVCS variable can be used to change the allowed version control systems
// for specific packages (identified by a module or import path).
// The GOVCS variable applies both when using modules and when using GOPATH.
// When using modules, the patterns match against the module path.
// When using GOPATH, the patterns match against the import path
// corresponding to the root of the version control repository.
//
// The general form of the GOVCS setting is a comma-separated list of
// pattern:vcslist rules. The pattern is a glob pattern that must match
// one or more leading elements of the module or import path. The vcslist
// is a pipe-separated list of allowed version control commands, or "all"
// to allow use of any known command, or "off" to allow nothing.
// The earliest matching pattern in the list applies, even if later patterns
// might also match.
//
// For example, consider:
//
// GOVCS=github.com:git,evil.com:off,*:git|hg
//
// With this setting, code with an module or import path beginning with
// github.com/ can only use git; paths on evil.com cannot use any version
// control command, and all other paths (* matches everything) can use
// only git or hg.
//
// The special patterns "public" and "private" match public and private
// module or import paths. A path is private if it matches the GOPRIVATE
// variable; otherwise it is public.
//
// If no rules in the GOVCS variable match a particular module or import path,
// the 'go get' command applies its default rule, which can now be summarized
// in GOVCS notation as 'public:git|hg,private:all'.
//
// To allow unfettered use of any version control system for any package, use:
//
// GOVCS=*:all
//
// To disable all use of version control, use:
//
// GOVCS=*:off
//
// The 'go env -w' command (see 'go help env') can be used to set the GOVCS
// variable for future go command invocations.
//
//
package main package main

View file

@ -35,6 +35,14 @@ import (
"cmd/internal/sys" "cmd/internal/sys"
) )
func init() {
// GOVCS defaults to public:git|hg,private:all,
// which breaks many tests here - they can't use non-git, non-hg VCS at all!
// Change to fully permissive.
// The tests of the GOVCS setting itself are in ../../testdata/script/govcs.txt.
os.Setenv("GOVCS", "*:all")
}
var ( var (
canRace = false // whether we can run the race detector canRace = false // whether we can run the race detector
canCgo = false // whether we can use cgo canCgo = false // whether we can use cgo
@ -1878,6 +1886,18 @@ func TestGoEnv(t *testing.T) {
tg.grepStdout("gcc", "CC not found") tg.grepStdout("gcc", "CC not found")
tg.run("env", "GOGCCFLAGS") tg.run("env", "GOGCCFLAGS")
tg.grepStdout("-ffaster", "CC arguments not found") tg.grepStdout("-ffaster", "CC arguments not found")
tg.run("env", "GOVERSION")
envVersion := strings.TrimSpace(tg.stdout.String())
tg.run("version")
cmdVersion := strings.TrimSpace(tg.stdout.String())
// If 'go version' is "go version <version> <goos>/<goarch>", then
// 'go env GOVERSION' is just "<version>".
if cmdVersion == envVersion || !strings.Contains(cmdVersion, envVersion) {
t.Fatalf("'go env GOVERSION' %q should be a shorter substring of 'go version' %q", envVersion, cmdVersion)
}
} }
const ( const (
@ -2022,7 +2042,7 @@ func TestBuildmodePIE(t *testing.T) {
platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
switch platform { switch platform {
case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x", case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x",
"android/amd64", "android/arm", "android/arm64", "android/386", "android/amd64", "android/arm", "android/arm64", "android/386",
"freebsd/amd64", "freebsd/amd64",
"windows/386", "windows/amd64", "windows/arm": "windows/386", "windows/amd64", "windows/arm":

View file

@ -268,6 +268,7 @@ var (
GONOPROXY = envOr("GONOPROXY", GOPRIVATE) GONOPROXY = envOr("GONOPROXY", GOPRIVATE)
GONOSUMDB = envOr("GONOSUMDB", GOPRIVATE) GONOSUMDB = envOr("GONOSUMDB", GOPRIVATE)
GOINSECURE = Getenv("GOINSECURE") GOINSECURE = Getenv("GOINSECURE")
GOVCS = Getenv("GOVCS")
) )
var SumdbDir = gopathDir("pkg/sumdb") var SumdbDir = gopathDir("pkg/sumdb")

View file

@ -87,6 +87,8 @@ func MkEnv() []cfg.EnvVar {
{Name: "GOSUMDB", Value: cfg.GOSUMDB}, {Name: "GOSUMDB", Value: cfg.GOSUMDB},
{Name: "GOTMPDIR", Value: cfg.Getenv("GOTMPDIR")}, {Name: "GOTMPDIR", Value: cfg.Getenv("GOTMPDIR")},
{Name: "GOTOOLDIR", Value: base.ToolDir}, {Name: "GOTOOLDIR", Value: base.ToolDir},
{Name: "GOVCS", Value: cfg.GOVCS},
{Name: "GOVERSION", Value: runtime.Version()},
} }
if work.GccgoBin != "" { if work.GccgoBin != "" {
@ -398,7 +400,7 @@ func getOrigEnv(key string) string {
func checkEnvWrite(key, val string) error { func checkEnvWrite(key, val string) error {
switch key { switch key {
case "GOEXE", "GOGCCFLAGS", "GOHOSTARCH", "GOHOSTOS", "GOMOD", "GOTOOLDIR": case "GOEXE", "GOGCCFLAGS", "GOHOSTARCH", "GOHOSTOS", "GOMOD", "GOTOOLDIR", "GOVERSION":
return fmt.Errorf("%s cannot be modified", key) return fmt.Errorf("%s cannot be modified", key)
case "GOENV": case "GOENV":
return fmt.Errorf("%s can only be set using the OS environment", key) return fmt.Errorf("%s can only be set using the OS environment", key)

View file

@ -526,7 +526,7 @@ General-purpose environment variables:
Comma-separated list of glob patterns (in the syntax of Go's path.Match) Comma-separated list of glob patterns (in the syntax of Go's path.Match)
of module path prefixes that should always be fetched directly of module path prefixes that should always be fetched directly
or that should not be compared against the checksum database. or that should not be compared against the checksum database.
See 'go help module-private'. See 'go help private'.
GOROOT GOROOT
The root of the go tree. The root of the go tree.
GOSUMDB GOSUMDB
@ -632,6 +632,8 @@ Additional information available from 'go env' but not read from the environment
If module-aware mode is disabled, GOMOD will be the empty string. If module-aware mode is disabled, GOMOD will be the empty string.
GOTOOLDIR GOTOOLDIR
The directory where the go tools (compile, cover, doc, etc...) are installed. The directory where the go tools (compile, cover, doc, etc...) are installed.
GOVERSION
The version of the installed Go tree, as reported by runtime.Version.
`, `,
} }

View file

@ -88,12 +88,11 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
args = []string{"all"} args = []string{"all"}
} else if modload.HasModRoot() { } else if modload.HasModRoot() {
modload.LoadModFile(ctx) // to fill Target modload.LoadModFile(ctx) // to fill Target
targetAtLatest := modload.Target.Path + "@latest"
targetAtUpgrade := modload.Target.Path + "@upgrade" targetAtUpgrade := modload.Target.Path + "@upgrade"
targetAtPatch := modload.Target.Path + "@patch" targetAtPatch := modload.Target.Path + "@patch"
for _, arg := range args { for _, arg := range args {
switch arg { switch arg {
case modload.Target.Path, targetAtLatest, targetAtUpgrade, targetAtPatch: case modload.Target.Path, targetAtUpgrade, targetAtPatch:
os.Stderr.WriteString("go mod download: skipping argument " + arg + " that resolves to the main module\n") os.Stderr.WriteString("go mod download: skipping argument " + arg + " that resolves to the main module\n")
} }
} }
@ -170,7 +169,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
for _, m := range mods { for _, m := range mods {
b, err := json.MarshalIndent(m, "", "\t") b, err := json.MarshalIndent(m, "", "\t")
if err != nil { if err != nil {
base.Fatalf("%v", err) base.Fatalf("go mod download: %v", err)
} }
os.Stdout.Write(append(b, '\n')) os.Stdout.Write(append(b, '\n'))
if m.Error != "" { if m.Error != "" {
@ -180,7 +179,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
} else { } else {
for _, m := range mods { for _, m := range mods {
if m.Error != "" { if m.Error != "" {
base.Errorf("%s", m.Error) base.Errorf("go mod download: %v", m.Error)
} }
} }
base.ExitIfErrors() base.ExitIfErrors()

View file

@ -69,7 +69,7 @@ a version on the left side is dropped.
The -retract=version and -dropretract=version flags add and drop a The -retract=version and -dropretract=version flags add and drop a
retraction on the given version. The version may be a single version retraction on the given version. The version may be a single version
like "v1.2.3" or a closed interval like "[v1.1.0-v1.1.9]". Note that like "v1.2.3" or a closed interval like "[v1.1.0,v1.1.9]". Note that
-retract=version is a no-op if that retraction already exists. -retract=version is a no-op if that retraction already exists.
The -require, -droprequire, -exclude, -dropexclude, -replace, The -require, -droprequire, -exclude, -dropexclude, -replace,

View file

@ -16,13 +16,18 @@ var cmdInit = &base.Command{
UsageLine: "go mod init [module]", UsageLine: "go mod init [module]",
Short: "initialize new module in current directory", Short: "initialize new module in current directory",
Long: ` Long: `
Init initializes and writes a new go.mod to the current directory, Init initializes and writes a new go.mod file in the current directory, in
in effect creating a new module rooted at the current directory. effect creating a new module rooted at the current directory. The go.mod file
The file go.mod must not already exist. must not already exist.
If possible, init will guess the module path from import comments
(see 'go help importpath') or from version control configuration. Init accepts one optional argument, the module path for the new module. If the
To override this guess, supply the module path as an argument. module path argument is omitted, init will attempt to infer the module path
`, using import comments in .go files, vendoring tool configuration files (like
Gopkg.lock), and the current directory (if in GOPATH).
If a configuration file for a vendoring tool is present, init will attempt to
import module requirements from it.
`,
Run: runInit, Run: runInit,
} }

View file

@ -73,7 +73,7 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) {
modpkgs := make(map[module.Version][]string) modpkgs := make(map[module.Version][]string)
for _, pkg := range pkgs { for _, pkg := range pkgs {
m := modload.PackageModule(pkg) m := modload.PackageModule(pkg)
if m == modload.Target { if m.Path == "" || m == modload.Target {
continue continue
} }
modpkgs[m] = append(modpkgs[m], pkg) modpkgs[m] = append(modpkgs[m], pkg)
@ -91,28 +91,38 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) {
includeAllReplacements = true includeAllReplacements = true
} }
var vendorMods []module.Version
for m := range isExplicit {
vendorMods = append(vendorMods, m)
}
for m := range modpkgs {
if !isExplicit[m] {
vendorMods = append(vendorMods, m)
}
}
module.Sort(vendorMods)
var buf bytes.Buffer var buf bytes.Buffer
for _, m := range modload.LoadedModules()[1:] { for _, m := range vendorMods {
if pkgs := modpkgs[m]; len(pkgs) > 0 || isExplicit[m] { line := moduleLine(m, modload.Replacement(m))
line := moduleLine(m, modload.Replacement(m)) buf.WriteString(line)
buf.WriteString(line) if cfg.BuildV {
os.Stderr.WriteString(line)
}
if isExplicit[m] {
buf.WriteString("## explicit\n")
if cfg.BuildV { if cfg.BuildV {
os.Stderr.WriteString(line) os.Stderr.WriteString("## explicit\n")
} }
if isExplicit[m] { }
buf.WriteString("## explicit\n") pkgs := modpkgs[m]
if cfg.BuildV { sort.Strings(pkgs)
os.Stderr.WriteString("## explicit\n") for _, pkg := range pkgs {
} fmt.Fprintf(&buf, "%s\n", pkg)
} if cfg.BuildV {
sort.Strings(pkgs) fmt.Fprintf(os.Stderr, "%s\n", pkg)
for _, pkg := range pkgs {
fmt.Fprintf(&buf, "%s\n", pkg)
if cfg.BuildV {
fmt.Fprintf(os.Stderr, "%s\n", pkg)
}
vendorPkg(vdir, pkg)
} }
vendorPkg(vdir, pkg)
} }
} }

View file

@ -483,7 +483,7 @@ func readDiskStatByHash(path, rev string) (file string, info *RevInfo, err error
for _, name := range names { for _, name := range names {
if strings.HasSuffix(name, suffix) { if strings.HasSuffix(name, suffix) {
v := strings.TrimSuffix(name, ".info") v := strings.TrimSuffix(name, ".info")
if IsPseudoVersion(v) && semver.Max(maxVersion, v) == v { if IsPseudoVersion(v) && semver.Compare(v, maxVersion) > 0 {
maxVersion = v maxVersion = v
file, info, err = readDiskStat(path, strings.TrimSuffix(name, ".info")) file, info, err = readDiskStat(path, strings.TrimSuffix(name, ".info"))
} }

View file

@ -568,7 +568,7 @@ func bzrParseStat(rev, out string) (*RevInfo, error) {
func fossilParseStat(rev, out string) (*RevInfo, error) { func fossilParseStat(rev, out string) (*RevInfo, error) {
for _, line := range strings.Split(out, "\n") { for _, line := range strings.Split(out, "\n") {
if strings.HasPrefix(line, "uuid:") { if strings.HasPrefix(line, "uuid:") || strings.HasPrefix(line, "hash:") {
f := strings.Fields(line) f := strings.Fields(line)
if len(f) != 5 || len(f[1]) != 40 || f[4] != "UTC" { if len(f) != 5 || len(f[1]) != 40 || f[4] != "UTC" {
return nil, vcsErrorf("unexpected response from fossil info: %q", line) return nil, vcsErrorf("unexpected response from fossil info: %q", line)

View file

@ -848,16 +848,16 @@ the checksum database is not consulted, and all unrecognized modules are
accepted, at the cost of giving up the security guarantee of verified repeatable accepted, at the cost of giving up the security guarantee of verified repeatable
downloads for all modules. A better way to bypass the checksum database downloads for all modules. A better way to bypass the checksum database
for specific modules is to use the GOPRIVATE or GONOSUMDB environment for specific modules is to use the GOPRIVATE or GONOSUMDB environment
variables. See 'go help module-private' for details. variables. See 'go help private' for details.
The 'go env -w' command (see 'go help env') can be used to set these variables The 'go env -w' command (see 'go help env') can be used to set these variables
for future go command invocations. for future go command invocations.
`, `,
} }
var HelpModulePrivate = &base.Command{ var HelpPrivate = &base.Command{
UsageLine: "module-private", UsageLine: "private",
Short: "module configuration for non-public modules", Short: "configuration for downloading non-public code",
Long: ` Long: `
The go command defaults to downloading modules from the public Go module The go command defaults to downloading modules from the public Go module
mirror at proxy.golang.org. It also defaults to validating downloaded modules, mirror at proxy.golang.org. It also defaults to validating downloaded modules,
@ -898,6 +898,11 @@ be used for downloading both public and private modules, because
GONOPROXY has been set to a pattern that won't match any modules, GONOPROXY has been set to a pattern that won't match any modules,
overriding GOPRIVATE. overriding GOPRIVATE.
The GOPRIVATE variable is also used to define the "public" and "private"
patterns for the GOVCS variable; see 'go help vcs'. For that usage,
GOPRIVATE applies even in GOPATH mode. In that case, it matches import paths
instead of module paths.
The 'go env -w' command (see 'go help env') can be used to set these variables The 'go env -w' command (see 'go help env') can be used to set these variables
for future go command invocations. for future go command invocations.
`, `,

File diff suppressed because it is too large Load diff

View file

@ -1,202 +0,0 @@
// 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 modget
import (
"context"
"errors"
"cmd/go/internal/base"
"cmd/go/internal/modload"
"cmd/go/internal/mvs"
"golang.org/x/mod/module"
)
// An upgrader adapts an underlying mvs.Reqs to apply an
// upgrade policy to a list of targets and their dependencies.
type upgrader struct {
mvs.Reqs
// cmdline maps a module path to a query made for that module at a
// specific target version. Each query corresponds to a module
// matched by a command line argument.
cmdline map[string]*query
// upgrade is a set of modules providing dependencies of packages
// matched by command line arguments. If -u or -u=patch is set,
// these modules are upgraded accordingly.
upgrade map[string]bool
}
// newUpgrader creates an upgrader. cmdline contains queries made at
// specific versions for modules matched by command line arguments. pkgs
// is the set of packages matched by command line arguments. If -u or -u=patch
// is set, modules providing dependencies of pkgs are upgraded accordingly.
func newUpgrader(cmdline map[string]*query, pkgs map[string]bool) *upgrader {
u := &upgrader{
Reqs: modload.Reqs(),
cmdline: cmdline,
}
if getU != "" {
u.upgrade = make(map[string]bool)
// Traverse package import graph.
// Initialize work queue with root packages.
seen := make(map[string]bool)
var work []string
add := func(path string) {
if !seen[path] {
seen[path] = true
work = append(work, path)
}
}
for pkg := range pkgs {
add(pkg)
}
for len(work) > 0 {
pkg := work[0]
work = work[1:]
m := modload.PackageModule(pkg)
u.upgrade[m.Path] = true
// testImports is empty unless test imports were actually loaded,
// i.e., -t was set or "all" was one of the arguments.
imports, testImports := modload.PackageImports(pkg)
for _, imp := range imports {
add(imp)
}
for _, imp := range testImports {
add(imp)
}
}
}
return u
}
// Required returns the requirement list for m.
// For the main module, we override requirements with the modules named
// one the command line, and we include new requirements. Otherwise,
// we defer to u.Reqs.
func (u *upgrader) Required(m module.Version) ([]module.Version, error) {
rs, err := u.Reqs.Required(m)
if err != nil {
return nil, err
}
if m != modload.Target {
return rs, nil
}
overridden := make(map[string]bool)
for i, m := range rs {
if q := u.cmdline[m.Path]; q != nil && q.m.Version != "none" {
rs[i] = q.m
overridden[q.m.Path] = true
}
}
for _, q := range u.cmdline {
if !overridden[q.m.Path] && q.m.Path != modload.Target.Path && q.m.Version != "none" {
rs = append(rs, q.m)
}
}
return rs, nil
}
// Upgrade returns the desired upgrade for m.
//
// If m was requested at a specific version on the command line, then
// Upgrade returns that version.
//
// If -u is set and m provides a dependency of a package matched by
// command line arguments, then Upgrade may provider a newer tagged version.
// If m is a tagged version, then Upgrade will return the latest tagged
// version (with the same minor version number if -u=patch).
// If m is a pseudo-version, then Upgrade returns the latest tagged version
// only if that version has a time-stamp newer than m. This special case
// prevents accidental downgrades when already using a pseudo-version
// newer than the latest tagged version.
//
// If none of the above cases apply, then Upgrade returns m.
func (u *upgrader) Upgrade(m module.Version) (module.Version, error) {
// Allow pkg@vers on the command line to override the upgrade choice v.
// If q's version is < m.Version, then we're going to downgrade anyway,
// and it's cleaner to avoid moving back and forth and picking up
// extraneous other newer dependencies.
// If q's version is > m.Version, then we're going to upgrade past
// m.Version anyway, and again it's cleaner to avoid moving back and forth
// picking up extraneous other newer dependencies.
if q := u.cmdline[m.Path]; q != nil {
return q.m, nil
}
if !u.upgrade[m.Path] {
// Not involved in upgrade. Leave alone.
return m, nil
}
// Run query required by upgrade semantics.
// Note that Query "latest" is not the same as using repo.Latest,
// which may return a pseudoversion for the latest commit.
// Query "latest" returns the newest tagged version or the newest
// prerelease version if there are no non-prereleases, or repo.Latest
// if there aren't any tagged versions.
// If we're querying "upgrade" or "patch", Query will compare the current
// version against the chosen version and will return the current version
// if it is newer.
info, err := modload.Query(context.TODO(), m.Path, string(getU), m.Version, checkAllowedOrCurrent(m.Version))
if err != nil {
// Report error but return m, to let version selection continue.
// (Reporting the error will fail the command at the next base.ExitIfErrors.)
// Special case: if the error is for m.Version itself and m.Version has a
// replacement, then keep it and don't report the error: the fact that the
// version is invalid is likely the reason it was replaced to begin with.
var vErr *module.InvalidVersionError
if errors.As(err, &vErr) && vErr.Version == m.Version && modload.Replacement(m).Path != "" {
return m, nil
}
// Special case: if the error is "no matching versions" then don't
// even report the error. Because Query does not consider pseudo-versions,
// it may happen that we have a pseudo-version but during -u=patch
// the query v0.0 matches no versions (not even the one we're using).
var noMatch *modload.NoMatchingVersionError
if !errors.As(err, &noMatch) {
base.Errorf("go get: upgrading %s@%s: %v", m.Path, m.Version, err)
}
return m, nil
}
if info.Version != m.Version {
logOncef("go: %s %s => %s", m.Path, getU, info.Version)
}
return module.Version{Path: m.Path, Version: info.Version}, nil
}
// buildListForLostUpgrade returns the build list for the module graph
// rooted at lost. Unlike mvs.BuildList, the target module (lost) is not
// treated specially. The returned build list may contain a newer version
// of lost.
//
// buildListForLostUpgrade is used after a downgrade has removed a module
// requested at a specific version. This helps us understand the requirements
// implied by each downgrade.
func buildListForLostUpgrade(lost module.Version, reqs mvs.Reqs) ([]module.Version, error) {
return mvs.BuildList(lostUpgradeRoot, &lostUpgradeReqs{Reqs: reqs, lost: lost})
}
var lostUpgradeRoot = module.Version{Path: "lost-upgrade-root", Version: ""}
type lostUpgradeReqs struct {
mvs.Reqs
lost module.Version
}
func (r *lostUpgradeReqs) Required(mod module.Version) ([]module.Version, error) {
if mod == lostUpgradeRoot {
return []module.Version{r.lost}, nil
}
return r.Reqs.Required(mod)
}

View file

@ -0,0 +1,357 @@
// 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 modget
import (
"fmt"
"path/filepath"
"regexp"
"strings"
"sync"
"cmd/go/internal/base"
"cmd/go/internal/modload"
"cmd/go/internal/search"
"cmd/go/internal/str"
"golang.org/x/mod/module"
)
// A query describes a command-line argument and the modules and/or packages
// to which that argument may resolve..
type query struct {
// raw is the original argument, to be printed in error messages.
raw string
// rawVersion is the portion of raw corresponding to version, if any
rawVersion string
// pattern is the part of the argument before "@" (or the whole argument
// if there is no "@"), which may match either packages (preferred) or
// modules (if no matching packages).
//
// The pattern may also be "-u", for the synthetic query representing the -u
// (“upgrade”)flag.
pattern string
// patternIsLocal indicates whether pattern is restricted to match only paths
// local to the main module, such as absolute filesystem paths or paths
// beginning with './'.
//
// A local pattern must resolve to one or more packages in the main module.
patternIsLocal bool
// version is the part of the argument after "@", or an implied
// "upgrade" or "patch" if there is no "@". version specifies the
// module version to get.
version string
// matchWildcard, if non-nil, reports whether pattern, which must be a
// wildcard (with the substring "..."), matches the given package or module
// path.
matchWildcard func(path string) bool
// canMatchWildcard, if non-nil, reports whether the module with the given
// path could lexically contain a package matching pattern, which must be a
// wildcard.
canMatchWildcardInModule func(mPath string) bool
// conflict is the first query identified as incompatible with this one.
// conflict forces one or more of the modules matching this query to a
// version that does not match version.
conflict *query
// candidates is a list of sets of alternatives for a path that matches (or
// contains packages that match) the pattern. The query can be resolved by
// choosing exactly one alternative from each set in the list.
//
// A path-literal query results in only one set: the path itself, which
// may resolve to either a package path or a module path.
//
// A wildcard query results in one set for each matching module path, each
// module for which the matching version contains at least one matching
// package, and (if no other modules match) one candidate set for the pattern
// overall if no existing match is identified in the build list.
//
// A query for pattern "all" results in one set for each package transitively
// imported by the main module.
//
// The special query for the "-u" flag results in one set for each
// otherwise-unconstrained package that has available upgrades.
candidates []pathSet
candidatesMu sync.Mutex
// pathSeen ensures that only one pathSet is added to the query per
// unique path.
pathSeen sync.Map
// resolved contains the set of modules whose versions have been determined by
// this query, in the order in which they were determined.
//
// The resolver examines the candidate sets for each query, resolving one
// module per candidate set in a way that attempts to avoid obvious conflicts
// between the versions resolved by different queries.
resolved []module.Version
// matchesPackages is true if the resolved modules provide at least one
// package mathcing q.pattern.
matchesPackages bool
}
// A pathSet describes the possible options for resolving a specific path
// to a package and/or module.
type pathSet struct {
// path is a package (if "all" or "-u" or a non-wildcard) or module (if
// wildcard) path that could be resolved by adding any of the modules in this
// set. For a wildcard pattern that so far matches no packages, the path is
// the wildcard pattern itself.
//
// Each path must occur only once in a query's candidate sets, and the path is
// added implicitly to each pathSet returned to pathOnce.
path string
// pkgMods is a set of zero or more modules, each of which contains the
// package with the indicated path. Due to the requirement that imports be
// unambiguous, only one such module can be in the build list, and all others
// must be excluded.
pkgMods []module.Version
// mod is either the zero Version, or a module that does not contain any
// packages matching the query but for which the module path itself
// matches the query pattern.
//
// We track this module separately from pkgMods because, all else equal, we
// prefer to match a query to a package rather than just a module. Also,
// unlike the modules in pkgMods, this module does not inherently exclude
// any other module in pkgMods.
mod module.Version
err error
}
// errSet returns a pathSet containing the given error.
func errSet(err error) pathSet { return pathSet{err: err} }
// newQuery returns a new query parsed from the raw argument,
// which must be either path or path@version.
func newQuery(raw string) (*query, error) {
pattern := raw
rawVers := ""
if i := strings.Index(raw, "@"); i >= 0 {
pattern, rawVers = raw[:i], raw[i+1:]
if strings.Contains(rawVers, "@") || rawVers == "" {
return nil, fmt.Errorf("invalid module version syntax %q", raw)
}
}
// If no version suffix is specified, assume @upgrade.
// If -u=patch was specified, assume @patch instead.
version := rawVers
if version == "" {
if getU.version == "" {
version = "upgrade"
} else {
version = getU.version
}
}
q := &query{
raw: raw,
rawVersion: rawVers,
pattern: pattern,
patternIsLocal: filepath.IsAbs(pattern) || search.IsRelativePath(pattern),
version: version,
}
if strings.Contains(q.pattern, "...") {
q.matchWildcard = search.MatchPattern(q.pattern)
q.canMatchWildcardInModule = search.TreeCanMatchPattern(q.pattern)
}
if err := q.validate(); err != nil {
return q, err
}
return q, nil
}
// validate reports a non-nil error if q is not sensible and well-formed.
func (q *query) validate() error {
if q.patternIsLocal {
if q.rawVersion != "" {
return fmt.Errorf("can't request explicit version %q of path %q in main module", q.rawVersion, q.pattern)
}
return nil
}
if q.pattern == "all" {
// If there is no main module, "all" is not meaningful.
if !modload.HasModRoot() {
return fmt.Errorf(`cannot match "all": working directory is not part of a module`)
}
if !versionOkForMainModule(q.version) {
// TODO(bcmills): "all@none" seems like a totally reasonable way to
// request that we remove all module requirements, leaving only the main
// module and standard library. Perhaps we should implement that someday.
return &modload.QueryMatchesMainModuleError{
Pattern: q.pattern,
Query: q.version,
}
}
}
if search.IsMetaPackage(q.pattern) && q.pattern != "all" {
if q.pattern != q.raw {
return fmt.Errorf("can't request explicit version of standard-library pattern %q", q.pattern)
}
}
return nil
}
// String returns the original argument from which q was parsed.
func (q *query) String() string { return q.raw }
// ResolvedString returns a string describing m as a resolved match for q.
func (q *query) ResolvedString(m module.Version) string {
if m.Path != q.pattern {
if m.Version != q.version {
return fmt.Sprintf("%v (matching %s@%s)", m, q.pattern, q.version)
}
return fmt.Sprintf("%v (matching %v)", m, q)
}
if m.Version != q.version {
return fmt.Sprintf("%s@%s (%s)", q.pattern, q.version, m.Version)
}
return q.String()
}
// isWildcard reports whether q is a pattern that can match multiple paths.
func (q *query) isWildcard() bool {
return q.matchWildcard != nil || (q.patternIsLocal && strings.Contains(q.pattern, "..."))
}
// matchesPath reports whether the given path matches q.pattern.
func (q *query) matchesPath(path string) bool {
if q.matchWildcard != nil {
return q.matchWildcard(path)
}
return path == q.pattern
}
// canMatchInModule reports whether the given module path can potentially
// contain q.pattern.
func (q *query) canMatchInModule(mPath string) bool {
if q.canMatchWildcardInModule != nil {
return q.canMatchWildcardInModule(mPath)
}
return str.HasPathPrefix(q.pattern, mPath)
}
// pathOnce invokes f to generate the pathSet for the given path,
// if one is still needed.
//
// Note that, unlike sync.Once, pathOnce does not guarantee that a concurrent
// call to f for the given path has completed on return.
//
// pathOnce is safe for concurrent use by multiple goroutines, but note that
// multiple concurrent calls will result in the sets being added in
// nondeterministic order.
func (q *query) pathOnce(path string, f func() pathSet) {
if _, dup := q.pathSeen.LoadOrStore(path, nil); dup {
return
}
cs := f()
if len(cs.pkgMods) > 0 || cs.mod != (module.Version{}) || cs.err != nil {
cs.path = path
q.candidatesMu.Lock()
q.candidates = append(q.candidates, cs)
q.candidatesMu.Unlock()
}
}
// reportError logs err concisely using base.Errorf.
func reportError(q *query, err error) {
errStr := err.Error()
// If err already mentions all of the relevant parts of q, just log err to
// reduce stutter. Otherwise, log both q and err.
//
// TODO(bcmills): Use errors.As to unpack these errors instead of parsing
// strings with regular expressions.
patternRE := regexp.MustCompile("(?m)(?:[ \t(\"`]|^)" + regexp.QuoteMeta(q.pattern) + "(?:[ @:)\"`]|$)")
if patternRE.MatchString(errStr) {
if q.rawVersion == "" {
base.Errorf("go get: %s", errStr)
return
}
versionRE := regexp.MustCompile("(?m)(?:[ @(\"`]|^)" + regexp.QuoteMeta(q.version) + "(?:[ :)\"`]|$)")
if versionRE.MatchString(errStr) {
base.Errorf("go get: %s", errStr)
return
}
}
if qs := q.String(); qs != "" {
base.Errorf("go get %s: %s", qs, errStr)
} else {
base.Errorf("go get: %s", errStr)
}
}
func reportConflict(pq *query, m module.Version, conflict versionReason) {
if pq.conflict != nil {
// We've already reported a conflict for the proposed query.
// Don't report it again, even if it has other conflicts.
return
}
pq.conflict = conflict.reason
proposed := versionReason{
version: m.Version,
reason: pq,
}
if pq.isWildcard() && !conflict.reason.isWildcard() {
// Prefer to report the specific path first and the wildcard second.
proposed, conflict = conflict, proposed
}
reportError(pq, &conflictError{
mPath: m.Path,
proposed: proposed,
conflict: conflict,
})
}
type conflictError struct {
mPath string
proposed versionReason
conflict versionReason
}
func (e *conflictError) Error() string {
argStr := func(q *query, v string) string {
if v != q.version {
return fmt.Sprintf("%s@%s (%s)", q.pattern, q.version, v)
}
return q.String()
}
pq := e.proposed.reason
rq := e.conflict.reason
modDetail := ""
if e.mPath != pq.pattern {
modDetail = fmt.Sprintf("for module %s, ", e.mPath)
}
return fmt.Sprintf("%s%s conflicts with %s",
modDetail,
argStr(pq, e.proposed.version),
argStr(rq, e.conflict.version))
}
func versionOkForMainModule(version string) bool {
return version == "upgrade" || version == "patch"
}

View file

@ -123,13 +123,13 @@ func addRetraction(ctx context.Context, m *modinfo.ModulePublic) {
return return
} }
err := checkRetractions(ctx, module.Version{Path: m.Path, Version: m.Version}) err := CheckRetractions(ctx, module.Version{Path: m.Path, Version: m.Version})
var rerr *retractedError var rerr *ModuleRetractedError
if errors.As(err, &rerr) { if errors.As(err, &rerr) {
if len(rerr.rationale) == 0 { if len(rerr.Rationale) == 0 {
m.Retracted = []string{"retracted by module author"} m.Retracted = []string{"retracted by module author"}
} else { } else {
m.Retracted = rerr.rationale m.Retracted = rerr.Rationale
} }
} else if err != nil && m.Error == nil { } else if err != nil && m.Error == nil {
m.Error = &modinfo.ModuleError{Err: err.Error()} m.Error = &modinfo.ModuleError{Err: err.Error()}

View file

@ -12,6 +12,7 @@ import (
"context" "context"
"fmt" "fmt"
"os" "os"
"strings"
"golang.org/x/mod/module" "golang.org/x/mod/module"
) )
@ -27,6 +28,11 @@ import (
// //
var buildList []module.Version var buildList []module.Version
// capVersionSlice returns s with its cap reduced to its length.
func capVersionSlice(s []module.Version) []module.Version {
return s[:len(s):len(s)]
}
// LoadAllModules loads and returns the list of modules matching the "all" // LoadAllModules loads and returns the list of modules matching the "all"
// module pattern, starting with the Target module and in a deterministic // module pattern, starting with the Target module and in a deterministic
// (stable) order, without loading any packages. // (stable) order, without loading any packages.
@ -35,21 +41,21 @@ var buildList []module.Version
// LoadAllModules need only be called if LoadPackages is not, // LoadAllModules need only be called if LoadPackages is not,
// typically in commands that care about modules but no particular package. // typically in commands that care about modules but no particular package.
// //
// The caller must not modify the returned list. // The caller must not modify the returned list, but may append to it.
func LoadAllModules(ctx context.Context) []module.Version { func LoadAllModules(ctx context.Context) []module.Version {
LoadModFile(ctx) LoadModFile(ctx)
ReloadBuildList() ReloadBuildList()
WriteGoMod() WriteGoMod()
return buildList return capVersionSlice(buildList)
} }
// LoadedModules returns the list of module requirements loaded or set by a // LoadedModules returns the list of module requirements loaded or set by a
// previous call (typically LoadAllModules or LoadPackages), starting with the // previous call (typically LoadAllModules or LoadPackages), starting with the
// Target module and in a deterministic (stable) order. // Target module and in a deterministic (stable) order.
// //
// The caller must not modify the returned list. // The caller must not modify the returned list, but may append to it.
func LoadedModules() []module.Version { func LoadedModules() []module.Version {
return buildList return capVersionSlice(buildList)
} }
// Selected returns the selected version of the module with the given path, or // Selected returns the selected version of the module with the given path, or
@ -67,15 +73,149 @@ func Selected(path string) (version string) {
return "" return ""
} }
// SetBuildList sets the module build list. // EditBuildList edits the global build list by first adding every module in add
// The caller is responsible for ensuring that the list is valid. // to the existing build list, then adjusting versions (and adding or removing
// SetBuildList does not retain a reference to the original list. // requirements as needed) until every module in mustSelect is selected at the
func SetBuildList(list []module.Version) { // given version.
buildList = append([]module.Version{}, list...) //
// (Note that the newly-added modules might not be selected in the resulting
// build list: they could be lower than existing requirements or conflict with
// versions in mustSelect.)
//
// After performing the requested edits, EditBuildList returns the updated build
// list.
//
// If the versions listed in mustSelect are mutually incompatible (due to one of
// the listed modules requiring a higher version of another), EditBuildList
// returns a *ConstraintError and leaves the build list in its previous state.
func EditBuildList(ctx context.Context, add, mustSelect []module.Version) error {
var upgraded = capVersionSlice(buildList)
if len(add) > 0 {
// First, upgrade the build list with any additions.
// In theory we could just append the additions to the build list and let
// mvs.Downgrade take care of resolving the upgrades too, but the
// diagnostics from Upgrade are currently much better in case of errors.
var err error
upgraded, err = mvs.Upgrade(Target, &mvsReqs{buildList: upgraded}, add...)
if err != nil {
return err
}
}
downgraded, err := mvs.Downgrade(Target, &mvsReqs{buildList: append(upgraded, mustSelect...)}, mustSelect...)
if err != nil {
return err
}
final, err := mvs.Upgrade(Target, &mvsReqs{buildList: downgraded}, mustSelect...)
if err != nil {
return err
}
selected := make(map[string]module.Version, len(final))
for _, m := range final {
selected[m.Path] = m
}
inconsistent := false
for _, m := range mustSelect {
s, ok := selected[m.Path]
if !ok {
if m.Version != "none" {
panic(fmt.Sprintf("internal error: mvs.BuildList lost %v", m))
}
continue
}
if s.Version != m.Version {
inconsistent = true
break
}
}
if !inconsistent {
buildList = final
return nil
}
// We overshot one or more of the modules in mustSelected, which means that
// Downgrade removed something in mustSelect because it conflicted with
// something else in mustSelect.
//
// Walk the requirement graph to find the conflict.
//
// TODO(bcmills): Ideally, mvs.Downgrade (or a replacement for it) would do
// this directly.
reqs := &mvsReqs{buildList: final}
reason := map[module.Version]module.Version{}
for _, m := range mustSelect {
reason[m] = m
}
queue := mustSelect[:len(mustSelect):len(mustSelect)]
for len(queue) > 0 {
var m module.Version
m, queue = queue[0], queue[1:]
required, err := reqs.Required(m)
if err != nil {
return err
}
for _, r := range required {
if _, ok := reason[r]; !ok {
reason[r] = reason[m]
queue = append(queue, r)
}
}
}
var conflicts []Conflict
for _, m := range mustSelect {
s, ok := selected[m.Path]
if !ok {
if m.Version != "none" {
panic(fmt.Sprintf("internal error: mvs.BuildList lost %v", m))
}
continue
}
if s.Version != m.Version {
conflicts = append(conflicts, Conflict{
Source: reason[s],
Dep: s,
Constraint: m,
})
}
}
return &ConstraintError{
Conflicts: conflicts,
}
}
// A ConstraintError describes inconsistent constraints in EditBuildList
type ConstraintError struct {
// Conflict lists the source of the conflict for each version in mustSelect
// that could not be selected due to the requirements of some other version in
// mustSelect.
Conflicts []Conflict
}
func (e *ConstraintError) Error() string {
b := new(strings.Builder)
b.WriteString("version constraints conflict:")
for _, c := range e.Conflicts {
fmt.Fprintf(b, "\n\t%v requires %v, but %v is requested", c.Source, c.Dep, c.Constraint)
}
return b.String()
}
// A Conflict documents that Source requires Dep, which conflicts with Constraint.
// (That is, Dep has the same module path as Constraint but a higher version.)
type Conflict struct {
Source module.Version
Dep module.Version
Constraint module.Version
} }
// ReloadBuildList resets the state of loaded packages, then loads and returns // ReloadBuildList resets the state of loaded packages, then loads and returns
// the build list set in SetBuildList. // the build list set by EditBuildList.
func ReloadBuildList() []module.Version { func ReloadBuildList() []module.Version {
loaded = loadFromRoots(loaderParams{ loaded = loadFromRoots(loaderParams{
PackageOpts: PackageOpts{ PackageOpts: PackageOpts{
@ -84,7 +224,7 @@ func ReloadBuildList() []module.Version {
listRoots: func() []string { return nil }, listRoots: func() []string { return nil },
allClosesOverTests: index.allPatternClosesOverTests(), // but doesn't matter because the root list is empty. allClosesOverTests: index.allPatternClosesOverTests(), // but doesn't matter because the root list is empty.
}) })
return buildList return capVersionSlice(buildList)
} }
// TidyBuildList trims the build list to the minimal requirements needed to // TidyBuildList trims the build list to the minimal requirements needed to

View file

@ -365,7 +365,7 @@ list if the error is a 404 or 410 HTTP response or if the current proxy is
followed by a pipe character, indicating it is safe to fall back on any error. followed by a pipe character, indicating it is safe to fall back on any error.
The GOPRIVATE and GONOPROXY environment variables allow bypassing The GOPRIVATE and GONOPROXY environment variables allow bypassing
the proxy for selected modules. See 'go help module-private' for details. the proxy for selected modules. See 'go help private' for details.
No matter the source of the modules, the go command checks downloads against No matter the source of the modules, the go command checks downloads against
known checksums, to detect unexpected changes in the content of any specific known checksums, to detect unexpected changes in the content of any specific
@ -439,7 +439,7 @@ The leading verb can be factored out of adjacent lines to create a block,
like in Go imports: like in Go imports:
require ( require (
new/thing v2.3.4 new/thing/v2 v2.3.4
old/thing v1.2.3 old/thing v1.2.3
) )

View file

@ -188,9 +188,9 @@ func (e *invalidImportError) Unwrap() error {
// importFromBuildList can return an empty directory string, for fake packages // importFromBuildList can return an empty directory string, for fake packages
// like "C" and "unsafe". // like "C" and "unsafe".
// //
// If the package cannot be found in the current build list, // If the package cannot be found in buildList,
// importFromBuildList returns an *ImportMissingError. // importFromBuildList returns an *ImportMissingError.
func importFromBuildList(ctx context.Context, path string) (m module.Version, dir string, err error) { func importFromBuildList(ctx context.Context, path string, buildList []module.Version) (m module.Version, dir string, err error) {
if strings.Contains(path, "@") { if strings.Contains(path, "@") {
return module.Version{}, "", fmt.Errorf("import path should not have @version") return module.Version{}, "", fmt.Errorf("import path should not have @version")
} }
@ -383,7 +383,7 @@ func queryImport(ctx context.Context, path string) (module.Version, error) {
// and return m, dir, ImpportMissingError. // and return m, dir, ImpportMissingError.
fmt.Fprintf(os.Stderr, "go: finding module for package %s\n", path) fmt.Fprintf(os.Stderr, "go: finding module for package %s\n", path)
candidates, err := QueryPattern(ctx, path, "latest", Selected, CheckAllowed) candidates, err := QueryPackages(ctx, path, "latest", Selected, CheckAllowed)
if err != nil { if err != nil {
if errors.Is(err, fs.ErrNotExist) { if errors.Is(err, fs.ErrNotExist) {
// Return "cannot find module providing package […]" instead of whatever // Return "cannot find module providing package […]" instead of whatever

View file

@ -141,6 +141,15 @@ type PackageOpts struct {
// if the flag is set to "readonly" (the default) or "vendor". // if the flag is set to "readonly" (the default) or "vendor".
ResolveMissingImports bool ResolveMissingImports bool
// AllowPackage, if non-nil, is called after identifying the module providing
// each package. If AllowPackage returns a non-nil error, that error is set
// for the package, and the imports and test of that package will not be
// loaded.
//
// AllowPackage may be invoked concurrently by multiple goroutines,
// and may be invoked multiple times for a given package path.
AllowPackage func(ctx context.Context, path string, mod module.Version) error
// LoadTests loads the test dependencies of each package matching a requested // LoadTests loads the test dependencies of each package matching a requested
// pattern. If ResolveMissingImports is also true, test dependencies will be // pattern. If ResolveMissingImports is also true, test dependencies will be
// resolved if missing. // resolved if missing.
@ -550,6 +559,7 @@ func DirImportPath(dir string) string {
func TargetPackages(ctx context.Context, pattern string) *search.Match { func TargetPackages(ctx context.Context, pattern string) *search.Match {
// TargetPackages is relative to the main module, so ensure that the main // TargetPackages is relative to the main module, so ensure that the main
// module is a thing that can contain packages. // module is a thing that can contain packages.
LoadModFile(ctx)
ModRoot() ModRoot()
m := search.NewMatch(pattern) m := search.NewMatch(pattern)
@ -1042,7 +1052,7 @@ func (ld *loader) load(pkg *loadPkg) {
return return
} }
pkg.mod, pkg.dir, pkg.err = importFromBuildList(context.TODO(), pkg.path) pkg.mod, pkg.dir, pkg.err = importFromBuildList(context.TODO(), pkg.path, buildList)
if pkg.dir == "" { if pkg.dir == "" {
return return
} }
@ -1058,6 +1068,11 @@ func (ld *loader) load(pkg *loadPkg) {
// to scanning source code for imports). // to scanning source code for imports).
ld.applyPkgFlags(pkg, pkgInAll) ld.applyPkgFlags(pkg, pkgInAll)
} }
if ld.AllowPackage != nil {
if err := ld.AllowPackage(context.TODO(), pkg.path, pkg.mod); err != nil {
pkg.err = err
}
}
imports, testImports, err := scanDir(pkg.dir, ld.Tags) imports, testImports, err := scanDir(pkg.dir, ld.Tags)
if err != nil { if err != nil {

View file

@ -59,7 +59,7 @@ func CheckAllowed(ctx context.Context, m module.Version) error {
if err := CheckExclusions(ctx, m); err != nil { if err := CheckExclusions(ctx, m); err != nil {
return err return err
} }
if err := checkRetractions(ctx, m); err != nil { if err := CheckRetractions(ctx, m); err != nil {
return err return err
} }
return nil return nil
@ -85,9 +85,9 @@ type excludedError struct{}
func (e *excludedError) Error() string { return "excluded by go.mod" } func (e *excludedError) Error() string { return "excluded by go.mod" }
func (e *excludedError) Is(err error) bool { return err == ErrDisallowed } func (e *excludedError) Is(err error) bool { return err == ErrDisallowed }
// checkRetractions returns an error if module m has been retracted by // CheckRetractions returns an error if module m has been retracted by
// its author. // its author.
func checkRetractions(ctx context.Context, m module.Version) error { func CheckRetractions(ctx context.Context, m module.Version) error {
if m.Version == "" { if m.Version == "" {
// Main module, standard library, or file replacement module. // Main module, standard library, or file replacement module.
// Cannot be retracted. // Cannot be retracted.
@ -165,28 +165,28 @@ func checkRetractions(ctx context.Context, m module.Version) error {
} }
} }
if isRetracted { if isRetracted {
return module.VersionError(m, &retractedError{rationale: rationale}) return module.VersionError(m, &ModuleRetractedError{Rationale: rationale})
} }
return nil return nil
} }
var retractCache par.Cache var retractCache par.Cache
type retractedError struct { type ModuleRetractedError struct {
rationale []string Rationale []string
} }
func (e *retractedError) Error() string { func (e *ModuleRetractedError) Error() string {
msg := "retracted by module author" msg := "retracted by module author"
if len(e.rationale) > 0 { if len(e.Rationale) > 0 {
// This is meant to be a short error printed on a terminal, so just // This is meant to be a short error printed on a terminal, so just
// print the first rationale. // print the first rationale.
msg += ": " + ShortRetractionRationale(e.rationale[0]) msg += ": " + ShortRetractionRationale(e.Rationale[0])
} }
return msg return msg
} }
func (e *retractedError) Is(err error) bool { func (e *ModuleRetractedError) Is(err error) bool {
return err == ErrDisallowed return err == ErrDisallowed
} }

View file

@ -24,7 +24,7 @@ type mvsReqs struct {
} }
// Reqs returns the current module requirement graph. // Reqs returns the current module requirement graph.
// Future calls to SetBuildList do not affect the operation // Future calls to EditBuildList do not affect the operation
// of the returned Reqs. // of the returned Reqs.
func Reqs() mvs.Reqs { func Reqs() mvs.Reqs {
r := &mvsReqs{ r := &mvsReqs{
@ -58,7 +58,7 @@ func (r *mvsReqs) Required(mod module.Version) ([]module.Version, error) {
// be chosen over other versions of the same module in the module dependency // be chosen over other versions of the same module in the module dependency
// graph. // graph.
func (*mvsReqs) Max(v1, v2 string) string { func (*mvsReqs) Max(v1, v2 string) string {
if v1 != "" && semver.Compare(v1, v2) == -1 { if v1 != "" && (v2 == "" || semver.Compare(v1, v2) == -1) {
return v2 return v2
} }
return v1 return v1
@ -99,8 +99,16 @@ func versions(ctx context.Context, path string, allowed AllowedFunc) ([]string,
// Previous returns the tagged version of m.Path immediately prior to // Previous returns the tagged version of m.Path immediately prior to
// m.Version, or version "none" if no prior version is tagged. // m.Version, or version "none" if no prior version is tagged.
//
// Since the version of Target is not found in the version list,
// it has no previous version.
func (*mvsReqs) Previous(m module.Version) (module.Version, error) { func (*mvsReqs) Previous(m module.Version) (module.Version, error) {
// TODO(golang.org/issue/38714): thread tracing context through MVS. // TODO(golang.org/issue/38714): thread tracing context through MVS.
if m == Target {
return module.Version{Path: m.Path, Version: "none"}, nil
}
list, err := versions(context.TODO(), m.Path, CheckAllowed) list, err := versions(context.TODO(), m.Path, CheckAllowed)
if err != nil { if err != nil {
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {

View file

@ -0,0 +1,33 @@
// 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 modload_test
import (
"testing"
"cmd/go/internal/modload"
)
func TestReqsMax(t *testing.T) {
type testCase struct {
a, b, want string
}
reqs := modload.Reqs()
for _, tc := range []testCase{
{a: "v0.1.0", b: "v0.2.0", want: "v0.2.0"},
{a: "v0.2.0", b: "v0.1.0", want: "v0.2.0"},
{a: "", b: "v0.1.0", want: ""}, // "" is Target.Version
{a: "v0.1.0", b: "", want: ""},
{a: "none", b: "v0.1.0", want: "v0.1.0"},
{a: "v0.1.0", b: "none", want: "v0.1.0"},
{a: "none", b: "", want: ""},
{a: "", b: "none", want: ""},
} {
max := reqs.Max(tc.a, tc.b)
if max != tc.want {
t.Errorf("Reqs().Max(%q, %q) = %q; want %q", tc.a, tc.b, max, tc.want)
}
}
}

View file

@ -47,8 +47,9 @@ import (
// with non-prereleases preferred over prereleases. // with non-prereleases preferred over prereleases.
// - a repository commit identifier or tag, denoting that commit. // - a repository commit identifier or tag, denoting that commit.
// //
// current denotes the current version of the module; it may be "" if the // current denotes the currently-selected version of the module; it may be
// current version is unknown or should not be considered. If query is // "none" if no version is currently selected, or "" if the currently-selected
// version is unknown or should not be considered. If query is
// "upgrade" or "patch", current will be returned if it is a newer // "upgrade" or "patch", current will be returned if it is a newer
// semantic version or a chronologically later pseudo-version than the // semantic version or a chronologically later pseudo-version than the
// version that would otherwise be chosen. This prevents accidental downgrades // version that would otherwise be chosen. This prevents accidental downgrades
@ -98,7 +99,7 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed
ctx, span := trace.StartSpan(ctx, "modload.queryProxy "+path+" "+query) ctx, span := trace.StartSpan(ctx, "modload.queryProxy "+path+" "+query)
defer span.Done() defer span.Done()
if current != "" && !semver.IsValid(current) { if current != "" && current != "none" && !semver.IsValid(current) {
return nil, fmt.Errorf("invalid previous version %q", current) return nil, fmt.Errorf("invalid previous version %q", current)
} }
if cfg.BuildMod == "vendor" { if cfg.BuildMod == "vendor" {
@ -108,10 +109,7 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed
allowed = func(context.Context, module.Version) error { return nil } allowed = func(context.Context, module.Version) error { return nil }
} }
if path == Target.Path { if path == Target.Path && (query == "upgrade" || query == "patch") {
if query != "latest" {
return nil, fmt.Errorf("can't query specific version (%q) for the main module (%s)", query, path)
}
if err := allowed(ctx, Target); err != nil { if err := allowed(ctx, Target); err != nil {
return nil, fmt.Errorf("internal error: main module version is not allowed: %w", err) return nil, fmt.Errorf("internal error: main module version is not allowed: %w", err)
} }
@ -236,7 +234,7 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed
} }
} }
if (query == "upgrade" || query == "patch") && current != "" { if (query == "upgrade" || query == "patch") && current != "" && current != "none" {
// "upgrade" and "patch" may stay on the current version if allowed. // "upgrade" and "patch" may stay on the current version if allowed.
if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) { if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) {
return nil, err return nil, err
@ -323,7 +321,7 @@ func newQueryMatcher(path string, query, current string, allowed AllowedFunc) (*
qm.mayUseLatest = true qm.mayUseLatest = true
case query == "upgrade": case query == "upgrade":
if current == "" { if current == "" || current == "none" {
qm.mayUseLatest = true qm.mayUseLatest = true
} else { } else {
qm.mayUseLatest = modfetch.IsPseudoVersion(current) qm.mayUseLatest = modfetch.IsPseudoVersion(current)
@ -331,6 +329,9 @@ func newQueryMatcher(path string, query, current string, allowed AllowedFunc) (*
} }
case query == "patch": case query == "patch":
if current == "none" {
return nil, &NoPatchBaseError{path}
}
if current == "" { if current == "" {
qm.mayUseLatest = true qm.mayUseLatest = true
} else { } else {
@ -504,20 +505,39 @@ type QueryResult struct {
Packages []string Packages []string
} }
// QueryPackages is like QueryPattern, but requires that the pattern match at
// least one package and omits the non-package result (if any).
func QueryPackages(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) ([]QueryResult, error) {
pkgMods, modOnly, err := QueryPattern(ctx, pattern, query, current, allowed)
if len(pkgMods) == 0 && err == nil {
return nil, &PackageNotInModuleError{
Mod: modOnly.Mod,
Replacement: Replacement(modOnly.Mod),
Query: query,
Pattern: pattern,
}
}
return pkgMods, err
}
// QueryPattern looks up the module(s) containing at least one package matching // QueryPattern looks up the module(s) containing at least one package matching
// the given pattern at the given version. The results are sorted by module path // the given pattern at the given version. The results are sorted by module path
// length in descending order. // length in descending order. If any proxy provides a non-empty set of candidate
// modules, no further proxies are tried.
// //
// QueryPattern queries modules with package paths up to the first "..." // For wildcard patterns, QueryPattern looks in modules with package paths up to
// in the pattern. For the pattern "example.com/a/b.../c", QueryPattern would // the first "..." in the pattern. For the pattern "example.com/a/b.../c",
// consider prefixes of "example.com/a". If multiple modules have versions // QueryPattern would consider prefixes of "example.com/a".
// that match the query and packages that match the pattern, QueryPattern
// picks the one with the longest module path.
// //
// If any matching package is in the main module, QueryPattern considers only // If any matching package is in the main module, QueryPattern considers only
// the main module and only the version "latest", without checking for other // the main module and only the version "latest", without checking for other
// possible modules. // possible modules.
func QueryPattern(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) ([]QueryResult, error) { //
// QueryPattern always returns at least one QueryResult (which may be only
// modOnly) or a non-nil error.
func QueryPattern(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) (pkgMods []QueryResult, modOnly *QueryResult, err error) {
ctx, span := trace.StartSpan(ctx, "modload.QueryPattern "+pattern+" "+query) ctx, span := trace.StartSpan(ctx, "modload.QueryPattern "+pattern+" "+query)
defer span.Done() defer span.Done()
@ -531,9 +551,13 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
} }
var match func(mod module.Version, root string, isLocal bool) *search.Match var match func(mod module.Version, root string, isLocal bool) *search.Match
matchPattern := search.MatchPattern(pattern)
if i := strings.Index(pattern, "..."); i >= 0 { if i := strings.Index(pattern, "..."); i >= 0 {
base = pathpkg.Dir(pattern[:i+3]) base = pathpkg.Dir(pattern[:i+3])
if base == "." {
return nil, nil, &WildcardInFirstElementError{Pattern: pattern, Query: query}
}
match = func(mod module.Version, root string, isLocal bool) *search.Match { match = func(mod module.Version, root string, isLocal bool) *search.Match {
m := search.NewMatch(pattern) m := search.NewMatch(pattern)
matchPackages(ctx, m, imports.AnyTags(), omitStd, []module.Version{mod}) matchPackages(ctx, m, imports.AnyTags(), omitStd, []module.Version{mod})
@ -555,23 +579,41 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
} }
} }
var queryMatchesMainModule bool
if HasModRoot() { if HasModRoot() {
m := match(Target, modRoot, true) m := match(Target, modRoot, true)
if len(m.Pkgs) > 0 { if len(m.Pkgs) > 0 {
if query != "latest" { if query != "upgrade" && query != "patch" {
return nil, fmt.Errorf("can't query specific version for package %s in the main module (%s)", pattern, Target.Path) return nil, nil, &QueryMatchesPackagesInMainModuleError{
Pattern: pattern,
Query: query,
Packages: m.Pkgs,
}
} }
if err := allowed(ctx, Target); err != nil { if err := allowed(ctx, Target); err != nil {
return nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed: %w", pattern, Target.Path, err) return nil, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed: %w", pattern, Target.Path, err)
} }
return []QueryResult{{ return []QueryResult{{
Mod: Target, Mod: Target,
Rev: &modfetch.RevInfo{Version: Target.Version}, Rev: &modfetch.RevInfo{Version: Target.Version},
Packages: m.Pkgs, Packages: m.Pkgs,
}}, nil }}, nil, nil
} }
if err := firstError(m); err != nil { if err := firstError(m); err != nil {
return nil, err return nil, nil, err
}
if matchPattern(Target.Path) {
queryMatchesMainModule = true
}
if (query == "upgrade" || query == "patch") && queryMatchesMainModule {
if err := allowed(ctx, Target); err == nil {
modOnly = &QueryResult{
Mod: Target,
Rev: &modfetch.RevInfo{Version: Target.Version},
}
}
} }
} }
@ -580,14 +622,23 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
candidateModules = modulePrefixesExcludingTarget(base) candidateModules = modulePrefixesExcludingTarget(base)
) )
if len(candidateModules) == 0 { if len(candidateModules) == 0 {
return nil, &PackageNotInModuleError{ if modOnly != nil {
Mod: Target, return nil, modOnly, nil
Query: query, } else if queryMatchesMainModule {
Pattern: pattern, return nil, nil, &QueryMatchesMainModuleError{
Pattern: pattern,
Query: query,
}
} else {
return nil, nil, &PackageNotInModuleError{
Mod: Target,
Query: query,
Pattern: pattern,
}
} }
} }
err := modfetch.TryProxies(func(proxy string) error { err = modfetch.TryProxies(func(proxy string) error {
queryModule := func(ctx context.Context, path string) (r QueryResult, err error) { queryModule := func(ctx context.Context, path string) (r QueryResult, err error) {
ctx, span := trace.StartSpan(ctx, "modload.QueryPattern.queryModule ["+proxy+"] "+path) ctx, span := trace.StartSpan(ctx, "modload.QueryPattern.queryModule ["+proxy+"] "+path)
defer span.Done() defer span.Done()
@ -606,7 +657,7 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
} }
m := match(r.Mod, root, isLocal) m := match(r.Mod, root, isLocal)
r.Packages = m.Pkgs r.Packages = m.Pkgs
if len(r.Packages) == 0 { if len(r.Packages) == 0 && !matchPattern(path) {
if err := firstError(m); err != nil { if err := firstError(m); err != nil {
return r, err return r, err
} }
@ -620,12 +671,25 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
return r, nil return r, nil
} }
var err error allResults, err := queryPrefixModules(ctx, candidateModules, queryModule)
results, err = queryPrefixModules(ctx, candidateModules, queryModule) results = allResults[:0]
for _, r := range allResults {
if len(r.Packages) == 0 {
modOnly = &r
} else {
results = append(results, r)
}
}
return err return err
}) })
return results, err if queryMatchesMainModule && len(results) == 0 && modOnly == nil && errors.Is(err, fs.ErrNotExist) {
return nil, nil, &QueryMatchesMainModuleError{
Pattern: pattern,
Query: query,
}
}
return results[:len(results):len(results)], modOnly, err
} }
// modulePrefixesExcludingTarget returns all prefixes of path that may plausibly // modulePrefixesExcludingTarget returns all prefixes of path that may plausibly
@ -651,11 +715,6 @@ func modulePrefixesExcludingTarget(path string) []string {
return prefixes return prefixes
} }
type prefixResult struct {
QueryResult
err error
}
func queryPrefixModules(ctx context.Context, candidateModules []string, queryModule func(ctx context.Context, path string) (QueryResult, error)) (found []QueryResult, err error) { func queryPrefixModules(ctx context.Context, candidateModules []string, queryModule func(ctx context.Context, path string) (QueryResult, error)) (found []QueryResult, err error) {
ctx, span := trace.StartSpan(ctx, "modload.queryPrefixModules") ctx, span := trace.StartSpan(ctx, "modload.queryPrefixModules")
defer span.Done() defer span.Done()
@ -686,6 +745,7 @@ func queryPrefixModules(ctx context.Context, candidateModules []string, queryMod
var ( var (
noPackage *PackageNotInModuleError noPackage *PackageNotInModuleError
noVersion *NoMatchingVersionError noVersion *NoMatchingVersionError
noPatchBase *NoPatchBaseError
notExistErr error notExistErr error
) )
for _, r := range results { for _, r := range results {
@ -702,6 +762,10 @@ func queryPrefixModules(ctx context.Context, candidateModules []string, queryMod
if noVersion == nil { if noVersion == nil {
noVersion = rErr noVersion = rErr
} }
case *NoPatchBaseError:
if noPatchBase == nil {
noPatchBase = rErr
}
default: default:
if errors.Is(rErr, fs.ErrNotExist) { if errors.Is(rErr, fs.ErrNotExist) {
if notExistErr == nil { if notExistErr == nil {
@ -733,6 +797,8 @@ func queryPrefixModules(ctx context.Context, candidateModules []string, queryMod
err = noPackage err = noPackage
case noVersion != nil: case noVersion != nil:
err = noVersion err = noVersion
case noPatchBase != nil:
err = noPatchBase
case notExistErr != nil: case notExistErr != nil:
err = notExistErr err = notExistErr
default: default:
@ -757,12 +823,34 @@ type NoMatchingVersionError struct {
func (e *NoMatchingVersionError) Error() string { func (e *NoMatchingVersionError) Error() string {
currentSuffix := "" currentSuffix := ""
if (e.query == "upgrade" || e.query == "patch") && e.current != "" { if (e.query == "upgrade" || e.query == "patch") && e.current != "" && e.current != "none" {
currentSuffix = fmt.Sprintf(" (current version is %s)", e.current) currentSuffix = fmt.Sprintf(" (current version is %s)", e.current)
} }
return fmt.Sprintf("no matching versions for query %q", e.query) + currentSuffix return fmt.Sprintf("no matching versions for query %q", e.query) + currentSuffix
} }
// A NoPatchBaseError indicates that Query was called with the query "patch"
// but with a current version of "" or "none".
type NoPatchBaseError struct {
path string
}
func (e *NoPatchBaseError) Error() string {
return fmt.Sprintf(`can't query version "patch" of module %s: no existing version is required`, e.path)
}
// A WildcardInFirstElementError indicates that a pattern passed to QueryPattern
// had a wildcard in its first path element, and therefore had no pattern-prefix
// modules to search in.
type WildcardInFirstElementError struct {
Pattern string
Query string
}
func (e *WildcardInFirstElementError) Error() string {
return fmt.Sprintf("no modules to query for %s@%s because first path element contains a wildcard", e.Pattern, e.Query)
}
// A PackageNotInModuleError indicates that QueryPattern found a candidate // A PackageNotInModuleError indicates that QueryPattern found a candidate
// module at the requested version, but that module did not contain any packages // module at the requested version, but that module did not contain any packages
// matching the requested pattern. // matching the requested pattern.
@ -989,3 +1077,39 @@ func (rr *replacementRepo) replacementStat(v string) (*modfetch.RevInfo, error)
} }
return rev, nil return rev, nil
} }
// A QueryMatchesMainModuleError indicates that a query requests
// a version of the main module that cannot be satisfied.
// (The main module's version cannot be changed.)
type QueryMatchesMainModuleError struct {
Pattern string
Query string
}
func (e *QueryMatchesMainModuleError) Error() string {
if e.Pattern == Target.Path {
return fmt.Sprintf("can't request version %q of the main module (%s)", e.Query, e.Pattern)
}
return fmt.Sprintf("can't request version %q of pattern %q that includes the main module (%s)", e.Query, e.Pattern, Target.Path)
}
// A QueryMatchesPackagesInMainModuleError indicates that a query cannot be
// satisfied because it matches one or more packages found in the main module.
type QueryMatchesPackagesInMainModuleError struct {
Pattern string
Query string
Packages []string
}
func (e *QueryMatchesPackagesInMainModuleError) Error() string {
if len(e.Packages) > 1 {
return fmt.Sprintf("pattern %s matches %d packages in the main module, so can't request version %s", e.Pattern, len(e.Packages), e.Query)
}
if search.IsMetaPackage(e.Pattern) || strings.Contains(e.Pattern, "...") {
return fmt.Sprintf("pattern %s matches package %s in the main module, so can't request version %s", e.Pattern, e.Packages[0], e.Query)
}
return fmt.Sprintf("package %s is in the main module, so can't request version %s", e.Packages[0], e.Query)
}

View file

@ -156,7 +156,7 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f
isLocal = true isLocal = true
} else { } else {
var err error var err error
needSum := true const needSum = true
root, isLocal, err = fetch(ctx, mod, needSum) root, isLocal, err = fetch(ctx, mod, needSum)
if err != nil { if err != nil {
m.AddError(err) m.AddError(err)
@ -174,3 +174,46 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f
return return
} }
// MatchInModule identifies the packages matching the given pattern within the
// given module version, which does not need to be in the build list or module
// requirement graph.
//
// If m is the zero module.Version, MatchInModule matches the pattern
// against the standard library (std and cmd) in GOROOT/src.
func MatchInModule(ctx context.Context, pattern string, m module.Version, tags map[string]bool) *search.Match {
match := search.NewMatch(pattern)
if m == (module.Version{}) {
matchPackages(ctx, match, tags, includeStd, nil)
}
LoadModFile(ctx)
if !match.IsLiteral() {
matchPackages(ctx, match, tags, omitStd, []module.Version{m})
return match
}
const needSum = true
root, isLocal, err := fetch(ctx, m, needSum)
if err != nil {
match.Errs = []error{err}
return match
}
dir, haveGoFiles, err := dirInModule(pattern, m.Path, root, isLocal)
if err != nil {
match.Errs = []error{err}
return match
}
if haveGoFiles {
if _, _, err := scanDir(dir, tags); err != imports.ErrNoGo {
// ErrNoGo indicates that the directory is not actually a Go package,
// perhaps due to the tags in use. Any other non-nil error indicates a
// problem with one or more of the Go source files, but such an error does
// not stop the package from existing, so it has no impact on matching.
match.Pkgs = []string{pattern}
}
}
return match
}

View file

@ -108,19 +108,21 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (m
node := &modGraphNode{m: m} node := &modGraphNode{m: m}
mu.Lock() mu.Lock()
modGraph[m] = node modGraph[m] = node
if v, ok := min[m.Path]; !ok || reqs.Max(v, m.Version) != v { if m.Version != "none" {
min[m.Path] = m.Version if v, ok := min[m.Path]; !ok || reqs.Max(v, m.Version) != v {
min[m.Path] = m.Version
}
} }
mu.Unlock() mu.Unlock()
required, err := reqs.Required(m) if m.Version != "none" {
if err != nil { required, err := reqs.Required(m)
setErr(node, err) if err != nil {
return setErr(node, err)
} return
node.required = required }
for _, r := range node.required { node.required = required
if r.Version != "none" { for _, r := range node.required {
work.Add(r) work.Add(r)
} }
} }
@ -333,16 +335,36 @@ func Upgrade(target module.Version, reqs Reqs, upgrade ...module.Version) ([]mod
if err != nil { if err != nil {
return nil, err return nil, err
} }
// TODO: Maybe if an error is given,
// rerun with BuildList(upgrade[0], reqs) etc pathInList := make(map[string]bool, len(list))
// to find which ones are the buggy ones. for _, m := range list {
pathInList[m.Path] = true
}
list = append([]module.Version(nil), list...) list = append([]module.Version(nil), list...)
list = append(list, upgrade...)
return BuildList(target, &override{target, list, reqs}) upgradeTo := make(map[string]string, len(upgrade))
for _, u := range upgrade {
if !pathInList[u.Path] {
list = append(list, module.Version{Path: u.Path, Version: "none"})
}
if prev, dup := upgradeTo[u.Path]; dup {
upgradeTo[u.Path] = reqs.Max(prev, u.Version)
} else {
upgradeTo[u.Path] = u.Version
}
}
return buildList(target, &override{target, list, reqs}, func(m module.Version) (module.Version, error) {
if v, ok := upgradeTo[m.Path]; ok {
return module.Version{Path: m.Path, Version: v}, nil
}
return m, nil
})
} }
// Downgrade returns a build list for the target module // Downgrade returns a build list for the target module
// in which the given additional modules are downgraded. // in which the given additional modules are downgraded,
// potentially overriding the requirements of the target.
// //
// The versions to be downgraded may be unreachable from reqs.Latest and // The versions to be downgraded may be unreachable from reqs.Latest and
// reqs.Previous, but the methods of reqs must otherwise handle such versions // reqs.Previous, but the methods of reqs must otherwise handle such versions

View file

@ -229,8 +229,11 @@ E1:
F1: F1:
downgrade A F1: A B1 E1 downgrade A F1: A B1 E1
name: down3 name: downcycle
A: A: A B2
B2: A
B1:
downgrade A B1: A B1
# golang.org/issue/25542. # golang.org/issue/25542.
name: noprev1 name: noprev1
@ -338,6 +341,9 @@ func Test(t *testing.T) {
for _, fn := range fns { for _, fn := range fns {
fn(t) fn(t)
} }
if len(fns) == 0 {
t.Errorf("no functions tested")
}
}) })
} }
} }
@ -485,9 +491,9 @@ func (r reqsMap) Max(v1, v2 string) string {
} }
func (r reqsMap) Upgrade(m module.Version) (module.Version, error) { func (r reqsMap) Upgrade(m module.Version) (module.Version, error) {
var u module.Version u := module.Version{Version: "none"}
for k := range r { for k := range r {
if k.Path == m.Path && u.Version < k.Version && !strings.HasSuffix(k.Version, ".hidden") { if k.Path == m.Path && r.Max(u.Version, k.Version) == k.Version && !strings.HasSuffix(k.Version, ".hidden") {
u = k u = k
} }
} }

View file

@ -150,6 +150,7 @@ In addition to the build flags, the flags handled by 'go test' itself are:
-i -i
Install packages that are dependencies of the test. Install packages that are dependencies of the test.
Do not run the test. Do not run the test.
The -i flag is deprecated. Compiled packages are cached automatically.
-json -json
Convert test output to JSON suitable for automated processing. Convert test output to JSON suitable for automated processing.
@ -640,6 +641,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
b.Init() b.Init()
if cfg.BuildI { if cfg.BuildI {
fmt.Fprint(os.Stderr, "go test: -i flag is deprecated\n")
cfg.BuildV = testV cfg.BuildV = testV
deps := make(map[string]bool) deps := make(map[string]bool)

View file

@ -22,7 +22,10 @@ import (
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg" "cmd/go/internal/cfg"
"cmd/go/internal/search"
"cmd/go/internal/web" "cmd/go/internal/web"
"golang.org/x/mod/module"
) )
// A vcsCmd describes how to use a version control system // A vcsCmd describes how to use a version control system
@ -591,12 +594,146 @@ func FromDir(dir, srcRoot string) (vcs *Cmd, root string, err error) {
} }
if vcsRet != nil { if vcsRet != nil {
if err := checkGOVCS(vcsRet, rootRet); err != nil {
return nil, "", err
}
return vcsRet, rootRet, nil return vcsRet, rootRet, nil
} }
return nil, "", fmt.Errorf("directory %q is not using a known version control system", origDir) return nil, "", fmt.Errorf("directory %q is not using a known version control system", origDir)
} }
// A govcsRule is a single GOVCS rule like private:hg|svn.
type govcsRule struct {
pattern string
allowed []string
}
// A govcsConfig is a full GOVCS configuration.
type govcsConfig []govcsRule
func parseGOVCS(s string) (govcsConfig, error) {
s = strings.TrimSpace(s)
if s == "" {
return nil, nil
}
var cfg govcsConfig
have := make(map[string]string)
for _, item := range strings.Split(s, ",") {
item = strings.TrimSpace(item)
if item == "" {
return nil, fmt.Errorf("empty entry in GOVCS")
}
i := strings.Index(item, ":")
if i < 0 {
return nil, fmt.Errorf("malformed entry in GOVCS (missing colon): %q", item)
}
pattern, list := strings.TrimSpace(item[:i]), strings.TrimSpace(item[i+1:])
if pattern == "" {
return nil, fmt.Errorf("empty pattern in GOVCS: %q", item)
}
if list == "" {
return nil, fmt.Errorf("empty VCS list in GOVCS: %q", item)
}
if search.IsRelativePath(pattern) {
return nil, fmt.Errorf("relative pattern not allowed in GOVCS: %q", pattern)
}
if old := have[pattern]; old != "" {
return nil, fmt.Errorf("unreachable pattern in GOVCS: %q after %q", item, old)
}
have[pattern] = item
allowed := strings.Split(list, "|")
for i, a := range allowed {
a = strings.TrimSpace(a)
if a == "" {
return nil, fmt.Errorf("empty VCS name in GOVCS: %q", item)
}
allowed[i] = a
}
cfg = append(cfg, govcsRule{pattern, allowed})
}
return cfg, nil
}
func (c *govcsConfig) allow(path string, private bool, vcs string) bool {
for _, rule := range *c {
match := false
switch rule.pattern {
case "private":
match = private
case "public":
match = !private
default:
// Note: rule.pattern is known to be comma-free,
// so MatchPrefixPatterns is only matching a single pattern for us.
match = module.MatchPrefixPatterns(rule.pattern, path)
}
if !match {
continue
}
for _, allow := range rule.allowed {
if allow == vcs || allow == "all" {
return true
}
}
return false
}
// By default, nothing is allowed.
return false
}
var (
govcs govcsConfig
govcsErr error
govcsOnce sync.Once
)
// defaultGOVCS is the default setting for GOVCS.
// Setting GOVCS adds entries ahead of these but does not remove them.
// (They are appended to the parsed GOVCS setting.)
//
// The rationale behind allowing only Git and Mercurial is that
// these two systems have had the most attention to issues
// of being run as clients of untrusted servers. In contrast,
// Bazaar, Fossil, and Subversion have primarily been used
// in trusted, authenticated environments and are not as well
// scrutinized as attack surfaces.
//
// See golang.org/issue/41730 for details.
var defaultGOVCS = govcsConfig{
{"private", []string{"all"}},
{"public", []string{"git", "hg"}},
}
func checkGOVCS(vcs *Cmd, root string) error {
if vcs == vcsMod {
// Direct module (proxy protocol) fetches don't
// involve an external version control system
// and are always allowed.
return nil
}
govcsOnce.Do(func() {
govcs, govcsErr = parseGOVCS(os.Getenv("GOVCS"))
govcs = append(govcs, defaultGOVCS...)
})
if govcsErr != nil {
return govcsErr
}
private := module.MatchPrefixPatterns(cfg.GOPRIVATE, root)
if !govcs.allow(root, private, vcs.Cmd) {
what := "public"
if private {
what = "private"
}
return fmt.Errorf("GOVCS disallows using %s for %s %s", vcs.Cmd, what, root)
}
return nil
}
// CheckNested checks for an incorrectly-nested VCS-inside-VCS // CheckNested checks for an incorrectly-nested VCS-inside-VCS
// situation for dir, checking parents up until srcRoot. // situation for dir, checking parents up until srcRoot.
func CheckNested(vcs *Cmd, dir, srcRoot string) error { func CheckNested(vcs *Cmd, dir, srcRoot string) error {
@ -733,6 +870,9 @@ func repoRootFromVCSPaths(importPath string, security web.SecurityMode, vcsPaths
if vcs == nil { if vcs == nil {
return nil, fmt.Errorf("unknown version control system %q", match["vcs"]) return nil, fmt.Errorf("unknown version control system %q", match["vcs"])
} }
if err := checkGOVCS(vcs, match["root"]); err != nil {
return nil, err
}
var repoURL string var repoURL string
if !srv.schemelessRepo { if !srv.schemelessRepo {
repoURL = match["repo"] repoURL = match["repo"]
@ -857,6 +997,10 @@ func repoRootForImportDynamic(importPath string, mod ModuleMode, security web.Se
} }
} }
if err := checkGOVCS(vcs, mmi.Prefix); err != nil {
return nil, err
}
rr := &RepoRoot{ rr := &RepoRoot{
Repo: mmi.RepoRoot, Repo: mmi.RepoRoot,
Root: mmi.Prefix, Root: mmi.Prefix,

View file

@ -11,11 +11,20 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
"cmd/go/internal/web" "cmd/go/internal/web"
) )
func init() {
// GOVCS defaults to public:git|hg,private:all,
// which breaks many tests here - they can't use non-git, non-hg VCS at all!
// Change to fully permissive.
// The tests of the GOVCS setting itself are in ../../testdata/script/govcs.txt.
os.Setenv("GOVCS", "*:all")
}
// Test that RepoRootForImportPath determines the correct RepoRoot for a given importPath. // Test that RepoRootForImportPath determines the correct RepoRoot for a given importPath.
// TODO(cmang): Add tests for SVN and BZR. // TODO(cmang): Add tests for SVN and BZR.
func TestRepoRootForImportPath(t *testing.T) { func TestRepoRootForImportPath(t *testing.T) {
@ -473,3 +482,98 @@ func TestValidateRepoRoot(t *testing.T) {
} }
} }
} }
var govcsTests = []struct {
govcs string
path string
vcs string
ok bool
}{
{"private:all", "is-public.com/foo", "zzz", false},
{"private:all", "is-private.com/foo", "zzz", true},
{"public:all", "is-public.com/foo", "zzz", true},
{"public:all", "is-private.com/foo", "zzz", false},
{"public:all,private:none", "is-public.com/foo", "zzz", true},
{"public:all,private:none", "is-private.com/foo", "zzz", false},
{"*:all", "is-public.com/foo", "zzz", true},
{"golang.org:git", "golang.org/x/text", "zzz", false},
{"golang.org:git", "golang.org/x/text", "git", true},
{"golang.org:zzz", "golang.org/x/text", "zzz", true},
{"golang.org:zzz", "golang.org/x/text", "git", false},
{"golang.org:zzz", "golang.org/x/text", "zzz", true},
{"golang.org:zzz", "golang.org/x/text", "git", false},
{"golang.org:git|hg", "golang.org/x/text", "hg", true},
{"golang.org:git|hg", "golang.org/x/text", "git", true},
{"golang.org:git|hg", "golang.org/x/text", "zzz", false},
{"golang.org:all", "golang.org/x/text", "hg", true},
{"golang.org:all", "golang.org/x/text", "git", true},
{"golang.org:all", "golang.org/x/text", "zzz", true},
{"other.xyz/p:none,golang.org/x:git", "other.xyz/p/x", "git", false},
{"other.xyz/p:none,golang.org/x:git", "unexpected.com", "git", false},
{"other.xyz/p:none,golang.org/x:git", "golang.org/x/text", "zzz", false},
{"other.xyz/p:none,golang.org/x:git", "golang.org/x/text", "git", true},
{"other.xyz/p:none,golang.org/x:zzz", "golang.org/x/text", "zzz", true},
{"other.xyz/p:none,golang.org/x:zzz", "golang.org/x/text", "git", false},
{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "hg", true},
{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "git", true},
{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "zzz", false},
{"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "hg", true},
{"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "git", true},
{"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "zzz", true},
{"other.xyz/p:none,golang.org/x:git", "golang.org/y/text", "zzz", false},
{"other.xyz/p:none,golang.org/x:git", "golang.org/y/text", "git", false},
{"other.xyz/p:none,golang.org/x:zzz", "golang.org/y/text", "zzz", false},
{"other.xyz/p:none,golang.org/x:zzz", "golang.org/y/text", "git", false},
{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "hg", false},
{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "git", false},
{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "zzz", false},
{"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "hg", false},
{"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "git", false},
{"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "zzz", false},
}
func TestGOVCS(t *testing.T) {
for _, tt := range govcsTests {
cfg, err := parseGOVCS(tt.govcs)
if err != nil {
t.Errorf("parseGOVCS(%q): %v", tt.govcs, err)
continue
}
private := strings.HasPrefix(tt.path, "is-private")
ok := cfg.allow(tt.path, private, tt.vcs)
if ok != tt.ok {
t.Errorf("parseGOVCS(%q).allow(%q, %v, %q) = %v, want %v",
tt.govcs, tt.path, private, tt.vcs, ok, tt.ok)
}
}
}
var govcsErrors = []struct {
s string
err string
}{
{`,`, `empty entry in GOVCS`},
{`,x`, `empty entry in GOVCS`},
{`x,`, `malformed entry in GOVCS (missing colon): "x"`},
{`x:y,`, `empty entry in GOVCS`},
{`x`, `malformed entry in GOVCS (missing colon): "x"`},
{`x:`, `empty VCS list in GOVCS: "x:"`},
{`x:|`, `empty VCS name in GOVCS: "x:|"`},
{`x:y|`, `empty VCS name in GOVCS: "x:y|"`},
{`x:|y`, `empty VCS name in GOVCS: "x:|y"`},
{`x:y,z:`, `empty VCS list in GOVCS: "z:"`},
{`x:y,z:|`, `empty VCS name in GOVCS: "z:|"`},
{`x:y,z:|w`, `empty VCS name in GOVCS: "z:|w"`},
{`x:y,z:w|`, `empty VCS name in GOVCS: "z:w|"`},
{`x:y,z:w||v`, `empty VCS name in GOVCS: "z:w||v"`},
{`x:y,x:z`, `unreachable pattern in GOVCS: "x:z" after "x:y"`},
}
func TestGOVCSErrors(t *testing.T) {
for _, tt := range govcsErrors {
_, err := parseGOVCS(tt.s)
if err == nil || !strings.Contains(err.Error(), tt.err) {
t.Errorf("parseGOVCS(%s): err=%v, want %v", tt.s, err, tt.err)
}
}
}

View file

@ -93,11 +93,12 @@ type Action struct {
output []byte // output redirect buffer (nil means use b.Print) output []byte // output redirect buffer (nil means use b.Print)
// Execution state. // Execution state.
pending int // number of deps yet to complete pending int // number of deps yet to complete
priority int // relative execution priority priority int // relative execution priority
Failed bool // whether the action failed Failed bool // whether the action failed
json *actionJSON // action graph information json *actionJSON // action graph information
traceSpan *trace.Span nonGoOverlay map[string]string // map from non-.go source files to copied files in objdir. Nil if no overlay is used.
traceSpan *trace.Span
} }
// BuildActionID returns the action ID section of a's build ID. // BuildActionID returns the action ID section of a's build ID.

View file

@ -31,7 +31,7 @@ import (
) )
var CmdBuild = &base.Command{ var CmdBuild = &base.Command{
UsageLine: "go build [-o output] [-i] [build flags] [packages]", UsageLine: "go build [-o output] [build flags] [packages]",
Short: "compile packages and dependencies", Short: "compile packages and dependencies",
Long: ` Long: `
Build compiles the packages named by the import paths, Build compiles the packages named by the import paths,
@ -59,6 +59,7 @@ ends with a slash or backslash, then any resulting executables
will be written to that directory. will be written to that directory.
The -i flag installs the packages that are dependencies of the target. The -i flag installs the packages that are dependencies of the target.
The -i flag is deprecated. Compiled packages are cached automatically.
The build flags are shared by the build, clean, get, install, list, run, The build flags are shared by the build, clean, get, install, list, run,
and test commands: and test commands:
@ -381,6 +382,7 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) {
depMode := ModeBuild depMode := ModeBuild
if cfg.BuildI { if cfg.BuildI {
depMode = ModeInstall depMode = ModeInstall
fmt.Fprint(os.Stderr, "go build: -i flag is deprecated\n")
} }
pkgs = omitTestOnly(pkgsFilter(load.Packages(ctx, args))) pkgs = omitTestOnly(pkgsFilter(load.Packages(ctx, args)))
@ -444,7 +446,7 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) {
} }
var CmdInstall = &base.Command{ var CmdInstall = &base.Command{
UsageLine: "go install [-i] [build flags] [packages]", UsageLine: "go install [build flags] [packages]",
Short: "compile and install packages and dependencies", Short: "compile and install packages and dependencies",
Long: ` Long: `
Install compiles and installs the packages named by the import paths. Install compiles and installs the packages named by the import paths.
@ -486,6 +488,7 @@ directory $GOPATH/pkg/$GOOS_$GOARCH. When module-aware mode is enabled,
other packages are built and cached but not installed. other packages are built and cached but not installed.
The -i flag installs the dependencies of the named packages as well. The -i flag installs the dependencies of the named packages as well.
The -i flag is deprecated. Compiled packages are cached automatically.
For more about the build flags, see 'go help build'. For more about the build flags, see 'go help build'.
For more about specifying packages, see 'go help packages'. For more about specifying packages, see 'go help packages'.
@ -551,14 +554,35 @@ func libname(args []string, pkgs []*load.Package) (string, error) {
} }
func runInstall(ctx context.Context, cmd *base.Command, args []string) { func runInstall(ctx context.Context, cmd *base.Command, args []string) {
// TODO(golang.org/issue/41696): print a deprecation message for the -i flag
// whenever it's set (or just remove it). For now, we don't print a message
// if all named packages are in GOROOT. cmd/dist (run by make.bash) uses
// 'go install -i' when bootstrapping, and we don't want to show deprecation
// messages in that case.
for _, arg := range args { for _, arg := range args {
if strings.Contains(arg, "@") && !build.IsLocalImport(arg) && !filepath.IsAbs(arg) { if strings.Contains(arg, "@") && !build.IsLocalImport(arg) && !filepath.IsAbs(arg) {
if cfg.BuildI {
fmt.Fprint(os.Stderr, "go install: -i flag is deprecated\n")
}
installOutsideModule(ctx, args) installOutsideModule(ctx, args)
return return
} }
} }
BuildInit() BuildInit()
InstallPackages(ctx, args, load.PackagesForBuild(ctx, args)) pkgs := load.PackagesForBuild(ctx, args)
if cfg.BuildI {
allGoroot := true
for _, pkg := range pkgs {
if !pkg.Goroot {
allGoroot = false
break
}
}
if !allGoroot {
fmt.Fprint(os.Stderr, "go install: -i flag is deprecated\n")
}
}
InstallPackages(ctx, args, pkgs)
} }
// omitTestOnly returns pkgs with test-only packages removed. // omitTestOnly returns pkgs with test-only packages removed.
@ -741,7 +765,8 @@ func installOutsideModule(ctx context.Context, args []string) {
// Don't check for retractions if a specific revision is requested. // Don't check for retractions if a specific revision is requested.
allowed = nil allowed = nil
} }
qrs, err := modload.QueryPattern(ctx, patterns[0], version, modload.Selected, allowed) noneSelected := func(path string) (version string) { return "none" }
qrs, err := modload.QueryPackages(ctx, patterns[0], version, noneSelected, allowed)
if err != nil { if err != nil {
base.Fatalf("go install %s: %v", args[0], err) base.Fatalf("go install %s: %v", args[0], err)
} }
@ -765,10 +790,12 @@ func installOutsideModule(ctx context.Context, args []string) {
base.Fatalf(directiveFmt, args[0], installMod, "exclude") base.Fatalf(directiveFmt, args[0], installMod, "exclude")
} }
// Initialize the build list using a dummy main module that requires the // Since we are in NoRoot mode, the build list initially contains only
// module providing the packages on the command line. // the dummy command-line-arguments module. Add a requirement on the
target := module.Version{Path: "go-install-target"} // module that provides the packages named on the command line.
modload.SetBuildList([]module.Version{target, installMod}) if err := modload.EditBuildList(ctx, nil, []module.Version{installMod}); err != nil {
base.Fatalf("go install %s: %v", args[0], err)
}
// Load packages for all arguments. Ignore non-main packages. // Load packages for all arguments. Ignore non-main packages.
// Print a warning if an argument contains "..." and matches no main packages. // Print a warning if an argument contains "..." and matches no main packages.

View file

@ -15,6 +15,7 @@ import (
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cache" "cmd/go/internal/cache"
"cmd/go/internal/cfg" "cmd/go/internal/cfg"
"cmd/go/internal/fsys"
"cmd/go/internal/str" "cmd/go/internal/str"
"cmd/internal/buildid" "cmd/internal/buildid"
) )
@ -375,6 +376,7 @@ func (b *Builder) buildID(file string) string {
// fileHash returns the content hash of the named file. // fileHash returns the content hash of the named file.
func (b *Builder) fileHash(file string) string { func (b *Builder) fileHash(file string) string {
file, _ = fsys.OverlayPath(file)
sum, err := cache.FileHash(file) sum, err := cache.FileHash(file)
if err != nil { if err != nil {
return "" return ""

View file

@ -8,6 +8,7 @@ package work
import ( import (
"bytes" "bytes"
"cmd/go/internal/fsys"
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
@ -537,6 +538,34 @@ func (b *Builder) build(ctx context.Context, a *Action) (err error) {
} }
} }
// Compute overlays for .c/.cc/.h/etc. and if there are any overlays
// put correct contents of all those files in the objdir, to ensure
// the correct headers are included. nonGoOverlay is the overlay that
// points from nongo files to the copied files in objdir.
nonGoFileLists := [][]string{a.Package.CFiles, a.Package.SFiles, a.Package.CXXFiles, a.Package.HFiles, a.Package.FFiles}
OverlayLoop:
for _, fs := range nonGoFileLists {
for _, f := range fs {
if _, ok := fsys.OverlayPath(mkAbs(p.Dir, f)); ok {
a.nonGoOverlay = make(map[string]string)
break OverlayLoop
}
}
}
if a.nonGoOverlay != nil {
for _, fs := range nonGoFileLists {
for i := range fs {
from := mkAbs(p.Dir, fs[i])
opath, _ := fsys.OverlayPath(from)
dst := objdir + filepath.Base(fs[i])
if err := b.copyFile(dst, opath, 0666, false); err != nil {
return err
}
a.nonGoOverlay[from] = dst
}
}
}
// Run SWIG on each .swig and .swigcxx file. // Run SWIG on each .swig and .swigcxx file.
// Each run will generate two files, a .go file and a .c or .cxx file. // Each run will generate two files, a .go file and a .c or .cxx file.
// The .go file will use import "C" and is to be processed by cgo. // The .go file will use import "C" and is to be processed by cgo.
@ -737,7 +766,7 @@ func (b *Builder) build(ctx context.Context, a *Action) (err error) {
} }
if err != nil { if err != nil {
if p.Module != nil && !allowedVersion(p.Module.GoVersion) { if p.Module != nil && !allowedVersion(p.Module.GoVersion) {
b.showOutput(a, a.Package.Dir, a.Package.Desc(), "note: module requires Go "+p.Module.GoVersion) b.showOutput(a, a.Package.Dir, a.Package.Desc(), "note: module requires Go "+p.Module.GoVersion+"\n")
} }
return err return err
} }
@ -2242,8 +2271,6 @@ func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []s
// when -trimpath is enabled. // when -trimpath is enabled.
if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") { if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") {
if cfg.BuildTrimpath { if cfg.BuildTrimpath {
// TODO(#39958): handle overlays
// Keep in sync with Action.trimpath. // Keep in sync with Action.trimpath.
// The trimmed paths are a little different, but we need to trim in the // The trimmed paths are a little different, but we need to trim in the
// same situations. // same situations.
@ -2270,7 +2297,11 @@ func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []s
} }
} }
output, err := b.runOut(a, filepath.Dir(file), b.cCompilerEnv(), compiler, flags, "-o", outfile, "-c", filepath.Base(file)) overlayPath := file
if p, ok := a.nonGoOverlay[overlayPath]; ok {
overlayPath = p
}
output, err := b.runOut(a, filepath.Dir(overlayPath), b.cCompilerEnv(), compiler, flags, "-o", outfile, "-c", filepath.Base(overlayPath))
if len(output) > 0 { if len(output) > 0 {
// On FreeBSD 11, when we pass -g to clang 3.8 it // On FreeBSD 11, when we pass -g to clang 3.8 it
// invokes its internal assembler with -dwarf-version=2. // invokes its internal assembler with -dwarf-version=2.
@ -2313,7 +2344,8 @@ func (b *Builder) gccld(a *Action, p *load.Package, objdir, outfile string, flag
cmdargs := []interface{}{cmd, "-o", outfile, objs, flags} cmdargs := []interface{}{cmd, "-o", outfile, objs, flags}
dir := p.Dir dir := p.Dir
out, err := b.runOut(a, dir, b.cCompilerEnv(), cmdargs...) out, err := b.runOut(a, base.Cwd, b.cCompilerEnv(), cmdargs...)
if len(out) > 0 { if len(out) > 0 {
// Filter out useless linker warnings caused by bugs outside Go. // Filter out useless linker warnings caused by bugs outside Go.
// See also cmd/link/internal/ld's hostlink method. // See also cmd/link/internal/ld's hostlink method.
@ -2641,7 +2673,8 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
cgoLDFLAGS = append([]string{"-fsanitize=memory"}, cgoLDFLAGS...) cgoLDFLAGS = append([]string{"-fsanitize=memory"}, cgoLDFLAGS...)
} }
// Allows including _cgo_export.h from .[ch] files in the package. // Allows including _cgo_export.h, as well as the user's .h files,
// from .[ch] files in the package.
cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", objdir) cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", objdir)
// cgo // cgo
@ -2698,7 +2731,23 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
cgoflags = append(cgoflags, "-exportheader="+objdir+"_cgo_install.h") cgoflags = append(cgoflags, "-exportheader="+objdir+"_cgo_install.h")
} }
if err := b.run(a, p.Dir, p.ImportPath, cgoenv, cfg.BuildToolexec, cgoExe, "-objdir", objdir, "-importpath", p.ImportPath, cgoflags, "--", cgoCPPFLAGS, cgoCFLAGS, cgofiles); err != nil { execdir := p.Dir
// Rewrite overlaid paths in cgo files.
// cgo adds //line and #line pragmas in generated files with these paths.
var trimpath []string
for i := range cgofiles {
path := mkAbs(p.Dir, cgofiles[i])
if opath, ok := fsys.OverlayPath(path); ok {
cgofiles[i] = opath
trimpath = append(trimpath, opath+"=>"+path)
}
}
if len(trimpath) > 0 {
cgoflags = append(cgoflags, "-trimpath", strings.Join(trimpath, ";"))
}
if err := b.run(a, execdir, p.ImportPath, cgoenv, cfg.BuildToolexec, cgoExe, "-objdir", objdir, "-importpath", p.ImportPath, cgoflags, "--", cgoCPPFLAGS, cgoCFLAGS, cgofiles); err != nil {
return nil, nil, err return nil, nil, err
} }
outGo = append(outGo, gofiles...) outGo = append(outGo, gofiles...)
@ -2779,6 +2828,81 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
noCompiler() noCompiler()
} }
// Double check the //go:cgo_ldflag comments in the generated files.
// The compiler only permits such comments in files whose base name
// starts with "_cgo_". Make sure that the comments in those files
// are safe. This is a backstop against people somehow smuggling
// such a comment into a file generated by cgo.
if cfg.BuildToolchainName == "gc" && !cfg.BuildN {
var flags []string
for _, f := range outGo {
if !strings.HasPrefix(filepath.Base(f), "_cgo_") {
continue
}
src, err := ioutil.ReadFile(f)
if err != nil {
return nil, nil, err
}
const cgoLdflag = "//go:cgo_ldflag"
idx := bytes.Index(src, []byte(cgoLdflag))
for idx >= 0 {
// We are looking at //go:cgo_ldflag.
// Find start of line.
start := bytes.LastIndex(src[:idx], []byte("\n"))
if start == -1 {
start = 0
}
// Find end of line.
end := bytes.Index(src[idx:], []byte("\n"))
if end == -1 {
end = len(src)
} else {
end += idx
}
// Check for first line comment in line.
// We don't worry about /* */ comments,
// which normally won't appear in files
// generated by cgo.
commentStart := bytes.Index(src[start:], []byte("//"))
commentStart += start
// If that line comment is //go:cgo_ldflag,
// it's a match.
if bytes.HasPrefix(src[commentStart:], []byte(cgoLdflag)) {
// Pull out the flag, and unquote it.
// This is what the compiler does.
flag := string(src[idx+len(cgoLdflag) : end])
flag = strings.TrimSpace(flag)
flag = strings.Trim(flag, `"`)
flags = append(flags, flag)
}
src = src[end:]
idx = bytes.Index(src, []byte(cgoLdflag))
}
}
// We expect to find the contents of cgoLDFLAGS in flags.
if len(cgoLDFLAGS) > 0 {
outer:
for i := range flags {
for j, f := range cgoLDFLAGS {
if f != flags[i+j] {
continue outer
}
}
flags = append(flags[:i], flags[i+len(cgoLDFLAGS):]...)
break
}
}
if err := checkLinkerFlags("LDFLAGS", "go:cgo_ldflag", flags); err != nil {
return nil, nil, err
}
}
return outGo, outObj, nil return outGo, outObj, nil
} }
@ -2792,7 +2916,7 @@ func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe
return err return err
} }
linkobj := str.StringList(ofile, outObj, p.SysoFiles) linkobj := str.StringList(ofile, outObj, mkAbsFiles(p.Dir, p.SysoFiles))
dynobj := objdir + "_cgo_.o" dynobj := objdir + "_cgo_.o"
// we need to use -pie for Linux/ARM to get accurate imported sym // we need to use -pie for Linux/ARM to get accurate imported sym
@ -2817,7 +2941,7 @@ func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe
if p.Standard && p.ImportPath == "runtime/cgo" { if p.Standard && p.ImportPath == "runtime/cgo" {
cgoflags = []string{"-dynlinker"} // record path to dynamic linker cgoflags = []string{"-dynlinker"} // record path to dynamic linker
} }
return b.run(a, p.Dir, p.ImportPath, b.cCompilerEnv(), cfg.BuildToolexec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags) return b.run(a, base.Cwd, p.ImportPath, b.cCompilerEnv(), cfg.BuildToolexec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags)
} }
// Run SWIG on all SWIG input files. // Run SWIG on all SWIG input files.

View file

@ -262,7 +262,7 @@ func (a *Action) trimpath() string {
if len(objdir) > 1 && objdir[len(objdir)-1] == filepath.Separator { if len(objdir) > 1 && objdir[len(objdir)-1] == filepath.Separator {
objdir = objdir[:len(objdir)-1] objdir = objdir[:len(objdir)-1]
} }
rewrite := objdir + "=>" rewrite := ""
rewriteDir := a.Package.Dir rewriteDir := a.Package.Dir
if cfg.BuildTrimpath { if cfg.BuildTrimpath {
@ -271,21 +271,54 @@ func (a *Action) trimpath() string {
} else { } else {
rewriteDir = a.Package.ImportPath rewriteDir = a.Package.ImportPath
} }
rewrite += ";" + a.Package.Dir + "=>" + rewriteDir rewrite += a.Package.Dir + "=>" + rewriteDir + ";"
} }
// Add rewrites for overlays. The 'from' and 'to' paths in overlays don't need to have // Add rewrites for overlays. The 'from' and 'to' paths in overlays don't need to have
// same basename, so go from the overlay contents file path (passed to the compiler) // same basename, so go from the overlay contents file path (passed to the compiler)
// to the path the disk path would be rewritten to. // to the path the disk path would be rewritten to.
cgoFiles := make(map[string]bool)
for _, f := range a.Package.CgoFiles {
cgoFiles[f] = true
}
// TODO(matloob): Higher up in the stack, when the logic for deciding when to make copies
// of c/c++/m/f/hfiles is consolidated, use the same logic that Build uses to determine
// whether to create the copies in objdir to decide whether to rewrite objdir to the
// package directory here.
var overlayNonGoRewrites string // rewrites for non-go files
hasCgoOverlay := false
if fsys.OverlayFile != "" { if fsys.OverlayFile != "" {
for _, filename := range a.Package.AllFiles() { for _, filename := range a.Package.AllFiles() {
overlayPath, ok := fsys.OverlayPath(filepath.Join(a.Package.Dir, filename)) path := filename
if !ok { if !filepath.IsAbs(path) {
continue path = filepath.Join(a.Package.Dir, path)
}
base := filepath.Base(path)
isGo := strings.HasSuffix(filename, ".go") || strings.HasSuffix(filename, ".s")
isCgo := cgoFiles[filename] || !isGo
overlayPath, isOverlay := fsys.OverlayPath(path)
if isCgo && isOverlay {
hasCgoOverlay = true
}
if !isCgo && isOverlay {
rewrite += overlayPath + "=>" + filepath.Join(rewriteDir, base) + ";"
} else if isCgo {
// Generate rewrites for non-Go files copied to files in objdir.
if filepath.Dir(path) == a.Package.Dir {
// This is a file copied to objdir.
overlayNonGoRewrites += filepath.Join(objdir, base) + "=>" + filepath.Join(rewriteDir, base) + ";"
}
} else {
// Non-overlay Go files are covered by the a.Package.Dir rewrite rule above.
} }
rewrite += ";" + overlayPath + "=>" + filepath.Join(rewriteDir, filename)
} }
} }
if hasCgoOverlay {
rewrite += overlayNonGoRewrites
}
rewrite += objdir + "=>"
return rewrite return rewrite
} }
@ -337,9 +370,10 @@ func (gcToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error)
var ofiles []string var ofiles []string
for _, sfile := range sfiles { for _, sfile := range sfiles {
overlayPath, _ := fsys.OverlayPath(mkAbs(p.Dir, sfile))
ofile := a.Objdir + sfile[:len(sfile)-len(".s")] + ".o" ofile := a.Objdir + sfile[:len(sfile)-len(".s")] + ".o"
ofiles = append(ofiles, ofile) ofiles = append(ofiles, ofile)
args1 := append(args, "-o", ofile, mkAbs(p.Dir, sfile)) args1 := append(args, "-o", ofile, overlayPath)
if err := b.run(a, p.Dir, p.ImportPath, nil, args1...); err != nil { if err := b.run(a, p.Dir, p.ImportPath, nil, args1...); err != nil {
return nil, err return nil, err
} }
@ -355,7 +389,8 @@ func (gcToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, erro
if p.ImportPath == "runtime/cgo" && strings.HasPrefix(sfile, "gcc_") { if p.ImportPath == "runtime/cgo" && strings.HasPrefix(sfile, "gcc_") {
continue continue
} }
args = append(args, mkAbs(p.Dir, sfile)) op, _ := fsys.OverlayPath(mkAbs(p.Dir, sfile))
args = append(args, op)
} }
// Supply an empty go_asm.h as if the compiler had been run. // Supply an empty go_asm.h as if the compiler had been run.

View file

@ -199,7 +199,7 @@ func (tools gccgoToolchain) asm(b *Builder, a *Action, sfiles []string) ([]strin
base := filepath.Base(sfile) base := filepath.Base(sfile)
ofile := a.Objdir + base[:len(base)-len(".s")] + ".o" ofile := a.Objdir + base[:len(base)-len(".s")] + ".o"
ofiles = append(ofiles, ofile) ofiles = append(ofiles, ofile)
sfile = mkAbs(p.Dir, sfile) sfile, _ = fsys.OverlayPath(mkAbs(p.Dir, sfile))
defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch} defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" { if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" {
defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath) defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath)

View file

@ -42,8 +42,8 @@ import (
var re = lazyregexp.New var re = lazyregexp.New
var validCompilerFlags = []*lazyregexp.Regexp{ var validCompilerFlags = []*lazyregexp.Regexp{
re(`-D([A-Za-z_].*)`), re(`-D([A-Za-z_][A-Za-z0-9_]*)(=[^@\-]*)?`),
re(`-U([A-Za-z_]*)`), re(`-U([A-Za-z_][A-Za-z0-9_]*)`),
re(`-F([^@\-].*)`), re(`-F([^@\-].*)`),
re(`-I([^@\-].*)`), re(`-I([^@\-].*)`),
re(`-O`), re(`-O`),
@ -51,8 +51,8 @@ var validCompilerFlags = []*lazyregexp.Regexp{
re(`-W`), re(`-W`),
re(`-W([^@,]+)`), // -Wall but not -Wa,-foo. re(`-W([^@,]+)`), // -Wall but not -Wa,-foo.
re(`-Wa,-mbig-obj`), re(`-Wa,-mbig-obj`),
re(`-Wp,-D([A-Za-z_].*)`), re(`-Wp,-D([A-Za-z_][A-Za-z0-9_]*)(=[^@,\-]*)?`),
re(`-Wp,-U([A-Za-z_]*)`), re(`-Wp,-U([A-Za-z_][A-Za-z0-9_]*)`),
re(`-ansi`), re(`-ansi`),
re(`-f(no-)?asynchronous-unwind-tables`), re(`-f(no-)?asynchronous-unwind-tables`),
re(`-f(no-)?blocks`), re(`-f(no-)?blocks`),
@ -179,7 +179,7 @@ var validLinkerFlags = []*lazyregexp.Regexp{
re(`-Wl,-berok`), re(`-Wl,-berok`),
re(`-Wl,-Bstatic`), re(`-Wl,-Bstatic`),
re(`-Wl,-Bsymbolic-functions`), re(`-Wl,-Bsymbolic-functions`),
re(`-WL,-O([^@,\-][^,]*)?`), re(`-Wl,-O([^@,\-][^,]*)?`),
re(`-Wl,-d[ny]`), re(`-Wl,-d[ny]`),
re(`-Wl,--disable-new-dtags`), re(`-Wl,--disable-new-dtags`),
re(`-Wl,-e[=,][a-zA-Z0-9]*`), re(`-Wl,-e[=,][a-zA-Z0-9]*`),

View file

@ -13,6 +13,7 @@ var goodCompilerFlags = [][]string{
{"-DFOO"}, {"-DFOO"},
{"-Dfoo=bar"}, {"-Dfoo=bar"},
{"-Ufoo"}, {"-Ufoo"},
{"-Ufoo1"},
{"-F/Qt"}, {"-F/Qt"},
{"-I/"}, {"-I/"},
{"-I/etc/passwd"}, {"-I/etc/passwd"},
@ -24,6 +25,8 @@ var goodCompilerFlags = [][]string{
{"-Wall"}, {"-Wall"},
{"-Wp,-Dfoo=bar"}, {"-Wp,-Dfoo=bar"},
{"-Wp,-Ufoo"}, {"-Wp,-Ufoo"},
{"-Wp,-Dfoo1"},
{"-Wp,-Ufoo1"},
{"-fobjc-arc"}, {"-fobjc-arc"},
{"-fno-objc-arc"}, {"-fno-objc-arc"},
{"-fomit-frame-pointer"}, {"-fomit-frame-pointer"},
@ -80,6 +83,8 @@ var badCompilerFlags = [][]string{
{"-O@1"}, {"-O@1"},
{"-Wa,-foo"}, {"-Wa,-foo"},
{"-W@foo"}, {"-W@foo"},
{"-Wp,-DX,-D@X"},
{"-Wp,-UX,-U@X"},
{"-g@gdb"}, {"-g@gdb"},
{"-g-gdb"}, {"-g-gdb"},
{"-march=@dawn"}, {"-march=@dawn"},

View file

@ -75,10 +75,11 @@ func init() {
modload.HelpModules, modload.HelpModules,
modget.HelpModuleGet, modget.HelpModuleGet,
modfetch.HelpModuleAuth, modfetch.HelpModuleAuth,
modfetch.HelpModulePrivate,
help.HelpPackages, help.HelpPackages,
modfetch.HelpPrivate,
test.HelpTestflag, test.HelpTestflag,
test.HelpTestfunc, test.HelpTestfunc,
modget.HelpVCS,
} }
} }

View file

@ -135,6 +135,7 @@ func (ts *testScript) setup() {
"GOSUMDB=" + testSumDBVerifierKey, "GOSUMDB=" + testSumDBVerifierKey,
"GONOPROXY=", "GONOPROXY=",
"GONOSUMDB=", "GONOSUMDB=",
"GOVCS=*:all",
"PWD=" + ts.cd, "PWD=" + ts.cd,
tempEnvName() + "=" + filepath.Join(ts.workdir, "tmp"), tempEnvName() + "=" + filepath.Join(ts.workdir, "tmp"),
"devnull=" + os.DevNull, "devnull=" + os.DevNull,
@ -1255,7 +1256,12 @@ func (ts *testScript) parse(line string) command {
if cmd.name != "" { if cmd.name != "" {
cmd.args = append(cmd.args, arg) cmd.args = append(cmd.args, arg)
isRegexp = false // Commands take only one regexp argument, so no subsequent args are regexps. // Commands take only one regexp argument (after the optional flags),
// so no subsequent args are regexps. Liberally assume an argument that
// starts with a '-' is a flag.
if len(arg) == 0 || arg[0] != '-' {
isRegexp = false
}
return return
} }

View file

@ -0,0 +1,10 @@
-- .mod --
module example.com/retract/ambiguous/nested
go 1.16
retract v1.9.0-bad // nested modules are bad
-- .info --
{"Version":"v1.9.0-bad"}
-- nested.go --
package nested

View file

@ -0,0 +1,12 @@
-- .mod --
module example.com/retract/ambiguous/other
go 1.16
require example.com/retract/ambiguous v1.0.0
-- .info --
{"Version":"v1.0.0"}
-- other.go --
package other
import _ "example.com/retract/ambiguous/nested"

View file

@ -0,0 +1,9 @@
-- .mod --
module example.com/retract/ambiguous
go 1.16
-- .info --
{"Version":"v1.0.0"}
-- nested/nested.go --
package nested

View file

@ -0,0 +1,24 @@
# Check that deprecation warnings are printed when the -i flag is used.
# TODO(golang.org/issue/41696): remove the -i flag after Go 1.16, and this test.
go build -n -i
stderr '^go build: -i flag is deprecated$'
go install -n -i
stderr '^go install: -i flag is deprecated$'
go test -n -i
stderr '^go test: -i flag is deprecated$'
# 'go clean -i' should not print a deprecation warning.
# It will continue working.
go clean -i .
! stderr .
-- go.mod --
module m
go 1.16
-- m.go --
package m

View file

@ -1,9 +1,11 @@
[short] skip [short] skip
# Test building in overlays. # Test building in overlays.
# TODO(matloob): add a test case where the destination file in the replace map # TODO(#39958): add a test case where the destination file in the replace map
# isn't a go file. Either completely exclude that case in fs.IsDirWithGoFiles # isn't a go file. Either completely exclude that case in fs.IsDirWithGoFiles
# if the compiler doesn't allow it, or test that it works all the way. # if the compiler doesn't allow it, or test that it works all the way.
# TODO(#39958): add a test that both gc and gccgo assembly files can include .h
# files.
# The main package (m) is contained in an overlay. It imports m/dir2 which has one # The main package (m) is contained in an overlay. It imports m/dir2 which has one
# file in an overlay and one file outside the overlay, which in turn imports m/dir, # file in an overlay and one file outside the overlay, which in turn imports m/dir,
@ -29,6 +31,36 @@ exec ./print_trimpath_two_files$GOEXE
stdout $WORK[/\\]gopath[/\\]src[/\\]m[/\\]printpath[/\\]main.go stdout $WORK[/\\]gopath[/\\]src[/\\]m[/\\]printpath[/\\]main.go
stdout $WORK[/\\]gopath[/\\]src[/\\]m[/\\]printpath[/\\]other.go stdout $WORK[/\\]gopath[/\\]src[/\\]m[/\\]printpath[/\\]other.go
go build -overlay overlay.json -o main_cgo_replace$GOEXE ./cgo_hello_replace
exec ./main_cgo_replace$GOEXE
stdout '^hello cgo\r?\n'
go build -overlay overlay.json -o main_cgo_quote$GOEXE ./cgo_hello_quote
exec ./main_cgo_quote$GOEXE
stdout '^hello cgo\r?\n'
go build -overlay overlay.json -o main_cgo_angle$GOEXE ./cgo_hello_angle
exec ./main_cgo_angle$GOEXE
stdout '^hello cgo\r?\n'
go build -overlay overlay.json -o main_call_asm$GOEXE ./call_asm
exec ./main_call_asm$GOEXE
! stdout .
# Change the contents of a file in the overlay and ensure that makes the target stale
go install -overlay overlay.json ./test_cache
go list -overlay overlay.json -f '{{.Stale}}' ./test_cache
stdout '^false$'
cp overlay/test_cache_different.go overlay/test_cache.go
go list -overlay overlay.json -f '{{.Stale}}' ./test_cache
stdout '^true$'
go list -compiled -overlay overlay.json -f '{{range .CompiledGoFiles}}{{. | printf "%s\n"}}{{end}}' ./cgo_hello_replace
cp stdout compiled_cgo_sources.txt
go run ../print_line_comments.go compiled_cgo_sources.txt
stdout $GOPATH[/\\]src[/\\]m[/\\]cgo_hello_replace[/\\]cgo_hello_replace.go
! stdout $GOPATH[/\\]src[/\\]m[/\\]overlay[/\\]hello.c
# Run same tests but with gccgo. # Run same tests but with gccgo.
env GO111MODULE=off env GO111MODULE=off
[!exec:gccgo] stop [!exec:gccgo] stop
@ -46,6 +78,24 @@ go build -compiler=gccgo -overlay overlay.json -o print_trimpath_gccgo$GOEXE -tr
exec ./print_trimpath_gccgo$GOEXE exec ./print_trimpath_gccgo$GOEXE
stdout ^\.[/\\]printpath[/\\]main.go stdout ^\.[/\\]printpath[/\\]main.go
go build -compiler=gccgo -overlay overlay.json -o main_cgo_replace_gccgo$GOEXE ./cgo_hello_replace
exec ./main_cgo_replace_gccgo$GOEXE
stdout '^hello cgo\r?\n'
go build -compiler=gccgo -overlay overlay.json -o main_cgo_quote_gccgo$GOEXE ./cgo_hello_quote
exec ./main_cgo_quote_gccgo$GOEXE
stdout '^hello cgo\r?\n'
go build -compiler=gccgo -overlay overlay.json -o main_cgo_angle_gccgo$GOEXE ./cgo_hello_angle
exec ./main_cgo_angle_gccgo$GOEXE
stdout '^hello cgo\r?\n'
go build -compiler=gccgo -overlay overlay.json -o main_call_asm_gccgo$GOEXE ./call_asm
exec ./main_call_asm_gccgo$GOEXE
! stdout .
-- m/go.mod -- -- m/go.mod --
// TODO(matloob): how do overlays work with go.mod (especially if mod=readonly) // TODO(matloob): how do overlays work with go.mod (especially if mod=readonly)
module m module m
@ -71,9 +121,36 @@ the actual code is in the overlay
"dir/g.go": "overlay/dir_g.go", "dir/g.go": "overlay/dir_g.go",
"dir2/i.go": "overlay/dir2_i.go", "dir2/i.go": "overlay/dir2_i.go",
"printpath/main.go": "overlay/printpath.go", "printpath/main.go": "overlay/printpath.go",
"printpath/other.go": "overlay2/printpath2.go" "printpath/other.go": "overlay2/printpath2.go",
"call_asm/asm_gc.s": "overlay/asm_gc.s",
"call_asm/asm_gccgo.s": "overlay/asm_gccgo.s",
"test_cache/main.go": "overlay/test_cache.go",
"cgo_hello_replace/cgo_header.h": "overlay/cgo_head.h",
"cgo_hello_replace/hello.c": "overlay/hello.c",
"cgo_hello_quote/cgo_hello.go": "overlay/cgo_hello_quote.go",
"cgo_hello_quote/cgo_header.h": "overlay/cgo_head.h",
"cgo_hello_angle/cgo_hello.go": "overlay/cgo_hello_angle.go",
"cgo_hello_angle/cgo_header.h": "overlay/cgo_head.h"
} }
} }
-- m/cgo_hello_replace/cgo_hello_replace.go --
package main
// #include "cgo_header.h"
import "C"
func main() {
C.say_hello()
}
-- m/cgo_hello_replace/cgo_header.h --
// Test that this header is replaced with one that has the proper declaration.
void say_goodbye();
-- m/cgo_hello_replace/hello.c --
#include <stdio.h>
void say_goodbye() { puts("goodbye cgo\n"); fflush(stdout); }
-- m/overlay/f.go -- -- m/overlay/f.go --
package main package main
@ -82,6 +159,14 @@ import "m/dir2"
func main() { func main() {
dir2.PrintMessage() dir2.PrintMessage()
} }
-- m/call_asm/main.go --
package main
func foo() // There will be a "missing function body" error if the assembly file isn't found.
func main() {
foo()
}
-- m/overlay/dir_g.go -- -- m/overlay/dir_g.go --
package dir package dir
@ -128,3 +213,96 @@ import "m/dir"
func printMessage() { func printMessage() {
dir.PrintMessage() dir.PrintMessage()
} }
-- m/overlay/cgo_hello_quote.go --
package main
// #include "cgo_header.h"
import "C"
func main() {
C.say_hello()
}
-- m/overlay/cgo_hello_angle.go --
package main
// #include <cgo_header.h>
import "C"
func main() {
C.say_hello()
}
-- m/overlay/cgo_head.h --
void say_hello();
-- m/overlay/hello.c --
#include <stdio.h>
void say_hello() { puts("hello cgo\n"); fflush(stdout); }
-- m/overlay/asm_gc.s --
// +build !gccgo
TEXT ·foo(SB),0,$0
RET
-- m/overlay/asm_gccgo.s --
// +build gccgo
.globl main.foo
.text
main.foo:
ret
-- m/overlay/test_cache.go --
package foo
import "fmt"
func bar() {
fmt.Println("something")
}
-- m/overlay/test_cache_different.go --
package foo
import "fmt"
func bar() {
fmt.Println("different")
}
-- m/cgo_hello_quote/hello.c --
#include <stdio.h>
void say_hello() { puts("hello cgo\n"); fflush(stdout); }
-- m/cgo_hello_angle/hello.c --
#include <stdio.h>
void say_hello() { puts("hello cgo\n"); fflush(stdout); }
-- print_line_comments.go --
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"strings"
)
func main() {
compiledGoFilesArg := os.Args[1]
b, err := ioutil.ReadFile(compiledGoFilesArg)
if err != nil {
log.Fatal(err)
}
compiledGoFiles := strings.Split(strings.TrimSpace(string(b)), "\n")
for _, f := range compiledGoFiles {
b, err := ioutil.ReadFile(f)
if err != nil {
log.Fatal(err)
}
for _, line := range strings.Split(string(b), "\n") {
if strings.HasPrefix(line, "#line") || strings.HasPrefix(line, "//line") {
fmt.Println(line)
}
}
}
}

View file

@ -1,7 +1,5 @@
# Plugins are only supported on linux,cgo (!riscv64) and darwin,cgo. # Plugins are not supported on all platforms.
[!linux] [!darwin] skip [!buildmode:plugin] skip
[linux] [riscv64] skip
[!cgo] skip
go build -n testdep go build -n testdep
! go build -buildmode=plugin testdep ! go build -buildmode=plugin testdep

View file

@ -20,10 +20,38 @@ go build -trimpath -o hello.exe .
go run ./list-dwarf hello.exe go run ./list-dwarf hello.exe
! stdout gopath/src ! stdout gopath/src
# Do the above, with the cgo (but not .c) sources in an overlay
# Check that the source path appears when -trimpath is not used.
mkdir $WORK/overlay
cp hello.go $WORK/overlay/hello.go
mkdir hello_overlay
cp hello.c hello_overlay/hello.c
go build -overlay overlay.json -o hello_overlay.exe ./hello_overlay
grep -q gopath[/\\]src hello_overlay.exe
! grep -q $WORK[/\\]overlay hello_overlay.exe
go run ./list-dwarf hello_overlay.exe
stdout gopath[/\\]src
! stdout $WORK[/\\]overlay
# Check that the source path does not appear when -trimpath is used.
go build -overlay overlay.json -trimpath -o hello_overlay.exe ./hello_overlay
! grep -q gopath[/\\]src hello_overlay.exe
! grep -q $WORK[/\\]overlay hello_overlay.exe
go run ./list-dwarf hello_overlay.exe
! stdout gopath/src
! stdout $WORK[/\\]overlay
-- go.mod -- -- go.mod --
module m module m
go 1.14 go 1.14
-- overlay.json --
{
"Replace": {
"hello_overlay/hello.go": "../../overlay/hello.go"
}
}
-- hello.c -- -- hello.c --
#include <stdio.h> #include <stdio.h>

View file

@ -69,6 +69,8 @@ go env -u GOPATH
stderr 'unknown go command variable GODEBUG' stderr 'unknown go command variable GODEBUG'
! go env -w GOEXE=.bat ! go env -w GOEXE=.bat
stderr 'GOEXE cannot be modified' stderr 'GOEXE cannot be modified'
! go env -w GOVERSION=customversion
stderr 'GOVERSION cannot be modified'
! go env -w GOENV=/env ! go env -w GOENV=/env
stderr 'GOENV can only be set using the OS environment' stderr 'GOENV can only be set using the OS environment'

View file

@ -5,46 +5,46 @@
env GO111MODULE=off env GO111MODULE=off
# argument doesn't have .go suffix # argument doesn't have .go suffix
go get test go get -d test
# argument has .go suffix, is a file and exists # argument has .go suffix, is a file and exists
! go get test.go ! go get -d test.go
stderr 'go get test.go: arguments must be package or module paths' stderr 'go get test.go: arguments must be package or module paths'
# argument has .go suffix, doesn't exist and has no slashes # argument has .go suffix, doesn't exist and has no slashes
! go get test_missing.go ! go get -d test_missing.go
stderr 'go get test_missing.go: arguments must be package or module paths' stderr 'go get test_missing.go: arguments must be package or module paths'
# argument has .go suffix, is a file and exists in sub-directory # argument has .go suffix, is a file and exists in sub-directory
! go get test/test.go ! go get -d test/test.go
stderr 'go get: test/test.go exists as a file, but ''go get'' requires package arguments' stderr 'go get: test/test.go exists as a file, but ''go get'' requires package arguments'
# argument has .go suffix, doesn't exist and has slashes # argument has .go suffix, doesn't exist and has slashes
! go get test/test_missing.go ! go get -d test/test_missing.go
! stderr 'arguments must be package or module paths' ! stderr 'arguments must be package or module paths'
! stderr 'exists as a file, but ''go get'' requires package arguments' ! stderr 'exists as a file, but ''go get'' requires package arguments'
# argument has .go suffix, is a symlink and exists # argument has .go suffix, is a symlink and exists
[symlink] symlink test_sym.go -> test.go [symlink] symlink test_sym.go -> test.go
[symlink] ! go get test_sym.go [symlink] ! go get -d test_sym.go
[symlink] stderr 'go get test_sym.go: arguments must be package or module paths' [symlink] stderr 'go get test_sym.go: arguments must be package or module paths'
[symlink] rm test_sym.go [symlink] rm test_sym.go
# argument has .go suffix, is a symlink and exists in sub-directory # argument has .go suffix, is a symlink and exists in sub-directory
[symlink] symlink test/test_sym.go -> test.go [symlink] symlink test/test_sym.go -> test.go
[symlink] ! go get test/test_sym.go [symlink] ! go get -d test/test_sym.go
[symlink] stderr 'go get: test/test_sym.go exists as a file, but ''go get'' requires package arguments' [symlink] stderr 'go get: test/test_sym.go exists as a file, but ''go get'' requires package arguments'
[symlink] rm test_sym.go [symlink] rm test_sym.go
# argument has .go suffix, is a directory and exists # argument has .go suffix, is a directory and exists
mkdir test_dir.go mkdir test_dir.go
! go get test_dir.go ! go get -d test_dir.go
stderr 'go get test_dir.go: arguments must be package or module paths' stderr 'go get test_dir.go: arguments must be package or module paths'
rm test_dir.go rm test_dir.go
# argument has .go suffix, is a directory and exists in sub-directory # argument has .go suffix, is a directory and exists in sub-directory
mkdir test/test_dir.go mkdir test/test_dir.go
! go get test/test_dir.go ! go get -d test/test_dir.go
! stderr 'arguments must be package or module paths' ! stderr 'arguments must be package or module paths'
! stderr 'exists as a file, but ''go get'' requires package arguments' ! stderr 'exists as a file, but ''go get'' requires package arguments'
rm test/test_dir.go rm test/test_dir.go

Some files were not shown because too many files have changed in this diff Show more