From 86ee84c40e2770ff189b6a4d835849107d9c749a Mon Sep 17 00:00:00 2001 From: Jay Conrod Date: Wed, 2 Sep 2020 13:25:11 -0400 Subject: cmd/go: move get.Insecure to cfg.Insecure to break dependency cycle Change-Id: If9c73ff5adc7e080a48ecc6b35ce40822193d66f Reviewed-on: https://go-review.googlesource.com/c/go/+/254363 Run-TryBot: Jay Conrod Reviewed-by: Bryan C. Mills Reviewed-by: Michael Matloob TryBot-Result: Gobot Gobot --- src/cmd/go/internal/cfg/cfg.go | 2 ++ src/cmd/go/internal/get/get.go | 6 ++---- src/cmd/go/internal/modfetch/insecure.go | 3 +-- src/cmd/go/internal/modfetch/sumdb.go | 3 +-- src/cmd/go/internal/modget/get.go | 6 +++--- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go index f874b880a6..9bf1db73ef 100644 --- a/src/cmd/go/internal/cfg/cfg.go +++ b/src/cmd/go/internal/cfg/cfg.go @@ -49,6 +49,8 @@ var ( ModCacheRW bool // -modcacherw flag ModFile string // -modfile flag + Insecure bool // -insecure flag + CmdName string // "build", "install", "list", "mod tidy", etc. DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable) diff --git a/src/cmd/go/internal/get/get.go b/src/cmd/go/internal/get/get.go index d0be3fe1e7..9e4825eb37 100644 --- a/src/cmd/go/internal/get/get.go +++ b/src/cmd/go/internal/get/get.go @@ -108,14 +108,12 @@ var ( getT = CmdGet.Flag.Bool("t", false, "") getU = CmdGet.Flag.Bool("u", false, "") getFix = CmdGet.Flag.Bool("fix", false, "") - - Insecure bool ) func init() { work.AddBuildFlags(CmdGet, work.OmitModFlag|work.OmitModCommonFlags) CmdGet.Run = runGet // break init loop - CmdGet.Flag.BoolVar(&Insecure, "insecure", Insecure, "") + CmdGet.Flag.BoolVar(&cfg.Insecure, "insecure", cfg.Insecure, "") } func runGet(ctx context.Context, cmd *base.Command, args []string) { @@ -431,7 +429,7 @@ func downloadPackage(p *load.Package) error { return fmt.Errorf("%s: invalid import path: %v", p.ImportPath, err) } security := web.SecureOnly - if Insecure || module.MatchPrefixPatterns(cfg.GOINSECURE, importPrefix) { + if cfg.Insecure || module.MatchPrefixPatterns(cfg.GOINSECURE, importPrefix) { security = web.Insecure } diff --git a/src/cmd/go/internal/modfetch/insecure.go b/src/cmd/go/internal/modfetch/insecure.go index b692669cba..012d05f29d 100644 --- a/src/cmd/go/internal/modfetch/insecure.go +++ b/src/cmd/go/internal/modfetch/insecure.go @@ -6,12 +6,11 @@ package modfetch import ( "cmd/go/internal/cfg" - "cmd/go/internal/get" "golang.org/x/mod/module" ) // allowInsecure reports whether we are allowed to fetch this path in an insecure manner. func allowInsecure(path string) bool { - return get.Insecure || module.MatchPrefixPatterns(cfg.GOINSECURE, path) + return cfg.Insecure || module.MatchPrefixPatterns(cfg.GOINSECURE, path) } diff --git a/src/cmd/go/internal/modfetch/sumdb.go b/src/cmd/go/internal/modfetch/sumdb.go index 783c4a433b..47a2571531 100644 --- a/src/cmd/go/internal/modfetch/sumdb.go +++ b/src/cmd/go/internal/modfetch/sumdb.go @@ -22,7 +22,6 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" - "cmd/go/internal/get" "cmd/go/internal/lockedfile" "cmd/go/internal/web" @@ -33,7 +32,7 @@ import ( // useSumDB reports whether to use the Go checksum database for the given module. func useSumDB(mod module.Version) bool { - return cfg.GOSUMDB != "off" && !get.Insecure && !module.MatchPrefixPatterns(cfg.GONOSUMDB, mod.Path) + return cfg.GOSUMDB != "off" && !cfg.Insecure && !module.MatchPrefixPatterns(cfg.GONOSUMDB, mod.Path) } // lookupSumDB returns the Go checksum database's go.sum lines for the given module, diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go index a2a8287d84..829cfe055a 100644 --- a/src/cmd/go/internal/modget/get.go +++ b/src/cmd/go/internal/modget/get.go @@ -17,7 +17,7 @@ import ( "sync" "cmd/go/internal/base" - "cmd/go/internal/get" + "cmd/go/internal/cfg" "cmd/go/internal/imports" "cmd/go/internal/load" "cmd/go/internal/modload" @@ -181,7 +181,7 @@ var ( getM = CmdGet.Flag.Bool("m", false, "") getT = CmdGet.Flag.Bool("t", false, "") getU upgradeFlag - // -insecure is get.Insecure + // -insecure is cfg.Insecure // -v is cfg.BuildV ) @@ -206,7 +206,7 @@ func (v *upgradeFlag) String() string { return "" } func init() { work.AddBuildFlags(CmdGet, work.OmitModFlag) CmdGet.Run = runGet // break init loop - CmdGet.Flag.BoolVar(&get.Insecure, "insecure", get.Insecure, "") + CmdGet.Flag.BoolVar(&cfg.Insecure, "insecure", cfg.Insecure, "") CmdGet.Flag.Var(&getU, "u", "") } -- cgit v1.2.3-54-g00ecf From 07c1788357cfe6a4ee5f6f6a54d4fe9f579fa844 Mon Sep 17 00:00:00 2001 From: Jay Conrod Date: Wed, 2 Sep 2020 14:53:02 -0400 Subject: cmd/go: move repository resolution from internal/get to internal/vcs This is a refactoring intended to break the dependency from internal/modfetch to internal/get. No change in functionality is intended. Change-Id: If51aba7139cc0b62ecc9ba454c055c99e8f36f0f Reviewed-on: https://go-review.googlesource.com/c/go/+/254364 Run-TryBot: Jay Conrod Reviewed-by: Bryan C. Mills Reviewed-by: Michael Matloob TryBot-Result: Gobot Gobot --- src/cmd/go/internal/get/discovery.go | 97 --- src/cmd/go/internal/get/get.go | 35 +- src/cmd/go/internal/get/pkg_test.go | 131 ---- src/cmd/go/internal/get/vcs.go | 1182 ---------------------------- src/cmd/go/internal/get/vcs_test.go | 475 ------------ src/cmd/go/internal/modfetch/repo.go | 12 +- src/cmd/go/internal/str/str_test.go | 27 + src/cmd/go/internal/vcs/discovery.go | 97 +++ src/cmd/go/internal/vcs/discovery_test.go | 110 +++ src/cmd/go/internal/vcs/vcs.go | 1187 +++++++++++++++++++++++++++++ src/cmd/go/internal/vcs/vcs_test.go | 475 ++++++++++++ 11 files changed, 1920 insertions(+), 1908 deletions(-) delete mode 100644 src/cmd/go/internal/get/discovery.go delete mode 100644 src/cmd/go/internal/get/pkg_test.go delete mode 100644 src/cmd/go/internal/get/vcs.go delete mode 100644 src/cmd/go/internal/get/vcs_test.go create mode 100644 src/cmd/go/internal/str/str_test.go create mode 100644 src/cmd/go/internal/vcs/discovery.go create mode 100644 src/cmd/go/internal/vcs/discovery_test.go create mode 100644 src/cmd/go/internal/vcs/vcs.go create mode 100644 src/cmd/go/internal/vcs/vcs_test.go diff --git a/src/cmd/go/internal/get/discovery.go b/src/cmd/go/internal/get/discovery.go deleted file mode 100644 index afa6ef455f..0000000000 --- a/src/cmd/go/internal/get/discovery.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2012 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 get - -import ( - "encoding/xml" - "fmt" - "io" - "strings" -) - -// charsetReader returns a reader that converts from the given charset to UTF-8. -// Currently it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful -// error which is printed by go get, so the user can find why the package -// wasn't downloaded if the encoding is not supported. Note that, in -// order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters -// greater than 0x7f are not rejected). -func charsetReader(charset string, input io.Reader) (io.Reader, error) { - switch strings.ToLower(charset) { - case "utf-8", "ascii": - return input, nil - default: - return nil, fmt.Errorf("can't decode XML document using charset %q", charset) - } -} - -// parseMetaGoImports returns meta imports from the HTML in r. -// Parsing ends at the end of the section or the beginning of the . -func parseMetaGoImports(r io.Reader, mod ModuleMode) ([]metaImport, error) { - d := xml.NewDecoder(r) - d.CharsetReader = charsetReader - d.Strict = false - var imports []metaImport - for { - t, err := d.RawToken() - if err != nil { - if err != io.EOF && len(imports) == 0 { - return nil, err - } - break - } - if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") { - break - } - if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") { - break - } - e, ok := t.(xml.StartElement) - if !ok || !strings.EqualFold(e.Name.Local, "meta") { - continue - } - if attrValue(e.Attr, "name") != "go-import" { - continue - } - if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 { - imports = append(imports, metaImport{ - Prefix: f[0], - VCS: f[1], - RepoRoot: f[2], - }) - } - } - - // Extract mod entries if we are paying attention to them. - var list []metaImport - var have map[string]bool - if mod == PreferMod { - have = make(map[string]bool) - for _, m := range imports { - if m.VCS == "mod" { - have[m.Prefix] = true - list = append(list, m) - } - } - } - - // Append non-mod entries, ignoring those superseded by a mod entry. - for _, m := range imports { - if m.VCS != "mod" && !have[m.Prefix] { - list = append(list, m) - } - } - return list, nil -} - -// attrValue returns the attribute value for the case-insensitive key -// `name', or the empty string if nothing is found. -func attrValue(attrs []xml.Attr, name string) string { - for _, a := range attrs { - if strings.EqualFold(a.Name.Local, name) { - return a.Value - } - } - return "" -} diff --git a/src/cmd/go/internal/get/get.go b/src/cmd/go/internal/get/get.go index 9e4825eb37..3f7a66384a 100644 --- a/src/cmd/go/internal/get/get.go +++ b/src/cmd/go/internal/get/get.go @@ -18,6 +18,7 @@ import ( "cmd/go/internal/load" "cmd/go/internal/search" "cmd/go/internal/str" + "cmd/go/internal/vcs" "cmd/go/internal/web" "cmd/go/internal/work" @@ -406,7 +407,7 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int) // to make the first copy of or update a copy of the given package. func downloadPackage(p *load.Package) error { var ( - vcs *vcsCmd + vcsCmd *vcs.Cmd repo, rootPath string err error blindRepo bool // set if the repo has unusual configuration @@ -435,16 +436,16 @@ func downloadPackage(p *load.Package) error { if p.Internal.Build.SrcRoot != "" { // Directory exists. Look for checkout along path to src. - vcs, rootPath, err = vcsFromDir(p.Dir, p.Internal.Build.SrcRoot) + vcsCmd, rootPath, err = vcs.FromDir(p.Dir, p.Internal.Build.SrcRoot) if err != nil { return err } repo = "" // should be unused; make distinctive // Double-check where it came from. - if *getU && vcs.remoteRepo != nil { + if *getU && vcsCmd.RemoteRepo != nil { dir := filepath.Join(p.Internal.Build.SrcRoot, filepath.FromSlash(rootPath)) - remote, err := vcs.remoteRepo(vcs, dir) + remote, err := vcsCmd.RemoteRepo(vcsCmd, dir) if err != nil { // Proceed anyway. The package is present; we likely just don't understand // the repo configuration (e.g. unusual remote protocol). @@ -452,10 +453,10 @@ func downloadPackage(p *load.Package) error { } repo = remote if !*getF && err == nil { - if rr, err := RepoRootForImportPath(importPrefix, IgnoreMod, security); err == nil { + if rr, err := vcs.RepoRootForImportPath(importPrefix, vcs.IgnoreMod, security); err == nil { repo := rr.Repo - if rr.vcs.resolveRepo != nil { - resolved, err := rr.vcs.resolveRepo(rr.vcs, dir, repo) + if rr.VCS.ResolveRepo != nil { + resolved, err := rr.VCS.ResolveRepo(rr.VCS, dir, repo) if err == nil { repo = resolved } @@ -469,13 +470,13 @@ func downloadPackage(p *load.Package) error { } else { // Analyze the import path to determine the version control system, // repository, and the import path for the root of the repository. - rr, err := RepoRootForImportPath(importPrefix, IgnoreMod, security) + rr, err := vcs.RepoRootForImportPath(importPrefix, vcs.IgnoreMod, security) if err != nil { return err } - vcs, repo, rootPath = rr.vcs, rr.Repo, rr.Root + vcsCmd, repo, rootPath = rr.VCS, rr.Repo, rr.Root } - if !blindRepo && !vcs.isSecure(repo) && security != web.Insecure { + if !blindRepo && !vcsCmd.IsSecure(repo) && security != web.Insecure { return fmt.Errorf("cannot download, %v uses insecure protocol", repo) } @@ -498,7 +499,7 @@ func downloadPackage(p *load.Package) error { } root := filepath.Join(p.Internal.Build.SrcRoot, filepath.FromSlash(rootPath)) - if err := checkNestedVCS(vcs, root, p.Internal.Build.SrcRoot); err != nil { + if err := vcs.CheckNested(vcsCmd, root, p.Internal.Build.SrcRoot); err != nil { return err } @@ -514,7 +515,7 @@ func downloadPackage(p *load.Package) error { // Check that this is an appropriate place for the repo to be checked out. // The target directory must either not exist or have a repo checked out already. - meta := filepath.Join(root, "."+vcs.cmd) + meta := filepath.Join(root, "."+vcsCmd.Cmd) if _, err := os.Stat(meta); err != nil { // Metadata file or directory does not exist. Prepare to checkout new copy. // Some version control tools require the target directory not to exist. @@ -535,12 +536,12 @@ func downloadPackage(p *load.Package) error { fmt.Fprintf(os.Stderr, "created GOPATH=%s; see 'go help gopath'\n", p.Internal.Build.Root) } - if err = vcs.create(root, repo); err != nil { + if err = vcsCmd.Create(root, repo); err != nil { return err } } else { // Metadata directory does exist; download incremental updates. - if err = vcs.download(root); err != nil { + if err = vcsCmd.Download(root); err != nil { return err } } @@ -549,12 +550,12 @@ func downloadPackage(p *load.Package) error { // Do not show tag sync in -n; it's noise more than anything, // and since we're not running commands, no tag will be found. // But avoid printing nothing. - fmt.Fprintf(os.Stderr, "# cd %s; %s sync/update\n", root, vcs.cmd) + fmt.Fprintf(os.Stderr, "# cd %s; %s sync/update\n", root, vcsCmd.Cmd) return nil } // Select and sync to appropriate version of the repository. - tags, err := vcs.tags(root) + tags, err := vcsCmd.Tags(root) if err != nil { return err } @@ -562,7 +563,7 @@ func downloadPackage(p *load.Package) error { if i := strings.Index(vers, " "); i >= 0 { vers = vers[:i] } - if err := vcs.tagSync(root, selectTag(vers, tags)); err != nil { + if err := vcsCmd.TagSync(root, selectTag(vers, tags)); err != nil { return err } diff --git a/src/cmd/go/internal/get/pkg_test.go b/src/cmd/go/internal/get/pkg_test.go deleted file mode 100644 index fc6a179c2e..0000000000 --- a/src/cmd/go/internal/get/pkg_test.go +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package get - -import ( - "cmd/go/internal/str" - "reflect" - "strings" - "testing" -) - -var foldDupTests = []struct { - list []string - f1, f2 string -}{ - {str.StringList("math/rand", "math/big"), "", ""}, - {str.StringList("math", "strings"), "", ""}, - {str.StringList("strings"), "", ""}, - {str.StringList("strings", "strings"), "strings", "strings"}, - {str.StringList("Rand", "rand", "math", "math/rand", "math/Rand"), "Rand", "rand"}, -} - -func TestFoldDup(t *testing.T) { - for _, tt := range foldDupTests { - f1, f2 := str.FoldDup(tt.list) - if f1 != tt.f1 || f2 != tt.f2 { - t.Errorf("foldDup(%q) = %q, %q, want %q, %q", tt.list, f1, f2, tt.f1, tt.f2) - } - } -} - -var parseMetaGoImportsTests = []struct { - in string - mod ModuleMode - out []metaImport -}{ - { - ``, - IgnoreMod, - []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}}, - }, - { - ` - `, - IgnoreMod, - []metaImport{ - {"foo/bar", "git", "https://github.com/rsc/foo/bar"}, - {"baz/quux", "git", "http://github.com/rsc/baz/quux"}, - }, - }, - { - ` - `, - IgnoreMod, - []metaImport{ - {"foo/bar", "git", "https://github.com/rsc/foo/bar"}, - }, - }, - { - ` - `, - IgnoreMod, - []metaImport{ - {"foo/bar", "git", "https://github.com/rsc/foo/bar"}, - }, - }, - { - ` - `, - PreferMod, - []metaImport{ - {"foo/bar", "mod", "http://github.com/rsc/baz/quux"}, - }, - }, - { - ` - - `, - IgnoreMod, - []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}}, - }, - { - ` - `, - IgnoreMod, - []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}}, - }, - { - ``, - IgnoreMod, - []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}}, - }, - { - // XML doesn't like
. - `Page Not Found
DRAFT
`, - IgnoreMod, - []metaImport{{"chitin.io/chitin", "git", "https://github.com/chitin-io/chitin"}}, - }, - { - ` - - `, - IgnoreMod, - []metaImport{{"myitcv.io", "git", "https://github.com/myitcv/x"}}, - }, - { - ` - - `, - PreferMod, - []metaImport{ - {"myitcv.io/blah2", "mod", "https://raw.githubusercontent.com/myitcv/pubx/master"}, - {"myitcv.io", "git", "https://github.com/myitcv/x"}, - }, - }, -} - -func TestParseMetaGoImports(t *testing.T) { - for i, tt := range parseMetaGoImportsTests { - out, err := parseMetaGoImports(strings.NewReader(tt.in), tt.mod) - if err != nil { - t.Errorf("test#%d: %v", i, err) - continue - } - if !reflect.DeepEqual(out, tt.out) { - t.Errorf("test#%d:\n\thave %q\n\twant %q", i, out, tt.out) - } - } -} diff --git a/src/cmd/go/internal/get/vcs.go b/src/cmd/go/internal/get/vcs.go deleted file mode 100644 index 24c32935d0..0000000000 --- a/src/cmd/go/internal/get/vcs.go +++ /dev/null @@ -1,1182 +0,0 @@ -// Copyright 2012 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 get - -import ( - "encoding/json" - "errors" - "fmt" - "internal/lazyregexp" - "internal/singleflight" - "log" - urlpkg "net/url" - "os" - "os/exec" - "path/filepath" - "regexp" - "strings" - "sync" - - "cmd/go/internal/base" - "cmd/go/internal/cfg" - "cmd/go/internal/load" - "cmd/go/internal/web" -) - -// A vcsCmd describes how to use a version control system -// like Mercurial, Git, or Subversion. -type vcsCmd struct { - name string - cmd string // name of binary to invoke command - - createCmd []string // commands to download a fresh copy of a repository - downloadCmd []string // commands to download updates into an existing repository - - tagCmd []tagCmd // commands to list tags - tagLookupCmd []tagCmd // commands to lookup tags before running tagSyncCmd - tagSyncCmd []string // commands to sync to specific tag - tagSyncDefault []string // commands to sync to default tag - - scheme []string - pingCmd string - - remoteRepo func(v *vcsCmd, rootDir string) (remoteRepo string, err error) - resolveRepo func(v *vcsCmd, rootDir, remoteRepo string) (realRepo string, err error) -} - -var defaultSecureScheme = map[string]bool{ - "https": true, - "git+ssh": true, - "bzr+ssh": true, - "svn+ssh": true, - "ssh": true, -} - -func (v *vcsCmd) isSecure(repo string) bool { - u, err := urlpkg.Parse(repo) - if err != nil { - // If repo is not a URL, it's not secure. - return false - } - return v.isSecureScheme(u.Scheme) -} - -func (v *vcsCmd) isSecureScheme(scheme string) bool { - switch v.cmd { - case "git": - // GIT_ALLOW_PROTOCOL is an environment variable defined by Git. It is a - // colon-separated list of schemes that are allowed to be used with git - // fetch/clone. Any scheme not mentioned will be considered insecure. - if allow := os.Getenv("GIT_ALLOW_PROTOCOL"); allow != "" { - for _, s := range strings.Split(allow, ":") { - if s == scheme { - return true - } - } - return false - } - } - return defaultSecureScheme[scheme] -} - -// A tagCmd describes a command to list available tags -// that can be passed to tagSyncCmd. -type tagCmd struct { - cmd string // command to list tags - pattern string // regexp to extract tags from list -} - -// vcsList lists the known version control systems -var vcsList = []*vcsCmd{ - vcsHg, - vcsGit, - vcsSvn, - vcsBzr, - vcsFossil, -} - -// vcsByCmd returns the version control system for the given -// command name (hg, git, svn, bzr). -func vcsByCmd(cmd string) *vcsCmd { - for _, vcs := range vcsList { - if vcs.cmd == cmd { - return vcs - } - } - return nil -} - -// vcsHg describes how to use Mercurial. -var vcsHg = &vcsCmd{ - name: "Mercurial", - cmd: "hg", - - createCmd: []string{"clone -U -- {repo} {dir}"}, - downloadCmd: []string{"pull"}, - - // We allow both tag and branch names as 'tags' - // for selecting a version. This lets people have - // a go.release.r60 branch and a go1 branch - // and make changes in both, without constantly - // editing .hgtags. - tagCmd: []tagCmd{ - {"tags", `^(\S+)`}, - {"branches", `^(\S+)`}, - }, - tagSyncCmd: []string{"update -r {tag}"}, - tagSyncDefault: []string{"update default"}, - - scheme: []string{"https", "http", "ssh"}, - pingCmd: "identify -- {scheme}://{repo}", - remoteRepo: hgRemoteRepo, -} - -func hgRemoteRepo(vcsHg *vcsCmd, rootDir string) (remoteRepo string, err error) { - out, err := vcsHg.runOutput(rootDir, "paths default") - if err != nil { - return "", err - } - return strings.TrimSpace(string(out)), nil -} - -// vcsGit describes how to use Git. -var vcsGit = &vcsCmd{ - name: "Git", - cmd: "git", - - createCmd: []string{"clone -- {repo} {dir}", "-go-internal-cd {dir} submodule update --init --recursive"}, - downloadCmd: []string{"pull --ff-only", "submodule update --init --recursive"}, - - tagCmd: []tagCmd{ - // tags/xxx matches a git tag named xxx - // origin/xxx matches a git branch named xxx on the default remote repository - {"show-ref", `(?:tags|origin)/(\S+)$`}, - }, - tagLookupCmd: []tagCmd{ - {"show-ref tags/{tag} origin/{tag}", `((?:tags|origin)/\S+)$`}, - }, - tagSyncCmd: []string{"checkout {tag}", "submodule update --init --recursive"}, - // both createCmd and downloadCmd update the working dir. - // No need to do more here. We used to 'checkout master' - // but that doesn't work if the default branch is not named master. - // DO NOT add 'checkout master' here. - // See golang.org/issue/9032. - tagSyncDefault: []string{"submodule update --init --recursive"}, - - scheme: []string{"git", "https", "http", "git+ssh", "ssh"}, - - // Leave out the '--' separator in the ls-remote command: git 2.7.4 does not - // support such a separator for that command, and this use should be safe - // without it because the {scheme} value comes from the predefined list above. - // See golang.org/issue/33836. - pingCmd: "ls-remote {scheme}://{repo}", - - remoteRepo: gitRemoteRepo, -} - -// scpSyntaxRe matches the SCP-like addresses used by Git to access -// repositories by SSH. -var scpSyntaxRe = lazyregexp.New(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`) - -func gitRemoteRepo(vcsGit *vcsCmd, rootDir string) (remoteRepo string, err error) { - cmd := "config remote.origin.url" - errParse := errors.New("unable to parse output of git " + cmd) - errRemoteOriginNotFound := errors.New("remote origin not found") - outb, err := vcsGit.run1(rootDir, cmd, nil, false) - if err != nil { - // if it doesn't output any message, it means the config argument is correct, - // but the config value itself doesn't exist - if outb != nil && len(outb) == 0 { - return "", errRemoteOriginNotFound - } - return "", err - } - out := strings.TrimSpace(string(outb)) - - var repoURL *urlpkg.URL - if m := scpSyntaxRe.FindStringSubmatch(out); m != nil { - // Match SCP-like syntax and convert it to a URL. - // Eg, "git@github.com:user/repo" becomes - // "ssh://git@github.com/user/repo". - repoURL = &urlpkg.URL{ - Scheme: "ssh", - User: urlpkg.User(m[1]), - Host: m[2], - Path: m[3], - } - } else { - repoURL, err = urlpkg.Parse(out) - if err != nil { - return "", err - } - } - - // Iterate over insecure schemes too, because this function simply - // reports the state of the repo. If we can't see insecure schemes then - // we can't report the actual repo URL. - for _, s := range vcsGit.scheme { - if repoURL.Scheme == s { - return repoURL.String(), nil - } - } - return "", errParse -} - -// vcsBzr describes how to use Bazaar. -var vcsBzr = &vcsCmd{ - name: "Bazaar", - cmd: "bzr", - - createCmd: []string{"branch -- {repo} {dir}"}, - - // Without --overwrite bzr will not pull tags that changed. - // Replace by --overwrite-tags after http://pad.lv/681792 goes in. - downloadCmd: []string{"pull --overwrite"}, - - tagCmd: []tagCmd{{"tags", `^(\S+)`}}, - tagSyncCmd: []string{"update -r {tag}"}, - tagSyncDefault: []string{"update -r revno:-1"}, - - scheme: []string{"https", "http", "bzr", "bzr+ssh"}, - pingCmd: "info -- {scheme}://{repo}", - remoteRepo: bzrRemoteRepo, - resolveRepo: bzrResolveRepo, -} - -func bzrRemoteRepo(vcsBzr *vcsCmd, rootDir string) (remoteRepo string, err error) { - outb, err := vcsBzr.runOutput(rootDir, "config parent_location") - if err != nil { - return "", err - } - return strings.TrimSpace(string(outb)), nil -} - -func bzrResolveRepo(vcsBzr *vcsCmd, rootDir, remoteRepo string) (realRepo string, err error) { - outb, err := vcsBzr.runOutput(rootDir, "info "+remoteRepo) - if err != nil { - return "", err - } - out := string(outb) - - // Expect: - // ... - // (branch root|repository branch): - // ... - - found := false - for _, prefix := range []string{"\n branch root: ", "\n repository branch: "} { - i := strings.Index(out, prefix) - if i >= 0 { - out = out[i+len(prefix):] - found = true - break - } - } - if !found { - return "", fmt.Errorf("unable to parse output of bzr info") - } - - i := strings.Index(out, "\n") - if i < 0 { - return "", fmt.Errorf("unable to parse output of bzr info") - } - out = out[:i] - return strings.TrimSpace(out), nil -} - -// vcsSvn describes how to use Subversion. -var vcsSvn = &vcsCmd{ - name: "Subversion", - cmd: "svn", - - createCmd: []string{"checkout -- {repo} {dir}"}, - downloadCmd: []string{"update"}, - - // There is no tag command in subversion. - // The branch information is all in the path names. - - scheme: []string{"https", "http", "svn", "svn+ssh"}, - pingCmd: "info -- {scheme}://{repo}", - remoteRepo: svnRemoteRepo, -} - -func svnRemoteRepo(vcsSvn *vcsCmd, rootDir string) (remoteRepo string, err error) { - outb, err := vcsSvn.runOutput(rootDir, "info") - if err != nil { - return "", err - } - out := string(outb) - - // Expect: - // - // ... - // URL: - // ... - // - // Note that we're not using the Repository Root line, - // because svn allows checking out subtrees. - // The URL will be the URL of the subtree (what we used with 'svn co') - // while the Repository Root may be a much higher parent. - i := strings.Index(out, "\nURL: ") - if i < 0 { - return "", fmt.Errorf("unable to parse output of svn info") - } - out = out[i+len("\nURL: "):] - i = strings.Index(out, "\n") - if i < 0 { - return "", fmt.Errorf("unable to parse output of svn info") - } - out = out[:i] - return strings.TrimSpace(out), nil -} - -// fossilRepoName is the name go get associates with a fossil repository. In the -// real world the file can be named anything. -const fossilRepoName = ".fossil" - -// vcsFossil describes how to use Fossil (fossil-scm.org) -var vcsFossil = &vcsCmd{ - name: "Fossil", - cmd: "fossil", - - createCmd: []string{"-go-internal-mkdir {dir} clone -- {repo} " + filepath.Join("{dir}", fossilRepoName), "-go-internal-cd {dir} open .fossil"}, - downloadCmd: []string{"up"}, - - tagCmd: []tagCmd{{"tag ls", `(.*)`}}, - tagSyncCmd: []string{"up tag:{tag}"}, - tagSyncDefault: []string{"up trunk"}, - - scheme: []string{"https", "http"}, - remoteRepo: fossilRemoteRepo, -} - -func fossilRemoteRepo(vcsFossil *vcsCmd, rootDir string) (remoteRepo string, err error) { - out, err := vcsFossil.runOutput(rootDir, "remote-url") - if err != nil { - return "", err - } - return strings.TrimSpace(string(out)), nil -} - -func (v *vcsCmd) String() string { - return v.name -} - -// run runs the command line cmd in the given directory. -// keyval is a list of key, value pairs. run expands -// instances of {key} in cmd into value, but only after -// splitting cmd into individual arguments. -// If an error occurs, run prints the command line and the -// command's combined stdout+stderr to standard error. -// Otherwise run discards the command's output. -func (v *vcsCmd) run(dir string, cmd string, keyval ...string) error { - _, err := v.run1(dir, cmd, keyval, true) - return err -} - -// runVerboseOnly is like run but only generates error output to standard error in verbose mode. -func (v *vcsCmd) runVerboseOnly(dir string, cmd string, keyval ...string) error { - _, err := v.run1(dir, cmd, keyval, false) - return err -} - -// runOutput is like run but returns the output of the command. -func (v *vcsCmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) { - return v.run1(dir, cmd, keyval, true) -} - -// run1 is the generalized implementation of run and runOutput. -func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) { - m := make(map[string]string) - for i := 0; i < len(keyval); i += 2 { - m[keyval[i]] = keyval[i+1] - } - args := strings.Fields(cmdline) - for i, arg := range args { - args[i] = expand(m, arg) - } - - if len(args) >= 2 && args[0] == "-go-internal-mkdir" { - var err error - if filepath.IsAbs(args[1]) { - err = os.Mkdir(args[1], os.ModePerm) - } else { - err = os.Mkdir(filepath.Join(dir, args[1]), os.ModePerm) - } - if err != nil { - return nil, err - } - args = args[2:] - } - - if len(args) >= 2 && args[0] == "-go-internal-cd" { - if filepath.IsAbs(args[1]) { - dir = args[1] - } else { - dir = filepath.Join(dir, args[1]) - } - args = args[2:] - } - - _, err := exec.LookPath(v.cmd) - if err != nil { - fmt.Fprintf(os.Stderr, - "go: missing %s command. See https://golang.org/s/gogetcmd\n", - v.name) - return nil, err - } - - cmd := exec.Command(v.cmd, args...) - cmd.Dir = dir - cmd.Env = base.AppendPWD(os.Environ(), cmd.Dir) - if cfg.BuildX { - fmt.Fprintf(os.Stderr, "cd %s\n", dir) - fmt.Fprintf(os.Stderr, "%s %s\n", v.cmd, strings.Join(args, " ")) - } - out, err := cmd.Output() - if err != nil { - if verbose || cfg.BuildV { - fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " ")) - if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 { - os.Stderr.Write(ee.Stderr) - } else { - fmt.Fprintf(os.Stderr, err.Error()) - } - } - } - return out, err -} - -// ping pings to determine scheme to use. -func (v *vcsCmd) ping(scheme, repo string) error { - return v.runVerboseOnly(".", v.pingCmd, "scheme", scheme, "repo", repo) -} - -// create creates a new copy of repo in dir. -// The parent of dir must exist; dir must not. -func (v *vcsCmd) create(dir, repo string) error { - for _, cmd := range v.createCmd { - if err := v.run(".", cmd, "dir", dir, "repo", repo); err != nil { - return err - } - } - return nil -} - -// download downloads any new changes for the repo in dir. -func (v *vcsCmd) download(dir string) error { - for _, cmd := range v.downloadCmd { - if err := v.run(dir, cmd); err != nil { - return err - } - } - return nil -} - -// tags returns the list of available tags for the repo in dir. -func (v *vcsCmd) tags(dir string) ([]string, error) { - var tags []string - for _, tc := range v.tagCmd { - out, err := v.runOutput(dir, tc.cmd) - if err != nil { - return nil, err - } - re := regexp.MustCompile(`(?m-s)` + tc.pattern) - for _, m := range re.FindAllStringSubmatch(string(out), -1) { - tags = append(tags, m[1]) - } - } - return tags, nil -} - -// tagSync syncs the repo in dir to the named tag, -// which either is a tag returned by tags or is v.tagDefault. -func (v *vcsCmd) tagSync(dir, tag string) error { - if v.tagSyncCmd == nil { - return nil - } - if tag != "" { - for _, tc := range v.tagLookupCmd { - out, err := v.runOutput(dir, tc.cmd, "tag", tag) - if err != nil { - return err - } - re := regexp.MustCompile(`(?m-s)` + tc.pattern) - m := re.FindStringSubmatch(string(out)) - if len(m) > 1 { - tag = m[1] - break - } - } - } - - if tag == "" && v.tagSyncDefault != nil { - for _, cmd := range v.tagSyncDefault { - if err := v.run(dir, cmd); err != nil { - return err - } - } - return nil - } - - for _, cmd := range v.tagSyncCmd { - if err := v.run(dir, cmd, "tag", tag); err != nil { - return err - } - } - return nil -} - -// A vcsPath describes how to convert an import path into a -// version control system and repository name. -type vcsPath struct { - prefix string // prefix this description applies to - regexp *lazyregexp.Regexp // compiled pattern for import path - repo string // repository to use (expand with match of re) - vcs string // version control system to use (expand with match of re) - check func(match map[string]string) error // additional checks - schemelessRepo bool // if true, the repo pattern lacks a scheme -} - -// vcsFromDir inspects dir and its parents to determine the -// version control system and code repository to use. -// On return, root is the import path -// corresponding to the root of the repository. -func vcsFromDir(dir, srcRoot string) (vcs *vcsCmd, root string, err error) { - // Clean and double-check that dir is in (a subdirectory of) srcRoot. - dir = filepath.Clean(dir) - srcRoot = filepath.Clean(srcRoot) - if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator { - return nil, "", fmt.Errorf("directory %q is outside source root %q", dir, srcRoot) - } - - var vcsRet *vcsCmd - var rootRet string - - origDir := dir - for len(dir) > len(srcRoot) { - for _, vcs := range vcsList { - if _, err := os.Stat(filepath.Join(dir, "."+vcs.cmd)); err == nil { - root := filepath.ToSlash(dir[len(srcRoot)+1:]) - // Record first VCS we find, but keep looking, - // to detect mistakes like one kind of VCS inside another. - if vcsRet == nil { - vcsRet = vcs - rootRet = root - continue - } - // Allow .git inside .git, which can arise due to submodules. - if vcsRet == vcs && vcs.cmd == "git" { - continue - } - // Otherwise, we have one VCS inside a different VCS. - return nil, "", fmt.Errorf("directory %q uses %s, but parent %q uses %s", - filepath.Join(srcRoot, rootRet), vcsRet.cmd, filepath.Join(srcRoot, root), vcs.cmd) - } - } - - // Move to parent. - ndir := filepath.Dir(dir) - if len(ndir) >= len(dir) { - // Shouldn't happen, but just in case, stop. - break - } - dir = ndir - } - - if vcsRet != nil { - return vcsRet, rootRet, nil - } - - return nil, "", fmt.Errorf("directory %q is not using a known version control system", origDir) -} - -// checkNestedVCS checks for an incorrectly-nested VCS-inside-VCS -// situation for dir, checking parents up until srcRoot. -func checkNestedVCS(vcs *vcsCmd, dir, srcRoot string) error { - if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator { - return fmt.Errorf("directory %q is outside source root %q", dir, srcRoot) - } - - otherDir := dir - for len(otherDir) > len(srcRoot) { - for _, otherVCS := range vcsList { - if _, err := os.Stat(filepath.Join(otherDir, "."+otherVCS.cmd)); err == nil { - // Allow expected vcs in original dir. - if otherDir == dir && otherVCS == vcs { - continue - } - // Allow .git inside .git, which can arise due to submodules. - if otherVCS == vcs && vcs.cmd == "git" { - continue - } - // Otherwise, we have one VCS inside a different VCS. - return fmt.Errorf("directory %q uses %s, but parent %q uses %s", dir, vcs.cmd, otherDir, otherVCS.cmd) - } - } - // Move to parent. - newDir := filepath.Dir(otherDir) - if len(newDir) >= len(otherDir) { - // Shouldn't happen, but just in case, stop. - break - } - otherDir = newDir - } - - return nil -} - -// RepoRoot describes the repository root for a tree of source code. -type RepoRoot struct { - Repo string // repository URL, including scheme - Root string // import path corresponding to root of repo - IsCustom bool // defined by served tags (as opposed to hard-coded pattern) - VCS string // vcs type ("mod", "git", ...) - - vcs *vcsCmd // internal: vcs command access -} - -func httpPrefix(s string) string { - for _, prefix := range [...]string{"http:", "https:"} { - if strings.HasPrefix(s, prefix) { - return prefix - } - } - return "" -} - -// ModuleMode specifies whether to prefer modules when looking up code sources. -type ModuleMode int - -const ( - IgnoreMod ModuleMode = iota - PreferMod -) - -// RepoRootForImportPath analyzes importPath to determine the -// version control system, and code repository to use. -func RepoRootForImportPath(importPath string, mod ModuleMode, security web.SecurityMode) (*RepoRoot, error) { - rr, err := repoRootFromVCSPaths(importPath, security, vcsPaths) - if err == errUnknownSite { - rr, err = repoRootForImportDynamic(importPath, mod, security) - if err != nil { - err = load.ImportErrorf(importPath, "unrecognized import path %q: %v", importPath, err) - } - } - if err != nil { - rr1, err1 := repoRootFromVCSPaths(importPath, security, vcsPathsAfterDynamic) - if err1 == nil { - rr = rr1 - err = nil - } - } - - // Should have been taken care of above, but make sure. - if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.Root, "...") { - // Do not allow wildcards in the repo root. - rr = nil - err = load.ImportErrorf(importPath, "cannot expand ... in %q", importPath) - } - return rr, err -} - -var errUnknownSite = errors.New("dynamic lookup required to find mapping") - -// repoRootFromVCSPaths attempts to map importPath to a repoRoot -// using the mappings defined in vcsPaths. -func repoRootFromVCSPaths(importPath string, security web.SecurityMode, vcsPaths []*vcsPath) (*RepoRoot, error) { - // A common error is to use https://packagepath because that's what - // hg and git require. Diagnose this helpfully. - if prefix := httpPrefix(importPath); prefix != "" { - // The importPath has been cleaned, so has only one slash. The pattern - // ignores the slashes; the error message puts them back on the RHS at least. - return nil, fmt.Errorf("%q not allowed in import path", prefix+"//") - } - for _, srv := range vcsPaths { - if !strings.HasPrefix(importPath, srv.prefix) { - continue - } - m := srv.regexp.FindStringSubmatch(importPath) - if m == nil { - if srv.prefix != "" { - return nil, load.ImportErrorf(importPath, "invalid %s import path %q", srv.prefix, importPath) - } - continue - } - - // Build map of named subexpression matches for expand. - match := map[string]string{ - "prefix": srv.prefix, - "import": importPath, - } - for i, name := range srv.regexp.SubexpNames() { - if name != "" && match[name] == "" { - match[name] = m[i] - } - } - if srv.vcs != "" { - match["vcs"] = expand(match, srv.vcs) - } - if srv.repo != "" { - match["repo"] = expand(match, srv.repo) - } - if srv.check != nil { - if err := srv.check(match); err != nil { - return nil, err - } - } - vcs := vcsByCmd(match["vcs"]) - if vcs == nil { - return nil, fmt.Errorf("unknown version control system %q", match["vcs"]) - } - var repoURL string - if !srv.schemelessRepo { - repoURL = match["repo"] - } else { - scheme := vcs.scheme[0] // default to first scheme - repo := match["repo"] - if vcs.pingCmd != "" { - // If we know how to test schemes, scan to find one. - for _, s := range vcs.scheme { - if security == web.SecureOnly && !vcs.isSecureScheme(s) { - continue - } - if vcs.ping(s, repo) == nil { - scheme = s - break - } - } - } - repoURL = scheme + "://" + repo - } - rr := &RepoRoot{ - Repo: repoURL, - Root: match["root"], - VCS: vcs.cmd, - vcs: vcs, - } - return rr, nil - } - return nil, errUnknownSite -} - -// urlForImportPath returns a partially-populated URL for the given Go import path. -// -// The URL leaves the Scheme field blank so that web.Get will try any scheme -// allowed by the selected security mode. -func urlForImportPath(importPath string) (*urlpkg.URL, error) { - slash := strings.Index(importPath, "/") - if slash < 0 { - slash = len(importPath) - } - host, path := importPath[:slash], importPath[slash:] - if !strings.Contains(host, ".") { - return nil, errors.New("import path does not begin with hostname") - } - if len(path) == 0 { - path = "/" - } - return &urlpkg.URL{Host: host, Path: path, RawQuery: "go-get=1"}, nil -} - -// repoRootForImportDynamic finds a *RepoRoot for a custom domain that's not -// statically known by repoRootForImportPathStatic. -// -// This handles custom import paths like "name.tld/pkg/foo" or just "name.tld". -func repoRootForImportDynamic(importPath string, mod ModuleMode, security web.SecurityMode) (*RepoRoot, error) { - url, err := urlForImportPath(importPath) - if err != nil { - return nil, err - } - resp, err := web.Get(security, url) - if err != nil { - msg := "https fetch: %v" - if security == web.Insecure { - msg = "http/" + msg - } - return nil, fmt.Errorf(msg, err) - } - body := resp.Body - defer body.Close() - imports, err := parseMetaGoImports(body, mod) - if len(imports) == 0 { - if respErr := resp.Err(); respErr != nil { - // If the server's status was not OK, prefer to report that instead of - // an XML parse error. - return nil, respErr - } - } - if err != nil { - return nil, fmt.Errorf("parsing %s: %v", importPath, err) - } - // Find the matched meta import. - mmi, err := matchGoImport(imports, importPath) - if err != nil { - if _, ok := err.(ImportMismatchError); !ok { - return nil, fmt.Errorf("parse %s: %v", url, err) - } - return nil, fmt.Errorf("parse %s: no go-import meta tags (%s)", resp.URL, err) - } - if cfg.BuildV { - log.Printf("get %q: found meta tag %#v at %s", importPath, mmi, url) - } - // If the import was "uni.edu/bob/project", which said the - // prefix was "uni.edu" and the RepoRoot was "evilroot.com", - // make sure we don't trust Bob and check out evilroot.com to - // "uni.edu" yet (possibly overwriting/preempting another - // non-evil student). Instead, first verify the root and see - // if it matches Bob's claim. - if mmi.Prefix != importPath { - if cfg.BuildV { - log.Printf("get %q: verifying non-authoritative meta tag", importPath) - } - var imports []metaImport - url, imports, err = metaImportsForPrefix(mmi.Prefix, mod, security) - if err != nil { - return nil, err - } - metaImport2, err := matchGoImport(imports, importPath) - if err != nil || mmi != metaImport2 { - return nil, fmt.Errorf("%s and %s disagree about go-import for %s", resp.URL, url, mmi.Prefix) - } - } - - if err := validateRepoRoot(mmi.RepoRoot); err != nil { - return nil, fmt.Errorf("%s: invalid repo root %q: %v", resp.URL, mmi.RepoRoot, err) - } - vcs := vcsByCmd(mmi.VCS) - if vcs == nil && mmi.VCS != "mod" { - return nil, fmt.Errorf("%s: unknown vcs %q", resp.URL, mmi.VCS) - } - - rr := &RepoRoot{ - Repo: mmi.RepoRoot, - Root: mmi.Prefix, - IsCustom: true, - VCS: mmi.VCS, - vcs: vcs, - } - return rr, nil -} - -// validateRepoRoot returns an error if repoRoot does not seem to be -// a valid URL with scheme. -func validateRepoRoot(repoRoot string) error { - url, err := urlpkg.Parse(repoRoot) - if err != nil { - return err - } - if url.Scheme == "" { - return errors.New("no scheme") - } - if url.Scheme == "file" { - return errors.New("file scheme disallowed") - } - return nil -} - -var fetchGroup singleflight.Group -var ( - fetchCacheMu sync.Mutex - fetchCache = map[string]fetchResult{} // key is metaImportsForPrefix's importPrefix -) - -// metaImportsForPrefix takes a package's root import path as declared in a tag -// and returns its HTML discovery URL and the parsed metaImport lines -// found on the page. -// -// The importPath is of the form "golang.org/x/tools". -// It is an error if no imports are found. -// url will still be valid if err != nil. -// The returned url will be of the form "https://golang.org/x/tools?go-get=1" -func metaImportsForPrefix(importPrefix string, mod ModuleMode, security web.SecurityMode) (*urlpkg.URL, []metaImport, error) { - setCache := func(res fetchResult) (fetchResult, error) { - fetchCacheMu.Lock() - defer fetchCacheMu.Unlock() - fetchCache[importPrefix] = res - return res, nil - } - - resi, _, _ := fetchGroup.Do(importPrefix, func() (resi interface{}, err error) { - fetchCacheMu.Lock() - if res, ok := fetchCache[importPrefix]; ok { - fetchCacheMu.Unlock() - return res, nil - } - fetchCacheMu.Unlock() - - url, err := urlForImportPath(importPrefix) - if err != nil { - return setCache(fetchResult{err: err}) - } - resp, err := web.Get(security, url) - if err != nil { - return setCache(fetchResult{url: url, err: fmt.Errorf("fetching %s: %v", importPrefix, err)}) - } - body := resp.Body - defer body.Close() - imports, err := parseMetaGoImports(body, mod) - if len(imports) == 0 { - if respErr := resp.Err(); respErr != nil { - // If the server's status was not OK, prefer to report that instead of - // an XML parse error. - return setCache(fetchResult{url: url, err: respErr}) - } - } - if err != nil { - return setCache(fetchResult{url: url, err: fmt.Errorf("parsing %s: %v", resp.URL, err)}) - } - if len(imports) == 0 { - err = fmt.Errorf("fetching %s: no go-import meta tag found in %s", importPrefix, resp.URL) - } - return setCache(fetchResult{url: url, imports: imports, err: err}) - }) - res := resi.(fetchResult) - return res.url, res.imports, res.err -} - -type fetchResult struct { - url *urlpkg.URL - imports []metaImport - err error -} - -// metaImport represents the parsed tags from HTML files. -type metaImport struct { - Prefix, VCS, RepoRoot string -} - -// pathPrefix reports whether sub is a prefix of s, -// only considering entire path components. -func pathPrefix(s, sub string) bool { - // strings.HasPrefix is necessary but not sufficient. - if !strings.HasPrefix(s, sub) { - return false - } - // The remainder after the prefix must either be empty or start with a slash. - rem := s[len(sub):] - return rem == "" || rem[0] == '/' -} - -// A ImportMismatchError is returned where metaImport/s are present -// but none match our import path. -type ImportMismatchError struct { - importPath string - mismatches []string // the meta imports that were discarded for not matching our importPath -} - -func (m ImportMismatchError) Error() string { - formattedStrings := make([]string, len(m.mismatches)) - for i, pre := range m.mismatches { - formattedStrings[i] = fmt.Sprintf("meta tag %s did not match import path %s", pre, m.importPath) - } - return strings.Join(formattedStrings, ", ") -} - -// matchGoImport returns the metaImport from imports matching importPath. -// An error is returned if there are multiple matches. -// An ImportMismatchError is returned if none match. -func matchGoImport(imports []metaImport, importPath string) (metaImport, error) { - match := -1 - - errImportMismatch := ImportMismatchError{importPath: importPath} - for i, im := range imports { - if !pathPrefix(importPath, im.Prefix) { - errImportMismatch.mismatches = append(errImportMismatch.mismatches, im.Prefix) - continue - } - - if match >= 0 { - if imports[match].VCS == "mod" && im.VCS != "mod" { - // All the mod entries precede all the non-mod entries. - // We have a mod entry and don't care about the rest, - // matching or not. - break - } - return metaImport{}, fmt.Errorf("multiple meta tags match import path %q", importPath) - } - match = i - } - - if match == -1 { - return metaImport{}, errImportMismatch - } - return imports[match], nil -} - -// expand rewrites s to replace {k} with match[k] for each key k in match. -func expand(match map[string]string, s string) string { - // We want to replace each match exactly once, and the result of expansion - // must not depend on the iteration order through the map. - // A strings.Replacer has exactly the properties we're looking for. - oldNew := make([]string, 0, 2*len(match)) - for k, v := range match { - oldNew = append(oldNew, "{"+k+"}", v) - } - return strings.NewReplacer(oldNew...).Replace(s) -} - -// vcsPaths defines the meaning of import paths referring to -// commonly-used VCS hosting sites (github.com/user/dir) -// and import paths referring to a fully-qualified importPath -// containing a VCS type (foo.com/repo.git/dir) -var vcsPaths = []*vcsPath{ - // Github - { - prefix: "github.com/", - regexp: lazyregexp.New(`^(?Pgithub\.com/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`), - vcs: "git", - repo: "https://{root}", - check: noVCSSuffix, - }, - - // Bitbucket - { - prefix: "bitbucket.org/", - regexp: lazyregexp.New(`^(?Pbitbucket\.org/(?P[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`), - repo: "https://{root}", - check: bitbucketVCS, - }, - - // IBM DevOps Services (JazzHub) - { - prefix: "hub.jazz.net/git/", - regexp: lazyregexp.New(`^(?Phub\.jazz\.net/git/[a-z0-9]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`), - vcs: "git", - repo: "https://{root}", - check: noVCSSuffix, - }, - - // Git at Apache - { - prefix: "git.apache.org/", - regexp: lazyregexp.New(`^(?Pgit\.apache\.org/[a-z0-9_.\-]+\.git)(/[A-Za-z0-9_.\-]+)*$`), - vcs: "git", - repo: "https://{root}", - }, - - // Git at OpenStack - { - prefix: "git.openstack.org/", - regexp: lazyregexp.New(`^(?Pgit\.openstack\.org/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(\.git)?(/[A-Za-z0-9_.\-]+)*$`), - vcs: "git", - repo: "https://{root}", - }, - - // chiselapp.com for fossil - { - prefix: "chiselapp.com/", - regexp: lazyregexp.New(`^(?Pchiselapp\.com/user/[A-Za-z0-9]+/repository/[A-Za-z0-9_.\-]+)$`), - vcs: "fossil", - repo: "https://{root}", - }, - - // General syntax for any server. - // Must be last. - { - regexp: lazyregexp.New(`(?P(?P([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?(/~?[A-Za-z0-9_.\-]+)+?)\.(?Pbzr|fossil|git|hg|svn))(/~?[A-Za-z0-9_.\-]+)*$`), - schemelessRepo: true, - }, -} - -// vcsPathsAfterDynamic gives additional vcsPaths entries -// to try after the dynamic HTML check. -// This gives those sites a chance to introduce tags -// as part of a graceful transition away from the hard-coded logic. -var vcsPathsAfterDynamic = []*vcsPath{ - // Launchpad. See golang.org/issue/11436. - { - prefix: "launchpad.net/", - regexp: lazyregexp.New(`^(?Plaunchpad\.net/((?P[A-Za-z0-9_.\-]+)(?P/[A-Za-z0-9_.\-]+)?|~[A-Za-z0-9_.\-]+/(\+junk|[A-Za-z0-9_.\-]+)/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`), - vcs: "bzr", - repo: "https://{root}", - check: launchpadVCS, - }, -} - -// noVCSSuffix checks that the repository name does not -// end in .foo for any version control system foo. -// The usual culprit is ".git". -func noVCSSuffix(match map[string]string) error { - repo := match["repo"] - for _, vcs := range vcsList { - if strings.HasSuffix(repo, "."+vcs.cmd) { - return fmt.Errorf("invalid version control suffix in %s path", match["prefix"]) - } - } - return nil -} - -// bitbucketVCS determines the version control system for a -// Bitbucket repository, by using the Bitbucket API. -func bitbucketVCS(match map[string]string) error { - if err := noVCSSuffix(match); err != nil { - return err - } - - var resp struct { - SCM string `json:"scm"` - } - url := &urlpkg.URL{ - Scheme: "https", - Host: "api.bitbucket.org", - Path: expand(match, "/2.0/repositories/{bitname}"), - RawQuery: "fields=scm", - } - data, err := web.GetBytes(url) - if err != nil { - if httpErr, ok := err.(*web.HTTPError); ok && httpErr.StatusCode == 403 { - // this may be a private repository. If so, attempt to determine which - // VCS it uses. See issue 5375. - root := match["root"] - for _, vcs := range []string{"git", "hg"} { - if vcsByCmd(vcs).ping("https", root) == nil { - resp.SCM = vcs - break - } - } - } - - if resp.SCM == "" { - return err - } - } else { - if err := json.Unmarshal(data, &resp); err != nil { - return fmt.Errorf("decoding %s: %v", url, err) - } - } - - if vcsByCmd(resp.SCM) != nil { - match["vcs"] = resp.SCM - if resp.SCM == "git" { - match["repo"] += ".git" - } - return nil - } - - return fmt.Errorf("unable to detect version control system for bitbucket.org/ path") -} - -// launchpadVCS solves the ambiguity for "lp.net/project/foo". In this case, -// "foo" could be a series name registered in Launchpad with its own branch, -// and it could also be the name of a directory within the main project -// branch one level up. -func launchpadVCS(match map[string]string) error { - if match["project"] == "" || match["series"] == "" { - return nil - } - url := &urlpkg.URL{ - Scheme: "https", - Host: "code.launchpad.net", - Path: expand(match, "/{project}{series}/.bzr/branch-format"), - } - _, err := web.GetBytes(url) - if err != nil { - match["root"] = expand(match, "launchpad.net/{project}") - match["repo"] = expand(match, "https://{root}") - } - return nil -} diff --git a/src/cmd/go/internal/get/vcs_test.go b/src/cmd/go/internal/get/vcs_test.go deleted file mode 100644 index 195bc231eb..0000000000 --- a/src/cmd/go/internal/get/vcs_test.go +++ /dev/null @@ -1,475 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package get - -import ( - "errors" - "internal/testenv" - "io/ioutil" - "os" - "path" - "path/filepath" - "testing" - - "cmd/go/internal/web" -) - -// Test that RepoRootForImportPath determines the correct RepoRoot for a given importPath. -// TODO(cmang): Add tests for SVN and BZR. -func TestRepoRootForImportPath(t *testing.T) { - testenv.MustHaveExternalNetwork(t) - - tests := []struct { - path string - want *RepoRoot - }{ - { - "github.com/golang/groupcache", - &RepoRoot{ - vcs: vcsGit, - Repo: "https://github.com/golang/groupcache", - }, - }, - // Unicode letters in directories are not valid. - { - "github.com/user/unicode/испытание", - nil, - }, - // IBM DevOps Services tests - { - "hub.jazz.net/git/user1/pkgname", - &RepoRoot{ - vcs: vcsGit, - Repo: "https://hub.jazz.net/git/user1/pkgname", - }, - }, - { - "hub.jazz.net/git/user1/pkgname/submodule/submodule/submodule", - &RepoRoot{ - vcs: vcsGit, - Repo: "https://hub.jazz.net/git/user1/pkgname", - }, - }, - { - "hub.jazz.net", - nil, - }, - { - "hubajazz.net", - nil, - }, - { - "hub2.jazz.net", - nil, - }, - { - "hub.jazz.net/someotherprefix", - nil, - }, - { - "hub.jazz.net/someotherprefix/user1/pkgname", - nil, - }, - // Spaces are not valid in user names or package names - { - "hub.jazz.net/git/User 1/pkgname", - nil, - }, - { - "hub.jazz.net/git/user1/pkg name", - nil, - }, - // Dots are not valid in user names - { - "hub.jazz.net/git/user.1/pkgname", - nil, - }, - { - "hub.jazz.net/git/user/pkg.name", - &RepoRoot{ - vcs: vcsGit, - Repo: "https://hub.jazz.net/git/user/pkg.name", - }, - }, - // User names cannot have uppercase letters - { - "hub.jazz.net/git/USER/pkgname", - nil, - }, - // OpenStack tests - { - "git.openstack.org/openstack/swift", - &RepoRoot{ - vcs: vcsGit, - Repo: "https://git.openstack.org/openstack/swift", - }, - }, - // Trailing .git is less preferred but included for - // compatibility purposes while the same source needs to - // be compilable on both old and new go - { - "git.openstack.org/openstack/swift.git", - &RepoRoot{ - vcs: vcsGit, - Repo: "https://git.openstack.org/openstack/swift.git", - }, - }, - { - "git.openstack.org/openstack/swift/go/hummingbird", - &RepoRoot{ - vcs: vcsGit, - Repo: "https://git.openstack.org/openstack/swift", - }, - }, - { - "git.openstack.org", - nil, - }, - { - "git.openstack.org/openstack", - nil, - }, - // Spaces are not valid in package name - { - "git.apache.org/package name/path/to/lib", - nil, - }, - // Should have ".git" suffix - { - "git.apache.org/package-name/path/to/lib", - nil, - }, - { - "gitbapache.org", - nil, - }, - { - "git.apache.org/package-name.git", - &RepoRoot{ - vcs: vcsGit, - Repo: "https://git.apache.org/package-name.git", - }, - }, - { - "git.apache.org/package-name_2.x.git/path/to/lib", - &RepoRoot{ - vcs: vcsGit, - Repo: "https://git.apache.org/package-name_2.x.git", - }, - }, - { - "chiselapp.com/user/kyle/repository/fossilgg", - &RepoRoot{ - vcs: vcsFossil, - Repo: "https://chiselapp.com/user/kyle/repository/fossilgg", - }, - }, - { - // must have a user/$name/repository/$repo path - "chiselapp.com/kyle/repository/fossilgg", - nil, - }, - { - "chiselapp.com/user/kyle/fossilgg", - nil, - }, - } - - for _, test := range tests { - got, err := RepoRootForImportPath(test.path, IgnoreMod, web.SecureOnly) - want := test.want - - if want == nil { - if err == nil { - t.Errorf("RepoRootForImportPath(%q): Error expected but not received", test.path) - } - continue - } - if err != nil { - t.Errorf("RepoRootForImportPath(%q): %v", test.path, err) - continue - } - if got.vcs.name != want.vcs.name || got.Repo != want.Repo { - t.Errorf("RepoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.vcs, got.Repo, want.vcs, want.Repo) - } - } -} - -// Test that vcsFromDir correctly inspects a given directory and returns the right VCS and root. -func TestFromDir(t *testing.T) { - tempDir, err := ioutil.TempDir("", "vcstest") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tempDir) - - for j, vcs := range vcsList { - dir := filepath.Join(tempDir, "example.com", vcs.name, "."+vcs.cmd) - if j&1 == 0 { - err := os.MkdirAll(dir, 0755) - if err != nil { - t.Fatal(err) - } - } else { - err := os.MkdirAll(filepath.Dir(dir), 0755) - if err != nil { - t.Fatal(err) - } - f, err := os.Create(dir) - if err != nil { - t.Fatal(err) - } - f.Close() - } - - want := RepoRoot{ - vcs: vcs, - Root: path.Join("example.com", vcs.name), - } - var got RepoRoot - got.vcs, got.Root, err = vcsFromDir(dir, tempDir) - if err != nil { - t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err) - continue - } - if got.vcs.name != want.vcs.name || got.Root != want.Root { - t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.vcs, got.Root, want.vcs, want.Root) - } - } -} - -func TestIsSecure(t *testing.T) { - tests := []struct { - vcs *vcsCmd - url string - secure bool - }{ - {vcsGit, "http://example.com/foo.git", false}, - {vcsGit, "https://example.com/foo.git", true}, - {vcsBzr, "http://example.com/foo.bzr", false}, - {vcsBzr, "https://example.com/foo.bzr", true}, - {vcsSvn, "http://example.com/svn", false}, - {vcsSvn, "https://example.com/svn", true}, - {vcsHg, "http://example.com/foo.hg", false}, - {vcsHg, "https://example.com/foo.hg", true}, - {vcsGit, "ssh://user@example.com/foo.git", true}, - {vcsGit, "user@server:path/to/repo.git", false}, - {vcsGit, "user@server:", false}, - {vcsGit, "server:repo.git", false}, - {vcsGit, "server:path/to/repo.git", false}, - {vcsGit, "example.com:path/to/repo.git", false}, - {vcsGit, "path/that/contains/a:colon/repo.git", false}, - {vcsHg, "ssh://user@example.com/path/to/repo.hg", true}, - {vcsFossil, "http://example.com/foo", false}, - {vcsFossil, "https://example.com/foo", true}, - } - - for _, test := range tests { - secure := test.vcs.isSecure(test.url) - if secure != test.secure { - t.Errorf("%s isSecure(%q) = %t; want %t", test.vcs, test.url, secure, test.secure) - } - } -} - -func TestIsSecureGitAllowProtocol(t *testing.T) { - tests := []struct { - vcs *vcsCmd - url string - secure bool - }{ - // Same as TestIsSecure to verify same behavior. - {vcsGit, "http://example.com/foo.git", false}, - {vcsGit, "https://example.com/foo.git", true}, - {vcsBzr, "http://example.com/foo.bzr", false}, - {vcsBzr, "https://example.com/foo.bzr", true}, - {vcsSvn, "http://example.com/svn", false}, - {vcsSvn, "https://example.com/svn", true}, - {vcsHg, "http://example.com/foo.hg", false}, - {vcsHg, "https://example.com/foo.hg", true}, - {vcsGit, "user@server:path/to/repo.git", false}, - {vcsGit, "user@server:", false}, - {vcsGit, "server:repo.git", false}, - {vcsGit, "server:path/to/repo.git", false}, - {vcsGit, "example.com:path/to/repo.git", false}, - {vcsGit, "path/that/contains/a:colon/repo.git", false}, - {vcsHg, "ssh://user@example.com/path/to/repo.hg", true}, - // New behavior. - {vcsGit, "ssh://user@example.com/foo.git", false}, - {vcsGit, "foo://example.com/bar.git", true}, - {vcsHg, "foo://example.com/bar.hg", false}, - {vcsSvn, "foo://example.com/svn", false}, - {vcsBzr, "foo://example.com/bar.bzr", false}, - } - - defer os.Unsetenv("GIT_ALLOW_PROTOCOL") - os.Setenv("GIT_ALLOW_PROTOCOL", "https:foo") - for _, test := range tests { - secure := test.vcs.isSecure(test.url) - if secure != test.secure { - t.Errorf("%s isSecure(%q) = %t; want %t", test.vcs, test.url, secure, test.secure) - } - } -} - -func TestMatchGoImport(t *testing.T) { - tests := []struct { - imports []metaImport - path string - mi metaImport - err error - }{ - { - imports: []metaImport{ - {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - }, - path: "example.com/user/foo", - mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - }, - { - imports: []metaImport{ - {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - }, - path: "example.com/user/foo/", - mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - }, - { - imports: []metaImport{ - {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - {Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - }, - path: "example.com/user/foo", - mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - }, - { - imports: []metaImport{ - {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - {Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - }, - path: "example.com/user/fooa", - mi: metaImport{Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - }, - { - imports: []metaImport{ - {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - }, - path: "example.com/user/foo/bar", - err: errors.New("should not be allowed to create nested repo"), - }, - { - imports: []metaImport{ - {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - }, - path: "example.com/user/foo/bar/baz", - err: errors.New("should not be allowed to create nested repo"), - }, - { - imports: []metaImport{ - {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - }, - path: "example.com/user/foo/bar/baz/qux", - err: errors.New("should not be allowed to create nested repo"), - }, - { - imports: []metaImport{ - {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - }, - path: "example.com/user/foo/bar/baz/", - err: errors.New("should not be allowed to create nested repo"), - }, - { - imports: []metaImport{ - {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - }, - path: "example.com", - err: errors.New("pathologically short path"), - }, - { - imports: []metaImport{ - {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, - }, - path: "different.example.com/user/foo", - err: errors.New("meta tags do not match import path"), - }, - { - imports: []metaImport{ - {Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"}, - {Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"}, - }, - path: "myitcv.io/blah2/foo", - mi: metaImport{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"}, - }, - { - imports: []metaImport{ - {Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"}, - {Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"}, - }, - path: "myitcv.io/other", - mi: metaImport{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"}, - }, - } - - for _, test := range tests { - mi, err := matchGoImport(test.imports, test.path) - if mi != test.mi { - t.Errorf("unexpected metaImport; got %v, want %v", mi, test.mi) - } - - got := err - want := test.err - if (got == nil) != (want == nil) { - t.Errorf("unexpected error; got %v, want %v", got, want) - } - } -} - -func TestValidateRepoRoot(t *testing.T) { - tests := []struct { - root string - ok bool - }{ - { - root: "", - ok: false, - }, - { - root: "http://", - ok: true, - }, - { - root: "git+ssh://", - ok: true, - }, - { - root: "http#://", - ok: false, - }, - { - root: "-config", - ok: false, - }, - { - root: "-config://", - ok: false, - }, - } - - for _, test := range tests { - err := validateRepoRoot(test.root) - ok := err == nil - if ok != test.ok { - want := "error" - if test.ok { - want = "nil" - } - t.Errorf("validateRepoRoot(%q) = %q, want %s", test.root, err, want) - } - } -} diff --git a/src/cmd/go/internal/modfetch/repo.go b/src/cmd/go/internal/modfetch/repo.go index 34f805d58a..eed4dd4258 100644 --- a/src/cmd/go/internal/modfetch/repo.go +++ b/src/cmd/go/internal/modfetch/repo.go @@ -13,9 +13,9 @@ import ( "time" "cmd/go/internal/cfg" - "cmd/go/internal/get" "cmd/go/internal/modfetch/codehost" "cmd/go/internal/par" + "cmd/go/internal/vcs" web "cmd/go/internal/web" "golang.org/x/mod/module" @@ -261,13 +261,13 @@ func lookupDirect(path string) (Repo, error) { if allowInsecure(path) { security = web.Insecure } - rr, err := get.RepoRootForImportPath(path, get.PreferMod, security) + rr, err := vcs.RepoRootForImportPath(path, vcs.PreferMod, security) if err != nil { // We don't know where to find code for a module with this path. return nil, notExistError{err: err} } - if rr.VCS == "mod" { + if rr.VCS.Name == "mod" { // Fetch module from proxy with base URL rr.Repo. return newProxyRepo(rr.Repo, path) } @@ -279,8 +279,8 @@ func lookupDirect(path string) (Repo, error) { return newCodeRepo(code, rr.Root, path) } -func lookupCodeRepo(rr *get.RepoRoot) (codehost.Repo, error) { - code, err := codehost.NewRepo(rr.VCS, rr.Repo) +func lookupCodeRepo(rr *vcs.RepoRoot) (codehost.Repo, error) { + code, err := codehost.NewRepo(rr.VCS.Cmd, rr.Repo) if err != nil { if _, ok := err.(*codehost.VCSError); ok { return nil, err @@ -306,7 +306,7 @@ func ImportRepoRev(path, rev string) (Repo, *RevInfo, error) { if allowInsecure(path) { security = web.Insecure } - rr, err := get.RepoRootForImportPath(path, get.IgnoreMod, security) + rr, err := vcs.RepoRootForImportPath(path, vcs.IgnoreMod, security) if err != nil { return nil, nil, err } diff --git a/src/cmd/go/internal/str/str_test.go b/src/cmd/go/internal/str/str_test.go new file mode 100644 index 0000000000..147ce1a63e --- /dev/null +++ b/src/cmd/go/internal/str/str_test.go @@ -0,0 +1,27 @@ +// 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 str + +import "testing" + +var foldDupTests = []struct { + list []string + f1, f2 string +}{ + {StringList("math/rand", "math/big"), "", ""}, + {StringList("math", "strings"), "", ""}, + {StringList("strings"), "", ""}, + {StringList("strings", "strings"), "strings", "strings"}, + {StringList("Rand", "rand", "math", "math/rand", "math/Rand"), "Rand", "rand"}, +} + +func TestFoldDup(t *testing.T) { + for _, tt := range foldDupTests { + f1, f2 := FoldDup(tt.list) + if f1 != tt.f1 || f2 != tt.f2 { + t.Errorf("foldDup(%q) = %q, %q, want %q, %q", tt.list, f1, f2, tt.f1, tt.f2) + } + } +} diff --git a/src/cmd/go/internal/vcs/discovery.go b/src/cmd/go/internal/vcs/discovery.go new file mode 100644 index 0000000000..327b44cb9a --- /dev/null +++ b/src/cmd/go/internal/vcs/discovery.go @@ -0,0 +1,97 @@ +// Copyright 2012 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 vcs + +import ( + "encoding/xml" + "fmt" + "io" + "strings" +) + +// charsetReader returns a reader that converts from the given charset to UTF-8. +// Currently it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful +// error which is printed by go get, so the user can find why the package +// wasn't downloaded if the encoding is not supported. Note that, in +// order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters +// greater than 0x7f are not rejected). +func charsetReader(charset string, input io.Reader) (io.Reader, error) { + switch strings.ToLower(charset) { + case "utf-8", "ascii": + return input, nil + default: + return nil, fmt.Errorf("can't decode XML document using charset %q", charset) + } +} + +// parseMetaGoImports returns meta imports from the HTML in r. +// Parsing ends at the end of the section or the beginning of the . +func parseMetaGoImports(r io.Reader, mod ModuleMode) ([]metaImport, error) { + d := xml.NewDecoder(r) + d.CharsetReader = charsetReader + d.Strict = false + var imports []metaImport + for { + t, err := d.RawToken() + if err != nil { + if err != io.EOF && len(imports) == 0 { + return nil, err + } + break + } + if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") { + break + } + if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") { + break + } + e, ok := t.(xml.StartElement) + if !ok || !strings.EqualFold(e.Name.Local, "meta") { + continue + } + if attrValue(e.Attr, "name") != "go-import" { + continue + } + if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 { + imports = append(imports, metaImport{ + Prefix: f[0], + VCS: f[1], + RepoRoot: f[2], + }) + } + } + + // Extract mod entries if we are paying attention to them. + var list []metaImport + var have map[string]bool + if mod == PreferMod { + have = make(map[string]bool) + for _, m := range imports { + if m.VCS == "mod" { + have[m.Prefix] = true + list = append(list, m) + } + } + } + + // Append non-mod entries, ignoring those superseded by a mod entry. + for _, m := range imports { + if m.VCS != "mod" && !have[m.Prefix] { + list = append(list, m) + } + } + return list, nil +} + +// attrValue returns the attribute value for the case-insensitive key +// `name', or the empty string if nothing is found. +func attrValue(attrs []xml.Attr, name string) string { + for _, a := range attrs { + if strings.EqualFold(a.Name.Local, name) { + return a.Value + } + } + return "" +} diff --git a/src/cmd/go/internal/vcs/discovery_test.go b/src/cmd/go/internal/vcs/discovery_test.go new file mode 100644 index 0000000000..eb99fdf64c --- /dev/null +++ b/src/cmd/go/internal/vcs/discovery_test.go @@ -0,0 +1,110 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package vcs + +import ( + "reflect" + "strings" + "testing" +) + +var parseMetaGoImportsTests = []struct { + in string + mod ModuleMode + out []metaImport +}{ + { + ``, + IgnoreMod, + []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}}, + }, + { + ` + `, + IgnoreMod, + []metaImport{ + {"foo/bar", "git", "https://github.com/rsc/foo/bar"}, + {"baz/quux", "git", "http://github.com/rsc/baz/quux"}, + }, + }, + { + ` + `, + IgnoreMod, + []metaImport{ + {"foo/bar", "git", "https://github.com/rsc/foo/bar"}, + }, + }, + { + ` + `, + IgnoreMod, + []metaImport{ + {"foo/bar", "git", "https://github.com/rsc/foo/bar"}, + }, + }, + { + ` + `, + PreferMod, + []metaImport{ + {"foo/bar", "mod", "http://github.com/rsc/baz/quux"}, + }, + }, + { + ` + + `, + IgnoreMod, + []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}}, + }, + { + ` + `, + IgnoreMod, + []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}}, + }, + { + ``, + IgnoreMod, + []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}}, + }, + { + // XML doesn't like
. + `Page Not Found
DRAFT
`, + IgnoreMod, + []metaImport{{"chitin.io/chitin", "git", "https://github.com/chitin-io/chitin"}}, + }, + { + ` + + `, + IgnoreMod, + []metaImport{{"myitcv.io", "git", "https://github.com/myitcv/x"}}, + }, + { + ` + + `, + PreferMod, + []metaImport{ + {"myitcv.io/blah2", "mod", "https://raw.githubusercontent.com/myitcv/pubx/master"}, + {"myitcv.io", "git", "https://github.com/myitcv/x"}, + }, + }, +} + +func TestParseMetaGoImports(t *testing.T) { + for i, tt := range parseMetaGoImportsTests { + out, err := parseMetaGoImports(strings.NewReader(tt.in), tt.mod) + if err != nil { + t.Errorf("test#%d: %v", i, err) + continue + } + if !reflect.DeepEqual(out, tt.out) { + t.Errorf("test#%d:\n\thave %q\n\twant %q", i, out, tt.out) + } + } +} diff --git a/src/cmd/go/internal/vcs/vcs.go b/src/cmd/go/internal/vcs/vcs.go new file mode 100644 index 0000000000..e535998d89 --- /dev/null +++ b/src/cmd/go/internal/vcs/vcs.go @@ -0,0 +1,1187 @@ +// Copyright 2012 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 vcs + +import ( + "encoding/json" + "errors" + "fmt" + "internal/lazyregexp" + "internal/singleflight" + "log" + urlpkg "net/url" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" + "sync" + + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/load" + "cmd/go/internal/web" +) + +// A vcsCmd describes how to use a version control system +// like Mercurial, Git, or Subversion. +type Cmd struct { + Name string + Cmd string // name of binary to invoke command + + CreateCmd []string // commands to download a fresh copy of a repository + DownloadCmd []string // commands to download updates into an existing repository + + TagCmd []tagCmd // commands to list tags + TagLookupCmd []tagCmd // commands to lookup tags before running tagSyncCmd + TagSyncCmd []string // commands to sync to specific tag + TagSyncDefault []string // commands to sync to default tag + + Scheme []string + PingCmd string + + RemoteRepo func(v *Cmd, rootDir string) (remoteRepo string, err error) + ResolveRepo func(v *Cmd, rootDir, remoteRepo string) (realRepo string, err error) +} + +var defaultSecureScheme = map[string]bool{ + "https": true, + "git+ssh": true, + "bzr+ssh": true, + "svn+ssh": true, + "ssh": true, +} + +func (v *Cmd) IsSecure(repo string) bool { + u, err := urlpkg.Parse(repo) + if err != nil { + // If repo is not a URL, it's not secure. + return false + } + return v.isSecureScheme(u.Scheme) +} + +func (v *Cmd) isSecureScheme(scheme string) bool { + switch v.Cmd { + case "git": + // GIT_ALLOW_PROTOCOL is an environment variable defined by Git. It is a + // colon-separated list of schemes that are allowed to be used with git + // fetch/clone. Any scheme not mentioned will be considered insecure. + if allow := os.Getenv("GIT_ALLOW_PROTOCOL"); allow != "" { + for _, s := range strings.Split(allow, ":") { + if s == scheme { + return true + } + } + return false + } + } + return defaultSecureScheme[scheme] +} + +// A tagCmd describes a command to list available tags +// that can be passed to tagSyncCmd. +type tagCmd struct { + cmd string // command to list tags + pattern string // regexp to extract tags from list +} + +// vcsList lists the known version control systems +var vcsList = []*Cmd{ + vcsHg, + vcsGit, + vcsSvn, + vcsBzr, + vcsFossil, +} + +// vcsMod is a stub for the "mod" scheme. It's returned by +// repoRootForImportPathDynamic, but is otherwise not treated as a VCS command. +var vcsMod = &Cmd{Name: "mod"} + +// vcsByCmd returns the version control system for the given +// command name (hg, git, svn, bzr). +func vcsByCmd(cmd string) *Cmd { + for _, vcs := range vcsList { + if vcs.Cmd == cmd { + return vcs + } + } + return nil +} + +// vcsHg describes how to use Mercurial. +var vcsHg = &Cmd{ + Name: "Mercurial", + Cmd: "hg", + + CreateCmd: []string{"clone -U -- {repo} {dir}"}, + DownloadCmd: []string{"pull"}, + + // We allow both tag and branch names as 'tags' + // for selecting a version. This lets people have + // a go.release.r60 branch and a go1 branch + // and make changes in both, without constantly + // editing .hgtags. + TagCmd: []tagCmd{ + {"tags", `^(\S+)`}, + {"branches", `^(\S+)`}, + }, + TagSyncCmd: []string{"update -r {tag}"}, + TagSyncDefault: []string{"update default"}, + + Scheme: []string{"https", "http", "ssh"}, + PingCmd: "identify -- {scheme}://{repo}", + RemoteRepo: hgRemoteRepo, +} + +func hgRemoteRepo(vcsHg *Cmd, rootDir string) (remoteRepo string, err error) { + out, err := vcsHg.runOutput(rootDir, "paths default") + if err != nil { + return "", err + } + return strings.TrimSpace(string(out)), nil +} + +// vcsGit describes how to use Git. +var vcsGit = &Cmd{ + Name: "Git", + Cmd: "git", + + CreateCmd: []string{"clone -- {repo} {dir}", "-go-internal-cd {dir} submodule update --init --recursive"}, + DownloadCmd: []string{"pull --ff-only", "submodule update --init --recursive"}, + + TagCmd: []tagCmd{ + // tags/xxx matches a git tag named xxx + // origin/xxx matches a git branch named xxx on the default remote repository + {"show-ref", `(?:tags|origin)/(\S+)$`}, + }, + TagLookupCmd: []tagCmd{ + {"show-ref tags/{tag} origin/{tag}", `((?:tags|origin)/\S+)$`}, + }, + TagSyncCmd: []string{"checkout {tag}", "submodule update --init --recursive"}, + // both createCmd and downloadCmd update the working dir. + // No need to do more here. We used to 'checkout master' + // but that doesn't work if the default branch is not named master. + // DO NOT add 'checkout master' here. + // See golang.org/issue/9032. + TagSyncDefault: []string{"submodule update --init --recursive"}, + + Scheme: []string{"git", "https", "http", "git+ssh", "ssh"}, + + // Leave out the '--' separator in the ls-remote command: git 2.7.4 does not + // support such a separator for that command, and this use should be safe + // without it because the {scheme} value comes from the predefined list above. + // See golang.org/issue/33836. + PingCmd: "ls-remote {scheme}://{repo}", + + RemoteRepo: gitRemoteRepo, +} + +// scpSyntaxRe matches the SCP-like addresses used by Git to access +// repositories by SSH. +var scpSyntaxRe = lazyregexp.New(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`) + +func gitRemoteRepo(vcsGit *Cmd, rootDir string) (remoteRepo string, err error) { + cmd := "config remote.origin.url" + errParse := errors.New("unable to parse output of git " + cmd) + errRemoteOriginNotFound := errors.New("remote origin not found") + outb, err := vcsGit.run1(rootDir, cmd, nil, false) + if err != nil { + // if it doesn't output any message, it means the config argument is correct, + // but the config value itself doesn't exist + if outb != nil && len(outb) == 0 { + return "", errRemoteOriginNotFound + } + return "", err + } + out := strings.TrimSpace(string(outb)) + + var repoURL *urlpkg.URL + if m := scpSyntaxRe.FindStringSubmatch(out); m != nil { + // Match SCP-like syntax and convert it to a URL. + // Eg, "git@github.com:user/repo" becomes + // "ssh://git@github.com/user/repo". + repoURL = &urlpkg.URL{ + Scheme: "ssh", + User: urlpkg.User(m[1]), + Host: m[2], + Path: m[3], + } + } else { + repoURL, err = urlpkg.Parse(out) + if err != nil { + return "", err + } + } + + // Iterate over insecure schemes too, because this function simply + // reports the state of the repo. If we can't see insecure schemes then + // we can't report the actual repo URL. + for _, s := range vcsGit.Scheme { + if repoURL.Scheme == s { + return repoURL.String(), nil + } + } + return "", errParse +} + +// vcsBzr describes how to use Bazaar. +var vcsBzr = &Cmd{ + Name: "Bazaar", + Cmd: "bzr", + + CreateCmd: []string{"branch -- {repo} {dir}"}, + + // Without --overwrite bzr will not pull tags that changed. + // Replace by --overwrite-tags after http://pad.lv/681792 goes in. + DownloadCmd: []string{"pull --overwrite"}, + + TagCmd: []tagCmd{{"tags", `^(\S+)`}}, + TagSyncCmd: []string{"update -r {tag}"}, + TagSyncDefault: []string{"update -r revno:-1"}, + + Scheme: []string{"https", "http", "bzr", "bzr+ssh"}, + PingCmd: "info -- {scheme}://{repo}", + RemoteRepo: bzrRemoteRepo, + ResolveRepo: bzrResolveRepo, +} + +func bzrRemoteRepo(vcsBzr *Cmd, rootDir string) (remoteRepo string, err error) { + outb, err := vcsBzr.runOutput(rootDir, "config parent_location") + if err != nil { + return "", err + } + return strings.TrimSpace(string(outb)), nil +} + +func bzrResolveRepo(vcsBzr *Cmd, rootDir, remoteRepo string) (realRepo string, err error) { + outb, err := vcsBzr.runOutput(rootDir, "info "+remoteRepo) + if err != nil { + return "", err + } + out := string(outb) + + // Expect: + // ... + // (branch root|repository branch): + // ... + + found := false + for _, prefix := range []string{"\n branch root: ", "\n repository branch: "} { + i := strings.Index(out, prefix) + if i >= 0 { + out = out[i+len(prefix):] + found = true + break + } + } + if !found { + return "", fmt.Errorf("unable to parse output of bzr info") + } + + i := strings.Index(out, "\n") + if i < 0 { + return "", fmt.Errorf("unable to parse output of bzr info") + } + out = out[:i] + return strings.TrimSpace(out), nil +} + +// vcsSvn describes how to use Subversion. +var vcsSvn = &Cmd{ + Name: "Subversion", + Cmd: "svn", + + CreateCmd: []string{"checkout -- {repo} {dir}"}, + DownloadCmd: []string{"update"}, + + // There is no tag command in subversion. + // The branch information is all in the path names. + + Scheme: []string{"https", "http", "svn", "svn+ssh"}, + PingCmd: "info -- {scheme}://{repo}", + RemoteRepo: svnRemoteRepo, +} + +func svnRemoteRepo(vcsSvn *Cmd, rootDir string) (remoteRepo string, err error) { + outb, err := vcsSvn.runOutput(rootDir, "info") + if err != nil { + return "", err + } + out := string(outb) + + // Expect: + // + // ... + // URL: + // ... + // + // Note that we're not using the Repository Root line, + // because svn allows checking out subtrees. + // The URL will be the URL of the subtree (what we used with 'svn co') + // while the Repository Root may be a much higher parent. + i := strings.Index(out, "\nURL: ") + if i < 0 { + return "", fmt.Errorf("unable to parse output of svn info") + } + out = out[i+len("\nURL: "):] + i = strings.Index(out, "\n") + if i < 0 { + return "", fmt.Errorf("unable to parse output of svn info") + } + out = out[:i] + return strings.TrimSpace(out), nil +} + +// fossilRepoName is the name go get associates with a fossil repository. In the +// real world the file can be named anything. +const fossilRepoName = ".fossil" + +// vcsFossil describes how to use Fossil (fossil-scm.org) +var vcsFossil = &Cmd{ + Name: "Fossil", + Cmd: "fossil", + + CreateCmd: []string{"-go-internal-mkdir {dir} clone -- {repo} " + filepath.Join("{dir}", fossilRepoName), "-go-internal-cd {dir} open .fossil"}, + DownloadCmd: []string{"up"}, + + TagCmd: []tagCmd{{"tag ls", `(.*)`}}, + TagSyncCmd: []string{"up tag:{tag}"}, + TagSyncDefault: []string{"up trunk"}, + + Scheme: []string{"https", "http"}, + RemoteRepo: fossilRemoteRepo, +} + +func fossilRemoteRepo(vcsFossil *Cmd, rootDir string) (remoteRepo string, err error) { + out, err := vcsFossil.runOutput(rootDir, "remote-url") + if err != nil { + return "", err + } + return strings.TrimSpace(string(out)), nil +} + +func (v *Cmd) String() string { + return v.Name +} + +// run runs the command line cmd in the given directory. +// keyval is a list of key, value pairs. run expands +// instances of {key} in cmd into value, but only after +// splitting cmd into individual arguments. +// If an error occurs, run prints the command line and the +// command's combined stdout+stderr to standard error. +// Otherwise run discards the command's output. +func (v *Cmd) run(dir string, cmd string, keyval ...string) error { + _, err := v.run1(dir, cmd, keyval, true) + return err +} + +// runVerboseOnly is like run but only generates error output to standard error in verbose mode. +func (v *Cmd) runVerboseOnly(dir string, cmd string, keyval ...string) error { + _, err := v.run1(dir, cmd, keyval, false) + return err +} + +// runOutput is like run but returns the output of the command. +func (v *Cmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) { + return v.run1(dir, cmd, keyval, true) +} + +// run1 is the generalized implementation of run and runOutput. +func (v *Cmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) { + m := make(map[string]string) + for i := 0; i < len(keyval); i += 2 { + m[keyval[i]] = keyval[i+1] + } + args := strings.Fields(cmdline) + for i, arg := range args { + args[i] = expand(m, arg) + } + + if len(args) >= 2 && args[0] == "-go-internal-mkdir" { + var err error + if filepath.IsAbs(args[1]) { + err = os.Mkdir(args[1], os.ModePerm) + } else { + err = os.Mkdir(filepath.Join(dir, args[1]), os.ModePerm) + } + if err != nil { + return nil, err + } + args = args[2:] + } + + if len(args) >= 2 && args[0] == "-go-internal-cd" { + if filepath.IsAbs(args[1]) { + dir = args[1] + } else { + dir = filepath.Join(dir, args[1]) + } + args = args[2:] + } + + _, err := exec.LookPath(v.Cmd) + if err != nil { + fmt.Fprintf(os.Stderr, + "go: missing %s command. See https://golang.org/s/gogetcmd\n", + v.Name) + return nil, err + } + + cmd := exec.Command(v.Cmd, args...) + cmd.Dir = dir + cmd.Env = base.AppendPWD(os.Environ(), cmd.Dir) + if cfg.BuildX { + fmt.Fprintf(os.Stderr, "cd %s\n", dir) + fmt.Fprintf(os.Stderr, "%s %s\n", v.Cmd, strings.Join(args, " ")) + } + out, err := cmd.Output() + if err != nil { + if verbose || cfg.BuildV { + fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.Cmd, strings.Join(args, " ")) + if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 { + os.Stderr.Write(ee.Stderr) + } else { + fmt.Fprintf(os.Stderr, err.Error()) + } + } + } + return out, err +} + +// Ping pings to determine scheme to use. +func (v *Cmd) Ping(scheme, repo string) error { + return v.runVerboseOnly(".", v.PingCmd, "scheme", scheme, "repo", repo) +} + +// Create creates a new copy of repo in dir. +// The parent of dir must exist; dir must not. +func (v *Cmd) Create(dir, repo string) error { + for _, cmd := range v.CreateCmd { + if err := v.run(".", cmd, "dir", dir, "repo", repo); err != nil { + return err + } + } + return nil +} + +// Download downloads any new changes for the repo in dir. +func (v *Cmd) Download(dir string) error { + for _, cmd := range v.DownloadCmd { + if err := v.run(dir, cmd); err != nil { + return err + } + } + return nil +} + +// Tags returns the list of available tags for the repo in dir. +func (v *Cmd) Tags(dir string) ([]string, error) { + var tags []string + for _, tc := range v.TagCmd { + out, err := v.runOutput(dir, tc.cmd) + if err != nil { + return nil, err + } + re := regexp.MustCompile(`(?m-s)` + tc.pattern) + for _, m := range re.FindAllStringSubmatch(string(out), -1) { + tags = append(tags, m[1]) + } + } + return tags, nil +} + +// tagSync syncs the repo in dir to the named tag, +// which either is a tag returned by tags or is v.tagDefault. +func (v *Cmd) TagSync(dir, tag string) error { + if v.TagSyncCmd == nil { + return nil + } + if tag != "" { + for _, tc := range v.TagLookupCmd { + out, err := v.runOutput(dir, tc.cmd, "tag", tag) + if err != nil { + return err + } + re := regexp.MustCompile(`(?m-s)` + tc.pattern) + m := re.FindStringSubmatch(string(out)) + if len(m) > 1 { + tag = m[1] + break + } + } + } + + if tag == "" && v.TagSyncDefault != nil { + for _, cmd := range v.TagSyncDefault { + if err := v.run(dir, cmd); err != nil { + return err + } + } + return nil + } + + for _, cmd := range v.TagSyncCmd { + if err := v.run(dir, cmd, "tag", tag); err != nil { + return err + } + } + return nil +} + +// A vcsPath describes how to convert an import path into a +// version control system and repository name. +type vcsPath struct { + prefix string // prefix this description applies to + regexp *lazyregexp.Regexp // compiled pattern for import path + repo string // repository to use (expand with match of re) + vcs string // version control system to use (expand with match of re) + check func(match map[string]string) error // additional checks + schemelessRepo bool // if true, the repo pattern lacks a scheme +} + +// FromDir inspects dir and its parents to determine the +// version control system and code repository to use. +// On return, root is the import path +// corresponding to the root of the repository. +func FromDir(dir, srcRoot string) (vcs *Cmd, root string, err error) { + // Clean and double-check that dir is in (a subdirectory of) srcRoot. + dir = filepath.Clean(dir) + srcRoot = filepath.Clean(srcRoot) + if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator { + return nil, "", fmt.Errorf("directory %q is outside source root %q", dir, srcRoot) + } + + var vcsRet *Cmd + var rootRet string + + origDir := dir + for len(dir) > len(srcRoot) { + for _, vcs := range vcsList { + if _, err := os.Stat(filepath.Join(dir, "."+vcs.Cmd)); err == nil { + root := filepath.ToSlash(dir[len(srcRoot)+1:]) + // Record first VCS we find, but keep looking, + // to detect mistakes like one kind of VCS inside another. + if vcsRet == nil { + vcsRet = vcs + rootRet = root + continue + } + // Allow .git inside .git, which can arise due to submodules. + if vcsRet == vcs && vcs.Cmd == "git" { + continue + } + // Otherwise, we have one VCS inside a different VCS. + return nil, "", fmt.Errorf("directory %q uses %s, but parent %q uses %s", + filepath.Join(srcRoot, rootRet), vcsRet.Cmd, filepath.Join(srcRoot, root), vcs.Cmd) + } + } + + // Move to parent. + ndir := filepath.Dir(dir) + if len(ndir) >= len(dir) { + // Shouldn't happen, but just in case, stop. + break + } + dir = ndir + } + + if vcsRet != nil { + return vcsRet, rootRet, nil + } + + return nil, "", fmt.Errorf("directory %q is not using a known version control system", origDir) +} + +// CheckNested checks for an incorrectly-nested VCS-inside-VCS +// situation for dir, checking parents up until srcRoot. +func CheckNested(vcs *Cmd, dir, srcRoot string) error { + if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator { + return fmt.Errorf("directory %q is outside source root %q", dir, srcRoot) + } + + otherDir := dir + for len(otherDir) > len(srcRoot) { + for _, otherVCS := range vcsList { + if _, err := os.Stat(filepath.Join(otherDir, "."+otherVCS.Cmd)); err == nil { + // Allow expected vcs in original dir. + if otherDir == dir && otherVCS == vcs { + continue + } + // Allow .git inside .git, which can arise due to submodules. + if otherVCS == vcs && vcs.Cmd == "git" { + continue + } + // Otherwise, we have one VCS inside a different VCS. + return fmt.Errorf("directory %q uses %s, but parent %q uses %s", dir, vcs.Cmd, otherDir, otherVCS.Cmd) + } + } + // Move to parent. + newDir := filepath.Dir(otherDir) + if len(newDir) >= len(otherDir) { + // Shouldn't happen, but just in case, stop. + break + } + otherDir = newDir + } + + return nil +} + +// RepoRoot describes the repository root for a tree of source code. +type RepoRoot struct { + Repo string // repository URL, including scheme + Root string // import path corresponding to root of repo + IsCustom bool // defined by served tags (as opposed to hard-coded pattern) + VCS *Cmd +} + +func httpPrefix(s string) string { + for _, prefix := range [...]string{"http:", "https:"} { + if strings.HasPrefix(s, prefix) { + return prefix + } + } + return "" +} + +// ModuleMode specifies whether to prefer modules when looking up code sources. +type ModuleMode int + +const ( + IgnoreMod ModuleMode = iota + PreferMod +) + +// RepoRootForImportPath analyzes importPath to determine the +// version control system, and code repository to use. +func RepoRootForImportPath(importPath string, mod ModuleMode, security web.SecurityMode) (*RepoRoot, error) { + rr, err := repoRootFromVCSPaths(importPath, security, vcsPaths) + if err == errUnknownSite { + rr, err = repoRootForImportDynamic(importPath, mod, security) + if err != nil { + err = load.ImportErrorf(importPath, "unrecognized import path %q: %v", importPath, err) + } + } + if err != nil { + rr1, err1 := repoRootFromVCSPaths(importPath, security, vcsPathsAfterDynamic) + if err1 == nil { + rr = rr1 + err = nil + } + } + + // Should have been taken care of above, but make sure. + if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.Root, "...") { + // Do not allow wildcards in the repo root. + rr = nil + err = load.ImportErrorf(importPath, "cannot expand ... in %q", importPath) + } + return rr, err +} + +var errUnknownSite = errors.New("dynamic lookup required to find mapping") + +// repoRootFromVCSPaths attempts to map importPath to a repoRoot +// using the mappings defined in vcsPaths. +func repoRootFromVCSPaths(importPath string, security web.SecurityMode, vcsPaths []*vcsPath) (*RepoRoot, error) { + // A common error is to use https://packagepath because that's what + // hg and git require. Diagnose this helpfully. + if prefix := httpPrefix(importPath); prefix != "" { + // The importPath has been cleaned, so has only one slash. The pattern + // ignores the slashes; the error message puts them back on the RHS at least. + return nil, fmt.Errorf("%q not allowed in import path", prefix+"//") + } + for _, srv := range vcsPaths { + if !strings.HasPrefix(importPath, srv.prefix) { + continue + } + m := srv.regexp.FindStringSubmatch(importPath) + if m == nil { + if srv.prefix != "" { + return nil, load.ImportErrorf(importPath, "invalid %s import path %q", srv.prefix, importPath) + } + continue + } + + // Build map of named subexpression matches for expand. + match := map[string]string{ + "prefix": srv.prefix, + "import": importPath, + } + for i, name := range srv.regexp.SubexpNames() { + if name != "" && match[name] == "" { + match[name] = m[i] + } + } + if srv.vcs != "" { + match["vcs"] = expand(match, srv.vcs) + } + if srv.repo != "" { + match["repo"] = expand(match, srv.repo) + } + if srv.check != nil { + if err := srv.check(match); err != nil { + return nil, err + } + } + vcs := vcsByCmd(match["vcs"]) + if vcs == nil { + return nil, fmt.Errorf("unknown version control system %q", match["vcs"]) + } + var repoURL string + if !srv.schemelessRepo { + repoURL = match["repo"] + } else { + scheme := vcs.Scheme[0] // default to first scheme + repo := match["repo"] + if vcs.PingCmd != "" { + // If we know how to test schemes, scan to find one. + for _, s := range vcs.Scheme { + if security == web.SecureOnly && !vcs.isSecureScheme(s) { + continue + } + if vcs.Ping(s, repo) == nil { + scheme = s + break + } + } + } + repoURL = scheme + "://" + repo + } + rr := &RepoRoot{ + Repo: repoURL, + Root: match["root"], + VCS: vcs, + } + return rr, nil + } + return nil, errUnknownSite +} + +// urlForImportPath returns a partially-populated URL for the given Go import path. +// +// The URL leaves the Scheme field blank so that web.Get will try any scheme +// allowed by the selected security mode. +func urlForImportPath(importPath string) (*urlpkg.URL, error) { + slash := strings.Index(importPath, "/") + if slash < 0 { + slash = len(importPath) + } + host, path := importPath[:slash], importPath[slash:] + if !strings.Contains(host, ".") { + return nil, errors.New("import path does not begin with hostname") + } + if len(path) == 0 { + path = "/" + } + return &urlpkg.URL{Host: host, Path: path, RawQuery: "go-get=1"}, nil +} + +// repoRootForImportDynamic finds a *RepoRoot for a custom domain that's not +// statically known by repoRootForImportPathStatic. +// +// This handles custom import paths like "name.tld/pkg/foo" or just "name.tld". +func repoRootForImportDynamic(importPath string, mod ModuleMode, security web.SecurityMode) (*RepoRoot, error) { + url, err := urlForImportPath(importPath) + if err != nil { + return nil, err + } + resp, err := web.Get(security, url) + if err != nil { + msg := "https fetch: %v" + if security == web.Insecure { + msg = "http/" + msg + } + return nil, fmt.Errorf(msg, err) + } + body := resp.Body + defer body.Close() + imports, err := parseMetaGoImports(body, mod) + if len(imports) == 0 { + if respErr := resp.Err(); respErr != nil { + // If the server's status was not OK, prefer to report that instead of + // an XML parse error. + return nil, respErr + } + } + if err != nil { + return nil, fmt.Errorf("parsing %s: %v", importPath, err) + } + // Find the matched meta import. + mmi, err := matchGoImport(imports, importPath) + if err != nil { + if _, ok := err.(ImportMismatchError); !ok { + return nil, fmt.Errorf("parse %s: %v", url, err) + } + return nil, fmt.Errorf("parse %s: no go-import meta tags (%s)", resp.URL, err) + } + if cfg.BuildV { + log.Printf("get %q: found meta tag %#v at %s", importPath, mmi, url) + } + // If the import was "uni.edu/bob/project", which said the + // prefix was "uni.edu" and the RepoRoot was "evilroot.com", + // make sure we don't trust Bob and check out evilroot.com to + // "uni.edu" yet (possibly overwriting/preempting another + // non-evil student). Instead, first verify the root and see + // if it matches Bob's claim. + if mmi.Prefix != importPath { + if cfg.BuildV { + log.Printf("get %q: verifying non-authoritative meta tag", importPath) + } + var imports []metaImport + url, imports, err = metaImportsForPrefix(mmi.Prefix, mod, security) + if err != nil { + return nil, err + } + metaImport2, err := matchGoImport(imports, importPath) + if err != nil || mmi != metaImport2 { + return nil, fmt.Errorf("%s and %s disagree about go-import for %s", resp.URL, url, mmi.Prefix) + } + } + + if err := validateRepoRoot(mmi.RepoRoot); err != nil { + return nil, fmt.Errorf("%s: invalid repo root %q: %v", resp.URL, mmi.RepoRoot, err) + } + var vcs *Cmd + if mmi.VCS == "mod" { + vcs = vcsMod + } else { + vcs = vcsByCmd(mmi.VCS) + if vcs == nil { + return nil, fmt.Errorf("%s: unknown vcs %q", resp.URL, mmi.VCS) + } + } + + rr := &RepoRoot{ + Repo: mmi.RepoRoot, + Root: mmi.Prefix, + IsCustom: true, + VCS: vcs, + } + return rr, nil +} + +// validateRepoRoot returns an error if repoRoot does not seem to be +// a valid URL with scheme. +func validateRepoRoot(repoRoot string) error { + url, err := urlpkg.Parse(repoRoot) + if err != nil { + return err + } + if url.Scheme == "" { + return errors.New("no scheme") + } + if url.Scheme == "file" { + return errors.New("file scheme disallowed") + } + return nil +} + +var fetchGroup singleflight.Group +var ( + fetchCacheMu sync.Mutex + fetchCache = map[string]fetchResult{} // key is metaImportsForPrefix's importPrefix +) + +// metaImportsForPrefix takes a package's root import path as declared in a tag +// and returns its HTML discovery URL and the parsed metaImport lines +// found on the page. +// +// The importPath is of the form "golang.org/x/tools". +// It is an error if no imports are found. +// url will still be valid if err != nil. +// The returned url will be of the form "https://golang.org/x/tools?go-get=1" +func metaImportsForPrefix(importPrefix string, mod ModuleMode, security web.SecurityMode) (*urlpkg.URL, []metaImport, error) { + setCache := func(res fetchResult) (fetchResult, error) { + fetchCacheMu.Lock() + defer fetchCacheMu.Unlock() + fetchCache[importPrefix] = res + return res, nil + } + + resi, _, _ := fetchGroup.Do(importPrefix, func() (resi interface{}, err error) { + fetchCacheMu.Lock() + if res, ok := fetchCache[importPrefix]; ok { + fetchCacheMu.Unlock() + return res, nil + } + fetchCacheMu.Unlock() + + url, err := urlForImportPath(importPrefix) + if err != nil { + return setCache(fetchResult{err: err}) + } + resp, err := web.Get(security, url) + if err != nil { + return setCache(fetchResult{url: url, err: fmt.Errorf("fetching %s: %v", importPrefix, err)}) + } + body := resp.Body + defer body.Close() + imports, err := parseMetaGoImports(body, mod) + if len(imports) == 0 { + if respErr := resp.Err(); respErr != nil { + // If the server's status was not OK, prefer to report that instead of + // an XML parse error. + return setCache(fetchResult{url: url, err: respErr}) + } + } + if err != nil { + return setCache(fetchResult{url: url, err: fmt.Errorf("parsing %s: %v", resp.URL, err)}) + } + if len(imports) == 0 { + err = fmt.Errorf("fetching %s: no go-import meta tag found in %s", importPrefix, resp.URL) + } + return setCache(fetchResult{url: url, imports: imports, err: err}) + }) + res := resi.(fetchResult) + return res.url, res.imports, res.err +} + +type fetchResult struct { + url *urlpkg.URL + imports []metaImport + err error +} + +// metaImport represents the parsed tags from HTML files. +type metaImport struct { + Prefix, VCS, RepoRoot string +} + +// pathPrefix reports whether sub is a prefix of s, +// only considering entire path components. +func pathPrefix(s, sub string) bool { + // strings.HasPrefix is necessary but not sufficient. + if !strings.HasPrefix(s, sub) { + return false + } + // The remainder after the prefix must either be empty or start with a slash. + rem := s[len(sub):] + return rem == "" || rem[0] == '/' +} + +// A ImportMismatchError is returned where metaImport/s are present +// but none match our import path. +type ImportMismatchError struct { + importPath string + mismatches []string // the meta imports that were discarded for not matching our importPath +} + +func (m ImportMismatchError) Error() string { + formattedStrings := make([]string, len(m.mismatches)) + for i, pre := range m.mismatches { + formattedStrings[i] = fmt.Sprintf("meta tag %s did not match import path %s", pre, m.importPath) + } + return strings.Join(formattedStrings, ", ") +} + +// matchGoImport returns the metaImport from imports matching importPath. +// An error is returned if there are multiple matches. +// An ImportMismatchError is returned if none match. +func matchGoImport(imports []metaImport, importPath string) (metaImport, error) { + match := -1 + + errImportMismatch := ImportMismatchError{importPath: importPath} + for i, im := range imports { + if !pathPrefix(importPath, im.Prefix) { + errImportMismatch.mismatches = append(errImportMismatch.mismatches, im.Prefix) + continue + } + + if match >= 0 { + if imports[match].VCS == "mod" && im.VCS != "mod" { + // All the mod entries precede all the non-mod entries. + // We have a mod entry and don't care about the rest, + // matching or not. + break + } + return metaImport{}, fmt.Errorf("multiple meta tags match import path %q", importPath) + } + match = i + } + + if match == -1 { + return metaImport{}, errImportMismatch + } + return imports[match], nil +} + +// expand rewrites s to replace {k} with match[k] for each key k in match. +func expand(match map[string]string, s string) string { + // We want to replace each match exactly once, and the result of expansion + // must not depend on the iteration order through the map. + // A strings.Replacer has exactly the properties we're looking for. + oldNew := make([]string, 0, 2*len(match)) + for k, v := range match { + oldNew = append(oldNew, "{"+k+"}", v) + } + return strings.NewReplacer(oldNew...).Replace(s) +} + +// vcsPaths defines the meaning of import paths referring to +// commonly-used VCS hosting sites (github.com/user/dir) +// and import paths referring to a fully-qualified importPath +// containing a VCS type (foo.com/repo.git/dir) +var vcsPaths = []*vcsPath{ + // Github + { + prefix: "github.com/", + regexp: lazyregexp.New(`^(?Pgithub\.com/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`), + vcs: "git", + repo: "https://{root}", + check: noVCSSuffix, + }, + + // Bitbucket + { + prefix: "bitbucket.org/", + regexp: lazyregexp.New(`^(?Pbitbucket\.org/(?P[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`), + repo: "https://{root}", + check: bitbucketVCS, + }, + + // IBM DevOps Services (JazzHub) + { + prefix: "hub.jazz.net/git/", + regexp: lazyregexp.New(`^(?Phub\.jazz\.net/git/[a-z0-9]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`), + vcs: "git", + repo: "https://{root}", + check: noVCSSuffix, + }, + + // Git at Apache + { + prefix: "git.apache.org/", + regexp: lazyregexp.New(`^(?Pgit\.apache\.org/[a-z0-9_.\-]+\.git)(/[A-Za-z0-9_.\-]+)*$`), + vcs: "git", + repo: "https://{root}", + }, + + // Git at OpenStack + { + prefix: "git.openstack.org/", + regexp: lazyregexp.New(`^(?Pgit\.openstack\.org/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(\.git)?(/[A-Za-z0-9_.\-]+)*$`), + vcs: "git", + repo: "https://{root}", + }, + + // chiselapp.com for fossil + { + prefix: "chiselapp.com/", + regexp: lazyregexp.New(`^(?Pchiselapp\.com/user/[A-Za-z0-9]+/repository/[A-Za-z0-9_.\-]+)$`), + vcs: "fossil", + repo: "https://{root}", + }, + + // General syntax for any server. + // Must be last. + { + regexp: lazyregexp.New(`(?P(?P([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?(/~?[A-Za-z0-9_.\-]+)+?)\.(?Pbzr|fossil|git|hg|svn))(/~?[A-Za-z0-9_.\-]+)*$`), + schemelessRepo: true, + }, +} + +// vcsPathsAfterDynamic gives additional vcsPaths entries +// to try after the dynamic HTML check. +// This gives those sites a chance to introduce tags +// as part of a graceful transition away from the hard-coded logic. +var vcsPathsAfterDynamic = []*vcsPath{ + // Launchpad. See golang.org/issue/11436. + { + prefix: "launchpad.net/", + regexp: lazyregexp.New(`^(?Plaunchpad\.net/((?P[A-Za-z0-9_.\-]+)(?P/[A-Za-z0-9_.\-]+)?|~[A-Za-z0-9_.\-]+/(\+junk|[A-Za-z0-9_.\-]+)/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`), + vcs: "bzr", + repo: "https://{root}", + check: launchpadVCS, + }, +} + +// noVCSSuffix checks that the repository name does not +// end in .foo for any version control system foo. +// The usual culprit is ".git". +func noVCSSuffix(match map[string]string) error { + repo := match["repo"] + for _, vcs := range vcsList { + if strings.HasSuffix(repo, "."+vcs.Cmd) { + return fmt.Errorf("invalid version control suffix in %s path", match["prefix"]) + } + } + return nil +} + +// bitbucketVCS determines the version control system for a +// Bitbucket repository, by using the Bitbucket API. +func bitbucketVCS(match map[string]string) error { + if err := noVCSSuffix(match); err != nil { + return err + } + + var resp struct { + SCM string `json:"scm"` + } + url := &urlpkg.URL{ + Scheme: "https", + Host: "api.bitbucket.org", + Path: expand(match, "/2.0/repositories/{bitname}"), + RawQuery: "fields=scm", + } + data, err := web.GetBytes(url) + if err != nil { + if httpErr, ok := err.(*web.HTTPError); ok && httpErr.StatusCode == 403 { + // this may be a private repository. If so, attempt to determine which + // VCS it uses. See issue 5375. + root := match["root"] + for _, vcs := range []string{"git", "hg"} { + if vcsByCmd(vcs).Ping("https", root) == nil { + resp.SCM = vcs + break + } + } + } + + if resp.SCM == "" { + return err + } + } else { + if err := json.Unmarshal(data, &resp); err != nil { + return fmt.Errorf("decoding %s: %v", url, err) + } + } + + if vcsByCmd(resp.SCM) != nil { + match["vcs"] = resp.SCM + if resp.SCM == "git" { + match["repo"] += ".git" + } + return nil + } + + return fmt.Errorf("unable to detect version control system for bitbucket.org/ path") +} + +// launchpadVCS solves the ambiguity for "lp.net/project/foo". In this case, +// "foo" could be a series name registered in Launchpad with its own branch, +// and it could also be the name of a directory within the main project +// branch one level up. +func launchpadVCS(match map[string]string) error { + if match["project"] == "" || match["series"] == "" { + return nil + } + url := &urlpkg.URL{ + Scheme: "https", + Host: "code.launchpad.net", + Path: expand(match, "/{project}{series}/.bzr/branch-format"), + } + _, err := web.GetBytes(url) + if err != nil { + match["root"] = expand(match, "launchpad.net/{project}") + match["repo"] = expand(match, "https://{root}") + } + return nil +} diff --git a/src/cmd/go/internal/vcs/vcs_test.go b/src/cmd/go/internal/vcs/vcs_test.go new file mode 100644 index 0000000000..5b874204f1 --- /dev/null +++ b/src/cmd/go/internal/vcs/vcs_test.go @@ -0,0 +1,475 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package vcs + +import ( + "errors" + "internal/testenv" + "io/ioutil" + "os" + "path" + "path/filepath" + "testing" + + "cmd/go/internal/web" +) + +// Test that RepoRootForImportPath determines the correct RepoRoot for a given importPath. +// TODO(cmang): Add tests for SVN and BZR. +func TestRepoRootForImportPath(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + + tests := []struct { + path string + want *RepoRoot + }{ + { + "github.com/golang/groupcache", + &RepoRoot{ + VCS: vcsGit, + Repo: "https://github.com/golang/groupcache", + }, + }, + // Unicode letters in directories are not valid. + { + "github.com/user/unicode/испытание", + nil, + }, + // IBM DevOps Services tests + { + "hub.jazz.net/git/user1/pkgname", + &RepoRoot{ + VCS: vcsGit, + Repo: "https://hub.jazz.net/git/user1/pkgname", + }, + }, + { + "hub.jazz.net/git/user1/pkgname/submodule/submodule/submodule", + &RepoRoot{ + VCS: vcsGit, + Repo: "https://hub.jazz.net/git/user1/pkgname", + }, + }, + { + "hub.jazz.net", + nil, + }, + { + "hubajazz.net", + nil, + }, + { + "hub2.jazz.net", + nil, + }, + { + "hub.jazz.net/someotherprefix", + nil, + }, + { + "hub.jazz.net/someotherprefix/user1/pkgname", + nil, + }, + // Spaces are not valid in user names or package names + { + "hub.jazz.net/git/User 1/pkgname", + nil, + }, + { + "hub.jazz.net/git/user1/pkg name", + nil, + }, + // Dots are not valid in user names + { + "hub.jazz.net/git/user.1/pkgname", + nil, + }, + { + "hub.jazz.net/git/user/pkg.name", + &RepoRoot{ + VCS: vcsGit, + Repo: "https://hub.jazz.net/git/user/pkg.name", + }, + }, + // User names cannot have uppercase letters + { + "hub.jazz.net/git/USER/pkgname", + nil, + }, + // OpenStack tests + { + "git.openstack.org/openstack/swift", + &RepoRoot{ + VCS: vcsGit, + Repo: "https://git.openstack.org/openstack/swift", + }, + }, + // Trailing .git is less preferred but included for + // compatibility purposes while the same source needs to + // be compilable on both old and new go + { + "git.openstack.org/openstack/swift.git", + &RepoRoot{ + VCS: vcsGit, + Repo: "https://git.openstack.org/openstack/swift.git", + }, + }, + { + "git.openstack.org/openstack/swift/go/hummingbird", + &RepoRoot{ + VCS: vcsGit, + Repo: "https://git.openstack.org/openstack/swift", + }, + }, + { + "git.openstack.org", + nil, + }, + { + "git.openstack.org/openstack", + nil, + }, + // Spaces are not valid in package name + { + "git.apache.org/package name/path/to/lib", + nil, + }, + // Should have ".git" suffix + { + "git.apache.org/package-name/path/to/lib", + nil, + }, + { + "gitbapache.org", + nil, + }, + { + "git.apache.org/package-name.git", + &RepoRoot{ + VCS: vcsGit, + Repo: "https://git.apache.org/package-name.git", + }, + }, + { + "git.apache.org/package-name_2.x.git/path/to/lib", + &RepoRoot{ + VCS: vcsGit, + Repo: "https://git.apache.org/package-name_2.x.git", + }, + }, + { + "chiselapp.com/user/kyle/repository/fossilgg", + &RepoRoot{ + VCS: vcsFossil, + Repo: "https://chiselapp.com/user/kyle/repository/fossilgg", + }, + }, + { + // must have a user/$name/repository/$repo path + "chiselapp.com/kyle/repository/fossilgg", + nil, + }, + { + "chiselapp.com/user/kyle/fossilgg", + nil, + }, + } + + for _, test := range tests { + got, err := RepoRootForImportPath(test.path, IgnoreMod, web.SecureOnly) + want := test.want + + if want == nil { + if err == nil { + t.Errorf("RepoRootForImportPath(%q): Error expected but not received", test.path) + } + continue + } + if err != nil { + t.Errorf("RepoRootForImportPath(%q): %v", test.path, err) + continue + } + if got.VCS.Name != want.VCS.Name || got.Repo != want.Repo { + t.Errorf("RepoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.VCS, got.Repo, want.VCS, want.Repo) + } + } +} + +// Test that vcsFromDir correctly inspects a given directory and returns the right VCS and root. +func TestFromDir(t *testing.T) { + tempDir, err := ioutil.TempDir("", "vcstest") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tempDir) + + for j, vcs := range vcsList { + dir := filepath.Join(tempDir, "example.com", vcs.Name, "."+vcs.Cmd) + if j&1 == 0 { + err := os.MkdirAll(dir, 0755) + if err != nil { + t.Fatal(err) + } + } else { + err := os.MkdirAll(filepath.Dir(dir), 0755) + if err != nil { + t.Fatal(err) + } + f, err := os.Create(dir) + if err != nil { + t.Fatal(err) + } + f.Close() + } + + want := RepoRoot{ + VCS: vcs, + Root: path.Join("example.com", vcs.Name), + } + var got RepoRoot + got.VCS, got.Root, err = FromDir(dir, tempDir) + if err != nil { + t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err) + continue + } + if got.VCS.Name != want.VCS.Name || got.Root != want.Root { + t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.VCS, got.Root, want.VCS, want.Root) + } + } +} + +func TestIsSecure(t *testing.T) { + tests := []struct { + vcs *Cmd + url string + secure bool + }{ + {vcsGit, "http://example.com/foo.git", false}, + {vcsGit, "https://example.com/foo.git", true}, + {vcsBzr, "http://example.com/foo.bzr", false}, + {vcsBzr, "https://example.com/foo.bzr", true}, + {vcsSvn, "http://example.com/svn", false}, + {vcsSvn, "https://example.com/svn", true}, + {vcsHg, "http://example.com/foo.hg", false}, + {vcsHg, "https://example.com/foo.hg", true}, + {vcsGit, "ssh://user@example.com/foo.git", true}, + {vcsGit, "user@server:path/to/repo.git", false}, + {vcsGit, "user@server:", false}, + {vcsGit, "server:repo.git", false}, + {vcsGit, "server:path/to/repo.git", false}, + {vcsGit, "example.com:path/to/repo.git", false}, + {vcsGit, "path/that/contains/a:colon/repo.git", false}, + {vcsHg, "ssh://user@example.com/path/to/repo.hg", true}, + {vcsFossil, "http://example.com/foo", false}, + {vcsFossil, "https://example.com/foo", true}, + } + + for _, test := range tests { + secure := test.vcs.IsSecure(test.url) + if secure != test.secure { + t.Errorf("%s isSecure(%q) = %t; want %t", test.vcs, test.url, secure, test.secure) + } + } +} + +func TestIsSecureGitAllowProtocol(t *testing.T) { + tests := []struct { + vcs *Cmd + url string + secure bool + }{ + // Same as TestIsSecure to verify same behavior. + {vcsGit, "http://example.com/foo.git", false}, + {vcsGit, "https://example.com/foo.git", true}, + {vcsBzr, "http://example.com/foo.bzr", false}, + {vcsBzr, "https://example.com/foo.bzr", true}, + {vcsSvn, "http://example.com/svn", false}, + {vcsSvn, "https://example.com/svn", true}, + {vcsHg, "http://example.com/foo.hg", false}, + {vcsHg, "https://example.com/foo.hg", true}, + {vcsGit, "user@server:path/to/repo.git", false}, + {vcsGit, "user@server:", false}, + {vcsGit, "server:repo.git", false}, + {vcsGit, "server:path/to/repo.git", false}, + {vcsGit, "example.com:path/to/repo.git", false}, + {vcsGit, "path/that/contains/a:colon/repo.git", false}, + {vcsHg, "ssh://user@example.com/path/to/repo.hg", true}, + // New behavior. + {vcsGit, "ssh://user@example.com/foo.git", false}, + {vcsGit, "foo://example.com/bar.git", true}, + {vcsHg, "foo://example.com/bar.hg", false}, + {vcsSvn, "foo://example.com/svn", false}, + {vcsBzr, "foo://example.com/bar.bzr", false}, + } + + defer os.Unsetenv("GIT_ALLOW_PROTOCOL") + os.Setenv("GIT_ALLOW_PROTOCOL", "https:foo") + for _, test := range tests { + secure := test.vcs.IsSecure(test.url) + if secure != test.secure { + t.Errorf("%s isSecure(%q) = %t; want %t", test.vcs, test.url, secure, test.secure) + } + } +} + +func TestMatchGoImport(t *testing.T) { + tests := []struct { + imports []metaImport + path string + mi metaImport + err error + }{ + { + imports: []metaImport{ + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + }, + path: "example.com/user/foo", + mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + }, + { + imports: []metaImport{ + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + }, + path: "example.com/user/foo/", + mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + }, + { + imports: []metaImport{ + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + {Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + }, + path: "example.com/user/foo", + mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + }, + { + imports: []metaImport{ + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + {Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + }, + path: "example.com/user/fooa", + mi: metaImport{Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + }, + { + imports: []metaImport{ + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + }, + path: "example.com/user/foo/bar", + err: errors.New("should not be allowed to create nested repo"), + }, + { + imports: []metaImport{ + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + }, + path: "example.com/user/foo/bar/baz", + err: errors.New("should not be allowed to create nested repo"), + }, + { + imports: []metaImport{ + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + }, + path: "example.com/user/foo/bar/baz/qux", + err: errors.New("should not be allowed to create nested repo"), + }, + { + imports: []metaImport{ + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + }, + path: "example.com/user/foo/bar/baz/", + err: errors.New("should not be allowed to create nested repo"), + }, + { + imports: []metaImport{ + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + }, + path: "example.com", + err: errors.New("pathologically short path"), + }, + { + imports: []metaImport{ + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, + }, + path: "different.example.com/user/foo", + err: errors.New("meta tags do not match import path"), + }, + { + imports: []metaImport{ + {Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"}, + {Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"}, + }, + path: "myitcv.io/blah2/foo", + mi: metaImport{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"}, + }, + { + imports: []metaImport{ + {Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"}, + {Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"}, + }, + path: "myitcv.io/other", + mi: metaImport{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"}, + }, + } + + for _, test := range tests { + mi, err := matchGoImport(test.imports, test.path) + if mi != test.mi { + t.Errorf("unexpected metaImport; got %v, want %v", mi, test.mi) + } + + got := err + want := test.err + if (got == nil) != (want == nil) { + t.Errorf("unexpected error; got %v, want %v", got, want) + } + } +} + +func TestValidateRepoRoot(t *testing.T) { + tests := []struct { + root string + ok bool + }{ + { + root: "", + ok: false, + }, + { + root: "http://", + ok: true, + }, + { + root: "git+ssh://", + ok: true, + }, + { + root: "http#://", + ok: false, + }, + { + root: "-config", + ok: false, + }, + { + root: "-config://", + ok: false, + }, + } + + for _, test := range tests { + err := validateRepoRoot(test.root) + ok := err == nil + if ok != test.ok { + want := "error" + if test.ok { + want = "nil" + } + t.Errorf("validateRepoRoot(%q) = %q, want %s", test.root, err, want) + } + } +} -- cgit v1.2.3-54-g00ecf From d7384f36121d52191097af50d6dc12c0eb08fd75 Mon Sep 17 00:00:00 2001 From: Constantin Konstantinidis Date: Sun, 16 Aug 2020 13:48:09 +0200 Subject: os: implement File.Chmod on Windows Fixes: #39606 Change-Id: I4def67ef18bd3ff866b140f6e76cdabe5d51a1c5 Reviewed-on: https://go-review.googlesource.com/c/go/+/250077 Run-TryBot: Alex Brainman TryBot-Result: Gobot Gobot Reviewed-by: Alex Brainman --- src/internal/poll/fd_posix.go | 11 ---------- src/internal/poll/fd_unix.go | 11 ++++++++++ src/internal/poll/fd_windows.go | 27 ++++++++++++++++++++++++ src/internal/syscall/windows/syscall_windows.go | 9 ++++++++ src/internal/syscall/windows/zsyscall_windows.go | 13 ++++++++++++ src/os/os_test.go | 27 ++++++++++++++---------- 6 files changed, 76 insertions(+), 22 deletions(-) diff --git a/src/internal/poll/fd_posix.go b/src/internal/poll/fd_posix.go index e5fb05c9c2..4edfa953a4 100644 --- a/src/internal/poll/fd_posix.go +++ b/src/internal/poll/fd_posix.go @@ -29,17 +29,6 @@ func (fd *FD) Shutdown(how int) error { return syscall.Shutdown(fd.Sysfd, how) } -// Fchmod wraps syscall.Fchmod. -func (fd *FD) Fchmod(mode uint32) error { - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return ignoringEINTR(func() error { - return syscall.Fchmod(fd.Sysfd, mode) - }) -} - // Fchown wraps syscall.Fchown. func (fd *FD) Fchown(uid, gid int) error { if err := fd.incref(); err != nil { diff --git a/src/internal/poll/fd_unix.go b/src/internal/poll/fd_unix.go index 1d5101eac3..f6f6c52f31 100644 --- a/src/internal/poll/fd_unix.go +++ b/src/internal/poll/fd_unix.go @@ -437,6 +437,17 @@ func (fd *FD) ReadDirent(buf []byte) (int, error) { } } +// Fchmod wraps syscall.Fchmod. +func (fd *FD) Fchmod(mode uint32) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return ignoringEINTR(func() error { + return syscall.Fchmod(fd.Sysfd, mode) + }) +} + // Fchdir wraps syscall.Fchdir. func (fd *FD) Fchdir() error { if err := fd.incref(); err != nil { diff --git a/src/internal/poll/fd_windows.go b/src/internal/poll/fd_windows.go index e1ef6199b3..d8c834f929 100644 --- a/src/internal/poll/fd_windows.go +++ b/src/internal/poll/fd_windows.go @@ -886,6 +886,33 @@ func (fd *FD) FindNextFile(data *syscall.Win32finddata) error { return syscall.FindNextFile(fd.Sysfd, data) } +// Fchmod updates syscall.ByHandleFileInformation.Fileattributes when needed. +func (fd *FD) Fchmod(mode uint32) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + + var d syscall.ByHandleFileInformation + if err := syscall.GetFileInformationByHandle(fd.Sysfd, &d); err != nil { + return err + } + attrs := d.FileAttributes + if mode&syscall.S_IWRITE != 0 { + attrs &^= syscall.FILE_ATTRIBUTE_READONLY + } else { + attrs |= syscall.FILE_ATTRIBUTE_READONLY + } + if attrs == d.FileAttributes { + return nil + } + + var du windows.FILE_BASIC_INFO + du.FileAttributes = attrs + l := uint32(unsafe.Sizeof(d)) + return windows.SetFileInformationByHandle(fd.Sysfd, windows.FileBasicInfo, uintptr(unsafe.Pointer(&du)), l) +} + // Fchdir wraps syscall.Fchdir. func (fd *FD) Fchdir() error { if err := fd.incref(); err != nil { diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go index edf0b5a40b..1f40c11820 100644 --- a/src/internal/syscall/windows/syscall_windows.go +++ b/src/internal/syscall/windows/syscall_windows.go @@ -131,6 +131,14 @@ type IpAdapterAddresses struct { /* more fields might be present here. */ } +type FILE_BASIC_INFO struct { + CreationTime syscall.Filetime + LastAccessTime syscall.Filetime + LastWriteTime syscall.Filetime + ChangedTime syscall.Filetime + FileAttributes uint32 +} + const ( IfOperStatusUp = 1 IfOperStatusDown = 2 @@ -145,6 +153,7 @@ const ( //sys GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW //sys MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) = MoveFileExW //sys GetModuleFileName(module syscall.Handle, fn *uint16, len uint32) (n uint32, err error) = kernel32.GetModuleFileNameW +//sys SetFileInformationByHandle(handle syscall.Handle, fileInformationClass uint32, buf uintptr, bufsize uint32) (err error) = kernel32.SetFileInformationByHandle const ( WSA_FLAG_OVERLAPPED = 0x01 diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go index ca5b4e6f16..0840dc283a 100644 --- a/src/internal/syscall/windows/zsyscall_windows.go +++ b/src/internal/syscall/windows/zsyscall_windows.go @@ -71,6 +71,7 @@ var ( procNetUserGetLocalGroups = modnetapi32.NewProc("NetUserGetLocalGroups") procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx") + procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle") ) func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizePointer *uint32) (errcode error) { @@ -81,6 +82,18 @@ func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapter return } +func SetFileInformationByHandle(handle syscall.Handle, fileInformationClass uint32, buf uintptr, bufsize uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(handle), uintptr(fileInformationClass), uintptr(buf), uintptr(bufsize), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + func GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) { r1, _, e1 := syscall.Syscall(procGetComputerNameExW.Addr(), 3, uintptr(nameformat), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n))) if r1 == 0 { diff --git a/src/os/os_test.go b/src/os/os_test.go index 520916d880..3359301316 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -1099,29 +1099,34 @@ func checkMode(t *testing.T, path string, mode FileMode) { if err != nil { t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err) } - if dir.Mode()&0777 != mode { + if dir.Mode()&ModePerm != mode { t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode(), mode) } } func TestChmod(t *testing.T) { - // Chmod is not supported under windows. - if runtime.GOOS == "windows" { - return - } f := newFile("TestChmod", t) defer Remove(f.Name()) defer f.Close() + // Creation mode is read write - if err := Chmod(f.Name(), 0456); err != nil { - t.Fatalf("chmod %s 0456: %s", f.Name(), err) + fm := FileMode(0456) + if runtime.GOOS == "windows" { + fm = FileMode(0444) // read-only file } - checkMode(t, f.Name(), 0456) + if err := Chmod(f.Name(), fm); err != nil { + t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err) + } + checkMode(t, f.Name(), fm) - if err := f.Chmod(0123); err != nil { - t.Fatalf("chmod %s 0123: %s", f.Name(), err) + fm = FileMode(0123) + if runtime.GOOS == "windows" { + fm = FileMode(0666) // read-write file + } + if err := f.Chmod(fm); err != nil { + t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err) } - checkMode(t, f.Name(), 0123) + checkMode(t, f.Name(), fm) } func checkSize(t *testing.T, f *File, size int64) { -- cgit v1.2.3-54-g00ecf From 2c95e3a6a8377ca9c72608c25b4cf2506baf782f Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Wed, 9 Sep 2020 11:09:01 +0700 Subject: cmd/compile: use clearer error message for stuct literal This CL changes "T literal.M" error message to "T{...}.M". It's clearer expression and focusing user on actual issue. Updates #38745 Change-Id: I84b455a86742f37e0bde5bf390aa02984eecc3c9 Reviewed-on: https://go-review.googlesource.com/c/go/+/253677 Run-TryBot: Cuong Manh Le TryBot-Result: Gobot Gobot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/fmt.go | 11 ++- test/alias2.go | 10 +-- test/ddd1.go | 2 +- test/escape2.go | 56 ++++++------- test/escape2n.go | 56 ++++++------- test/escape_calls.go | 2 +- test/escape_field.go | 6 +- test/escape_iface.go | 50 ++++++------ test/escape_indir.go | 34 ++++---- test/escape_map.go | 26 +++--- test/escape_param.go | 60 +++++++------- test/escape_slice.go | 14 ++-- test/escape_struct_param1.go | 158 ++++++++++++++++++------------------- test/escape_struct_param2.go | 158 ++++++++++++++++++------------------- test/fixedbugs/issue12006.go | 4 +- test/fixedbugs/issue13799.go | 4 +- test/fixedbugs/issue17645.go | 2 +- test/fixedbugs/issue21709.go | 4 +- test/fixedbugs/issue23732.go | 6 +- test/fixedbugs/issue26855.go | 4 +- test/fixedbugs/issue30898.go | 2 +- test/fixedbugs/issue31573.go | 24 +++--- test/fixedbugs/issue38745.go | 19 +++++ test/fixedbugs/issue39292.go | 6 +- test/fixedbugs/issue41247.go | 2 +- test/fixedbugs/issue7921.go | 10 +-- test/inline_variadic.go | 2 +- 27 files changed, 379 insertions(+), 353 deletions(-) create mode 100644 test/fixedbugs/issue38745.go diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 866cd0a714..43e501deaf 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -1407,7 +1407,7 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) { return } if n.Right != nil { - mode.Fprintf(s, "%v literal", n.Right) + mode.Fprintf(s, "%v{%s}", n.Right, ellipsisIf(n.List.Len() != 0)) return } @@ -1421,7 +1421,7 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) { case OSTRUCTLIT, OARRAYLIT, OSLICELIT, OMAPLIT: if mode == FErr { - mode.Fprintf(s, "%v literal", n.Type) + mode.Fprintf(s, "%v{%s}", n.Type, ellipsisIf(n.List.Len() != 0)) return } mode.Fprintf(s, "(%v{ %.v })", n.Type, n.List) @@ -1934,3 +1934,10 @@ func indent(s fmt.State) { fmt.Fprint(s, ". ") } } + +func ellipsisIf(b bool) string { + if b { + return "..." + } + return "" +} diff --git a/test/alias2.go b/test/alias2.go index 7ea1b2908d..1c141ac490 100644 --- a/test/alias2.go +++ b/test/alias2.go @@ -46,8 +46,8 @@ var _ A0 = T0{} var _ T0 = A0{} // But aliases and original types cannot be used with new types based on them. -var _ N0 = T0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type" -var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type" +var _ N0 = T0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|incompatible type" +var _ N0 = A0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|incompatible type" var _ A5 = Value{} @@ -82,10 +82,10 @@ func _() { var _ A0 = T0{} var _ T0 = A0{} - var _ N0 = T0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type" - var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type" + var _ N0 = T0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|incompatible type" + var _ N0 = A0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|incompatible type" - var _ A5 = Value{} // ERROR "cannot use reflect\.Value literal \(type reflect.Value\) as type A5 in assignment|incompatible type" + var _ A5 = Value{} // ERROR "cannot use reflect\.Value{} \(type reflect.Value\) as type A5 in assignment|incompatible type" } // Invalid type alias declarations. diff --git a/test/ddd1.go b/test/ddd1.go index b582f221b7..2c7e83e374 100644 --- a/test/ddd1.go +++ b/test/ddd1.go @@ -19,7 +19,7 @@ var ( _ = sum(1.0, 2.0) _ = sum(1.5) // ERROR "integer" _ = sum("hello") // ERROR ".hello. .type untyped string. as type int|incompatible" - _ = sum([]int{1}) // ERROR "\[\]int literal.*as type int|incompatible" + _ = sum([]int{1}) // ERROR "\[\]int{...}.*as type int|incompatible" ) func sum3(int, int, int) int { return 0 } diff --git a/test/escape2.go b/test/escape2.go index cf24f4bebc..5c6eb559fa 100644 --- a/test/escape2.go +++ b/test/escape2.go @@ -118,15 +118,15 @@ type Bar struct { } func NewBar() *Bar { - return &Bar{42, nil} // ERROR "&Bar literal escapes to heap$" + return &Bar{42, nil} // ERROR "&Bar{...} escapes to heap$" } func NewBarp(x *int) *Bar { // ERROR "leaking param: x$" - return &Bar{42, x} // ERROR "&Bar literal escapes to heap$" + return &Bar{42, x} // ERROR "&Bar{...} escapes to heap$" } func NewBarp2(x *int) *Bar { // ERROR "x does not escape$" - return &Bar{*x, nil} // ERROR "&Bar literal escapes to heap$" + return &Bar{*x, nil} // ERROR "&Bar{...} escapes to heap$" } func (b *Bar) NoLeak() int { // ERROR "b does not escape$" @@ -173,7 +173,7 @@ type Bar2 struct { } func NewBar2() *Bar2 { - return &Bar2{[12]int{42}, nil} // ERROR "&Bar2 literal escapes to heap$" + return &Bar2{[12]int{42}, nil} // ERROR "&Bar2{...} escapes to heap$" } func (b *Bar2) NoLeak() int { // ERROR "b does not escape$" @@ -539,7 +539,7 @@ func foo72b() [10]*int { // issue 2145 func foo73() { - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for _, v := range s { vv := v // actually just escapes its scope @@ -550,7 +550,7 @@ func foo73() { } func foo731() { - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for _, v := range s { vv := v // ERROR "moved to heap: vv$" // actually just escapes its scope @@ -562,7 +562,7 @@ func foo731() { } func foo74() { - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for _, v := range s { vv := v // actually just escapes its scope @@ -574,7 +574,7 @@ func foo74() { } func foo74a() { - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for _, v := range s { vv := v // ERROR "moved to heap: vv$" // actually just escapes its scope @@ -589,7 +589,7 @@ func foo74a() { // issue 3975 func foo74b() { var array [3]func() - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for i, v := range s { vv := v // actually just escapes its scope @@ -601,7 +601,7 @@ func foo74b() { func foo74c() { var array [3]func() - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for i, v := range s { vv := v // ERROR "moved to heap: vv$" // actually just escapes its scope @@ -759,15 +759,15 @@ type LimitedFooer struct { } func LimitFooer(r Fooer, n int64) Fooer { // ERROR "leaking param: r$" - return &LimitedFooer{r, n} // ERROR "&LimitedFooer literal escapes to heap$" + return &LimitedFooer{r, n} // ERROR "&LimitedFooer{...} escapes to heap$" } func foo90(x *int) map[*int]*int { // ERROR "leaking param: x$" - return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal escapes to heap$" + return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } func foo91(x *int) map[*int]*int { // ERROR "leaking param: x$" - return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal escapes to heap$" + return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } func foo92(x *int) [2]*int { // ERROR "leaking param: x to result ~r1 level=0$" @@ -870,15 +870,15 @@ func foo106(x *int) { // ERROR "leaking param: x$" } func foo107(x *int) map[*int]*int { // ERROR "leaking param: x$" - return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal escapes to heap$" + return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } func foo108(x *int) map[*int]*int { // ERROR "leaking param: x$" - return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal escapes to heap$" + return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } func foo109(x *int) *int { // ERROR "leaking param: x$" - m := map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal does not escape$" + m := map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} does not escape$" for k, _ := range m { return k } @@ -886,12 +886,12 @@ func foo109(x *int) *int { // ERROR "leaking param: x$" } func foo110(x *int) *int { // ERROR "leaking param: x$" - m := map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal does not escape$" + m := map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} does not escape$" return m[nil] } func foo111(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0" - m := []*int{x} // ERROR "\[\]\*int literal does not escape$" + m := []*int{x} // ERROR "\[\]\*int{...} does not escape$" return m[0] } @@ -906,7 +906,7 @@ func foo113(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" } func foo114(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" - m := &Bar{ii: x} // ERROR "&Bar literal does not escape$" + m := &Bar{ii: x} // ERROR "&Bar{...} does not escape$" return m.ii } @@ -1343,8 +1343,8 @@ func foo140() interface{} { X string T *T } - t := &T{} // ERROR "&T literal escapes to heap$" - return U{ // ERROR "U literal escapes to heap$" + t := &T{} // ERROR "&T{} escapes to heap$" + return U{ // ERROR "U{...} escapes to heap$" X: t.X, T: t, } @@ -1530,7 +1530,7 @@ type V struct { } func NewV(u U) *V { // ERROR "leaking param: u$" - return &V{u.String()} // ERROR "&V literal escapes to heap$" + return &V{u.String()} // ERROR "&V{...} escapes to heap$" } func foo152() { @@ -1571,21 +1571,21 @@ type Lit struct { func ptrlitNoescape() { // Both literal and element do not escape. i := 0 - x := &Lit{&i} // ERROR "&Lit literal does not escape$" + x := &Lit{&i} // ERROR "&Lit{...} does not escape$" _ = x } func ptrlitNoEscape2() { // Literal does not escape, but element does. i := 0 // ERROR "moved to heap: i$" - x := &Lit{&i} // ERROR "&Lit literal does not escape$" + x := &Lit{&i} // ERROR "&Lit{...} does not escape$" sink = *x } func ptrlitEscape() { // Both literal and element escape. i := 0 // ERROR "moved to heap: i$" - x := &Lit{&i} // ERROR "&Lit literal escapes to heap$" + x := &Lit{&i} // ERROR "&Lit{...} escapes to heap$" sink = x } @@ -1760,18 +1760,18 @@ func stringtoslicerune2() { } func slicerunetostring0() { - r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$" + r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$" s := string(r) // ERROR "string\(r\) does not escape$" _ = s } func slicerunetostring1() string { - r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$" + r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$" return string(r) // ERROR "string\(r\) escapes to heap$" } func slicerunetostring2() { - r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$" + r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$" sink = string(r) // ERROR "string\(r\) escapes to heap$" } diff --git a/test/escape2n.go b/test/escape2n.go index f771e0aef2..46e58f8566 100644 --- a/test/escape2n.go +++ b/test/escape2n.go @@ -118,15 +118,15 @@ type Bar struct { } func NewBar() *Bar { - return &Bar{42, nil} // ERROR "&Bar literal escapes to heap$" + return &Bar{42, nil} // ERROR "&Bar{...} escapes to heap$" } func NewBarp(x *int) *Bar { // ERROR "leaking param: x$" - return &Bar{42, x} // ERROR "&Bar literal escapes to heap$" + return &Bar{42, x} // ERROR "&Bar{...} escapes to heap$" } func NewBarp2(x *int) *Bar { // ERROR "x does not escape$" - return &Bar{*x, nil} // ERROR "&Bar literal escapes to heap$" + return &Bar{*x, nil} // ERROR "&Bar{...} escapes to heap$" } func (b *Bar) NoLeak() int { // ERROR "b does not escape$" @@ -173,7 +173,7 @@ type Bar2 struct { } func NewBar2() *Bar2 { - return &Bar2{[12]int{42}, nil} // ERROR "&Bar2 literal escapes to heap$" + return &Bar2{[12]int{42}, nil} // ERROR "&Bar2{...} escapes to heap$" } func (b *Bar2) NoLeak() int { // ERROR "b does not escape$" @@ -539,7 +539,7 @@ func foo72b() [10]*int { // issue 2145 func foo73() { - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for _, v := range s { vv := v // actually just escapes its scope @@ -550,7 +550,7 @@ func foo73() { } func foo731() { - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for _, v := range s { vv := v // ERROR "moved to heap: vv$" // actually just escapes its scope @@ -562,7 +562,7 @@ func foo731() { } func foo74() { - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for _, v := range s { vv := v // actually just escapes its scope @@ -574,7 +574,7 @@ func foo74() { } func foo74a() { - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for _, v := range s { vv := v // ERROR "moved to heap: vv$" // actually just escapes its scope @@ -589,7 +589,7 @@ func foo74a() { // issue 3975 func foo74b() { var array [3]func() - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for i, v := range s { vv := v // actually just escapes its scope @@ -601,7 +601,7 @@ func foo74b() { func foo74c() { var array [3]func() - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for i, v := range s { vv := v // ERROR "moved to heap: vv$" // actually just escapes its scope @@ -759,15 +759,15 @@ type LimitedFooer struct { } func LimitFooer(r Fooer, n int64) Fooer { // ERROR "leaking param: r$" - return &LimitedFooer{r, n} // ERROR "&LimitedFooer literal escapes to heap$" + return &LimitedFooer{r, n} // ERROR "&LimitedFooer{...} escapes to heap$" } func foo90(x *int) map[*int]*int { // ERROR "leaking param: x$" - return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal escapes to heap$" + return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } func foo91(x *int) map[*int]*int { // ERROR "leaking param: x$" - return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal escapes to heap$" + return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } func foo92(x *int) [2]*int { // ERROR "leaking param: x to result ~r1 level=0$" @@ -870,15 +870,15 @@ func foo106(x *int) { // ERROR "leaking param: x$" } func foo107(x *int) map[*int]*int { // ERROR "leaking param: x$" - return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal escapes to heap$" + return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } func foo108(x *int) map[*int]*int { // ERROR "leaking param: x$" - return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal escapes to heap$" + return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } func foo109(x *int) *int { // ERROR "leaking param: x$" - m := map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal does not escape$" + m := map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} does not escape$" for k, _ := range m { return k } @@ -886,12 +886,12 @@ func foo109(x *int) *int { // ERROR "leaking param: x$" } func foo110(x *int) *int { // ERROR "leaking param: x$" - m := map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal does not escape$" + m := map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} does not escape$" return m[nil] } func foo111(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0" - m := []*int{x} // ERROR "\[\]\*int literal does not escape$" + m := []*int{x} // ERROR "\[\]\*int{...} does not escape$" return m[0] } @@ -906,7 +906,7 @@ func foo113(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" } func foo114(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" - m := &Bar{ii: x} // ERROR "&Bar literal does not escape$" + m := &Bar{ii: x} // ERROR "&Bar{...} does not escape$" return m.ii } @@ -1343,8 +1343,8 @@ func foo140() interface{} { X string T *T } - t := &T{} // ERROR "&T literal escapes to heap$" - return U{ // ERROR "U literal escapes to heap$" + t := &T{} // ERROR "&T{} escapes to heap$" + return U{ // ERROR "U{...} escapes to heap$" X: t.X, T: t, } @@ -1530,7 +1530,7 @@ type V struct { } func NewV(u U) *V { // ERROR "leaking param: u$" - return &V{u.String()} // ERROR "&V literal escapes to heap$" + return &V{u.String()} // ERROR "&V{...} escapes to heap$" } func foo152() { @@ -1571,21 +1571,21 @@ type Lit struct { func ptrlitNoescape() { // Both literal and element do not escape. i := 0 - x := &Lit{&i} // ERROR "&Lit literal does not escape$" + x := &Lit{&i} // ERROR "&Lit{...} does not escape$" _ = x } func ptrlitNoEscape2() { // Literal does not escape, but element does. i := 0 // ERROR "moved to heap: i$" - x := &Lit{&i} // ERROR "&Lit literal does not escape$" + x := &Lit{&i} // ERROR "&Lit{...} does not escape$" sink = *x } func ptrlitEscape() { // Both literal and element escape. i := 0 // ERROR "moved to heap: i$" - x := &Lit{&i} // ERROR "&Lit literal escapes to heap$" + x := &Lit{&i} // ERROR "&Lit{...} escapes to heap$" sink = x } @@ -1760,18 +1760,18 @@ func stringtoslicerune2() { } func slicerunetostring0() { - r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$" + r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$" s := string(r) // ERROR "string\(r\) does not escape$" _ = s } func slicerunetostring1() string { - r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$" + r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$" return string(r) // ERROR "string\(r\) escapes to heap$" } func slicerunetostring2() { - r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$" + r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$" sink = string(r) // ERROR "string\(r\) escapes to heap$" } diff --git a/test/escape_calls.go b/test/escape_calls.go index 2dbfee1558..9e1db5426e 100644 --- a/test/escape_calls.go +++ b/test/escape_calls.go @@ -50,5 +50,5 @@ func bar() { f := prototype f = func(ss []string) { got = append(got, ss) } // ERROR "leaking param: ss" "func literal does not escape" s := "string" - f([]string{s}) // ERROR "\[\]string literal escapes to heap" + f([]string{s}) // ERROR "\[\]string{...} escapes to heap" } diff --git a/test/escape_field.go b/test/escape_field.go index bf1dfb18ff..95d0784d91 100644 --- a/test/escape_field.go +++ b/test/escape_field.go @@ -127,20 +127,20 @@ func field12() { func field13() { i := 0 // ERROR "moved to heap: i$" - x := &X{p1: &i} // ERROR "&X literal does not escape$" + x := &X{p1: &i} // ERROR "&X{...} does not escape$" sink = x.p1 } func field14() { i := 0 // ERROR "moved to heap: i$" // BAD: &i should not escape - x := &X{p1: &i} // ERROR "&X literal does not escape$" + x := &X{p1: &i} // ERROR "&X{...} does not escape$" sink = x.p2 } func field15() { i := 0 // ERROR "moved to heap: i$" - x := &X{p1: &i} // ERROR "&X literal escapes to heap$" + x := &X{p1: &i} // ERROR "&X{...} escapes to heap$" sink = x } diff --git a/test/escape_iface.go b/test/escape_iface.go index 118ed3c56f..7b0914cadb 100644 --- a/test/escape_iface.go +++ b/test/escape_iface.go @@ -37,7 +37,7 @@ func efaceEscape0() { _ = x } { - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" v := M0{&i} var x M = v sink = x @@ -50,7 +50,7 @@ func efaceEscape0() { _ = v1 } { - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" v := M0{&i} // BAD: v does not escape to heap here var x M = v @@ -58,14 +58,14 @@ func efaceEscape0() { sink = v1 } { - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" v := M0{&i} // BAD: v does not escape to heap here var x M = v x.M() } { - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" v := M0{&i} var x M = v mescapes(x) @@ -91,46 +91,46 @@ func efaceEscape1() { { i := 0 v := M1{&i, 0} - var x M = v // ERROR "v does not escape" + var x M = v // ERROR "v does not escape" _ = x } { - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" v := M1{&i, 0} - var x M = v // ERROR "v escapes to heap" + var x M = v // ERROR "v escapes to heap" sink = x } { i := 0 v := M1{&i, 0} - var x M = v // ERROR "v does not escape" + var x M = v // ERROR "v does not escape" v1 := x.(M1) _ = v1 } { - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" v := M1{&i, 0} var x M = v // ERROR "v does not escape" v1 := x.(M1) sink = v1 // ERROR "v1 escapes to heap" } { - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" v := M1{&i, 0} // BAD: v does not escape to heap here var x M = v // ERROR "v escapes to heap" x.M() } { - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" v := M1{&i, 0} - var x M = v // ERROR "v escapes to heap" + var x M = v // ERROR "v escapes to heap" mescapes(x) } { i := 0 v := M1{&i, 0} - var x M = v // ERROR "v does not escape" + var x M = v // ERROR "v does not escape" mdoesnotescape(x) } } @@ -146,26 +146,26 @@ func (*M2) M() { func efaceEscape2() { { i := 0 - v := &M2{&i} // ERROR "&M2 literal does not escape" + v := &M2{&i} // ERROR "&M2{...} does not escape" var x M = v _ = x } { i := 0 // ERROR "moved to heap: i" - v := &M2{&i} // ERROR "&M2 literal escapes to heap" + v := &M2{&i} // ERROR "&M2{...} escapes to heap" var x M = v sink = x } { i := 0 - v := &M2{&i} // ERROR "&M2 literal does not escape" + v := &M2{&i} // ERROR "&M2{...} does not escape" var x M = v v1 := x.(*M2) _ = v1 } { i := 0 // ERROR "moved to heap: i" - v := &M2{&i} // ERROR "&M2 literal escapes to heap" + v := &M2{&i} // ERROR "&M2{...} escapes to heap" // BAD: v does not escape to heap here var x M = v v1 := x.(*M2) @@ -173,7 +173,7 @@ func efaceEscape2() { } { i := 0 // ERROR "moved to heap: i" - v := &M2{&i} // ERROR "&M2 literal does not escape" + v := &M2{&i} // ERROR "&M2{...} does not escape" // BAD: v does not escape to heap here var x M = v v1 := x.(*M2) @@ -181,7 +181,7 @@ func efaceEscape2() { } { i := 0 // ERROR "moved to heap: i" - v := &M2{&i} // ERROR "&M2 literal does not escape" + v := &M2{&i} // ERROR "&M2{...} does not escape" // BAD: v does not escape to heap here var x M = v v1, ok := x.(*M2) @@ -190,20 +190,20 @@ func efaceEscape2() { } { i := 0 // ERROR "moved to heap: i" - v := &M2{&i} // ERROR "&M2 literal escapes to heap" + v := &M2{&i} // ERROR "&M2{...} escapes to heap" // BAD: v does not escape to heap here var x M = v x.M() } { i := 0 // ERROR "moved to heap: i" - v := &M2{&i} // ERROR "&M2 literal escapes to heap" + v := &M2{&i} // ERROR "&M2{...} escapes to heap" var x M = v mescapes(x) } { i := 0 - v := &M2{&i} // ERROR "&M2 literal does not escape" + v := &M2{&i} // ERROR "&M2{...} does not escape" var x M = v mdoesnotescape(x) } @@ -219,8 +219,8 @@ type T2 struct { func dotTypeEscape() *T2 { // #11931 var x interface{} - x = &T1{p: new(int)} // ERROR "new\(int\) escapes to heap" "&T1 literal does not escape" - return &T2{ // ERROR "&T2 literal escapes to heap" + x = &T1{p: new(int)} // ERROR "new\(int\) escapes to heap" "&T1{...} does not escape" + return &T2{ // ERROR "&T2{...} escapes to heap" T1: *(x.(*T1)), } } @@ -244,7 +244,7 @@ func dotTypeEscape2() { // #13805, #15796 var x interface{} = i // ERROR "i does not escape" var y interface{} = j // ERROR "j does not escape" - sink = x.(int) // ERROR "x.\(int\) escapes to heap" + sink = x.(int) // ERROR "x.\(int\) escapes to heap" sink, *(&ok) = y.(int) } { diff --git a/test/escape_indir.go b/test/escape_indir.go index 19889f259f..12005e35f9 100644 --- a/test/escape_indir.go +++ b/test/escape_indir.go @@ -23,7 +23,7 @@ type ConstPtr2 struct { func constptr0() { i := 0 // ERROR "moved to heap: i" - x := &ConstPtr{} // ERROR "&ConstPtr literal does not escape" + x := &ConstPtr{} // ERROR "&ConstPtr{} does not escape" // BAD: i should not escape here x.p = &i _ = x @@ -31,55 +31,55 @@ func constptr0() { func constptr01() *ConstPtr { i := 0 // ERROR "moved to heap: i" - x := &ConstPtr{} // ERROR "&ConstPtr literal escapes to heap" + x := &ConstPtr{} // ERROR "&ConstPtr{} escapes to heap" x.p = &i return x } func constptr02() ConstPtr { i := 0 // ERROR "moved to heap: i" - x := &ConstPtr{} // ERROR "&ConstPtr literal does not escape" + x := &ConstPtr{} // ERROR "&ConstPtr{} does not escape" x.p = &i return *x } func constptr03() **ConstPtr { i := 0 // ERROR "moved to heap: i" - x := &ConstPtr{} // ERROR "&ConstPtr literal escapes to heap" "moved to heap: x" + x := &ConstPtr{} // ERROR "&ConstPtr{} escapes to heap" "moved to heap: x" x.p = &i return &x } func constptr1() { i := 0 // ERROR "moved to heap: i" - x := &ConstPtr{} // ERROR "&ConstPtr literal escapes to heap" + x := &ConstPtr{} // ERROR "&ConstPtr{} escapes to heap" x.p = &i sink = x } func constptr2() { i := 0 // ERROR "moved to heap: i" - x := &ConstPtr{} // ERROR "&ConstPtr literal does not escape" + x := &ConstPtr{} // ERROR "&ConstPtr{} does not escape" x.p = &i - sink = *x // ERROR "\*x escapes to heap" + sink = *x // ERROR "\*x escapes to heap" } func constptr4() *ConstPtr { p := new(ConstPtr) // ERROR "new\(ConstPtr\) escapes to heap" - *p = *&ConstPtr{} // ERROR "&ConstPtr literal does not escape" + *p = *&ConstPtr{} // ERROR "&ConstPtr{} does not escape" return p } func constptr5() *ConstPtr { p := new(ConstPtr) // ERROR "new\(ConstPtr\) escapes to heap" - p1 := &ConstPtr{} // ERROR "&ConstPtr literal does not escape" + p1 := &ConstPtr{} // ERROR "&ConstPtr{} does not escape" *p = *p1 return p } // BAD: p should not escape here func constptr6(p *ConstPtr) { // ERROR "leaking param content: p" - p1 := &ConstPtr{} // ERROR "&ConstPtr literal does not escape" + p1 := &ConstPtr{} // ERROR "&ConstPtr{} does not escape" *p1 = *p _ = p1 } @@ -102,17 +102,17 @@ func constptr8() *ConstPtr { func constptr9() ConstPtr { p := new(ConstPtr) // ERROR "new\(ConstPtr\) does not escape" var p1 ConstPtr2 - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" p1.p = &i p.c = p1 return *p } func constptr10() ConstPtr { - x := &ConstPtr{} // ERROR "moved to heap: x" "&ConstPtr literal escapes to heap" + x := &ConstPtr{} // ERROR "moved to heap: x" "&ConstPtr{} escapes to heap" i := 0 // ERROR "moved to heap: i" var p *ConstPtr - p = &ConstPtr{p: &i, x: &x} // ERROR "&ConstPtr literal does not escape" + p = &ConstPtr{p: &i, x: &x} // ERROR "&ConstPtr{...} does not escape" var pp **ConstPtr pp = &p return **pp @@ -121,7 +121,7 @@ func constptr10() ConstPtr { func constptr11() *ConstPtr { i := 0 // ERROR "moved to heap: i" p := new(ConstPtr) // ERROR "new\(ConstPtr\) escapes to heap" - p1 := &ConstPtr{} // ERROR "&ConstPtr literal does not escape" + p1 := &ConstPtr{} // ERROR "&ConstPtr{} does not escape" p1.p = &i *p = *p1 return p @@ -134,7 +134,7 @@ func foo(p **int) { // ERROR "p does not escape" } func foo1(p *int) { // ERROR "p does not escape" - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" y := &p *y = &i } @@ -148,13 +148,13 @@ func foo2() { var z Z z.f = &x p := z.f - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" *p = &i } var global *byte func f() { - var x byte // ERROR "moved to heap: x" + var x byte // ERROR "moved to heap: x" global = &*&x } diff --git a/test/escape_map.go b/test/escape_map.go index 0e9896a9fc..23abaa1e0c 100644 --- a/test/escape_map.go +++ b/test/escape_map.go @@ -15,7 +15,7 @@ func map0() { // BAD: i should not escape i := 0 // ERROR "moved to heap: i" // BAD: j should not escape - j := 0 // ERROR "moved to heap: j" + j := 0 // ERROR "moved to heap: j" m[&i] = &j _ = m } @@ -23,8 +23,8 @@ func map0() { func map1() *int { m := make(map[*int]*int) // ERROR "make\(map\[\*int\]\*int\) does not escape" // BAD: i should not escape - i := 0 // ERROR "moved to heap: i" - j := 0 // ERROR "moved to heap: j" + i := 0 // ERROR "moved to heap: i" + j := 0 // ERROR "moved to heap: j" m[&i] = &j return m[&i] } @@ -41,7 +41,7 @@ func map3() []*int { m := make(map[*int]*int) // ERROR "make\(map\[\*int\]\*int\) does not escape" i := 0 // ERROR "moved to heap: i" // BAD: j should not escape - j := 0 // ERROR "moved to heap: j" + j := 0 // ERROR "moved to heap: j" m[&i] = &j var r []*int for k := range m { @@ -53,8 +53,8 @@ func map3() []*int { func map4() []*int { m := make(map[*int]*int) // ERROR "make\(map\[\*int\]\*int\) does not escape" // BAD: i should not escape - i := 0 // ERROR "moved to heap: i" - j := 0 // ERROR "moved to heap: j" + i := 0 // ERROR "moved to heap: i" + j := 0 // ERROR "moved to heap: j" m[&i] = &j var r []*int for k, v := range m { @@ -68,8 +68,8 @@ func map4() []*int { } func map5(m map[*int]*int) { // ERROR "m does not escape" - i := 0 // ERROR "moved to heap: i" - j := 0 // ERROR "moved to heap: j" + i := 0 // ERROR "moved to heap: i" + j := 0 // ERROR "moved to heap: j" m[&i] = &j } @@ -77,8 +77,8 @@ func map6(m map[*int]*int) { // ERROR "m does not escape" if m != nil { m = make(map[*int]*int) // ERROR "make\(map\[\*int\]\*int\) does not escape" } - i := 0 // ERROR "moved to heap: i" - j := 0 // ERROR "moved to heap: j" + i := 0 // ERROR "moved to heap: i" + j := 0 // ERROR "moved to heap: j" m[&i] = &j } @@ -87,14 +87,14 @@ func map7() { i := 0 // ERROR "moved to heap: i" // BAD: j should not escape j := 0 // ERROR "moved to heap: j" - m := map[*int]*int{&i: &j} // ERROR "literal does not escape" + m := map[*int]*int{&i: &j} // ERROR "map\[\*int\]\*int{...} does not escape" _ = m } func map8() { i := 0 // ERROR "moved to heap: i" j := 0 // ERROR "moved to heap: j" - m := map[*int]*int{&i: &j} // ERROR "literal escapes to heap" + m := map[*int]*int{&i: &j} // ERROR "map\[\*int\]\*int{...} escapes to heap" sink = m } @@ -102,6 +102,6 @@ func map9() *int { // BAD: i should not escape i := 0 // ERROR "moved to heap: i" j := 0 // ERROR "moved to heap: j" - m := map[*int]*int{&i: &j} // ERROR "literal does not escape" + m := map[*int]*int{&i: &j} // ERROR "map\[\*int\]\*int{...} does not escape" return m[nil] } diff --git a/test/escape_param.go b/test/escape_param.go index d8fafc53f8..993e914e1d 100644 --- a/test/escape_param.go +++ b/test/escape_param.go @@ -26,7 +26,7 @@ func caller0a() { } func caller0b() { - i := 0 // ERROR "moved to heap: i$" + i := 0 // ERROR "moved to heap: i$" sink = param0(&i) } @@ -150,11 +150,11 @@ func caller3a() { } func caller3b() { - i := 0 // ERROR "moved to heap: i$" - j := 0 // ERROR "moved to heap: j$" + i := 0 // ERROR "moved to heap: i$" + j := 0 // ERROR "moved to heap: j$" p := Pair{&i, &j} param3(&p) - sink = p // ERROR "p escapes to heap$" + sink = p // ERROR "p escapes to heap$" } // in -> rcvr @@ -173,7 +173,7 @@ func caller4b() { i := 0 // ERROR "moved to heap: i$" p := Pair{} p.param4(&i) - sink = p // ERROR "p escapes to heap$" + sink = p // ERROR "p escapes to heap$" } // in -> heap @@ -182,7 +182,7 @@ func param5(i *int) { // ERROR "leaking param: i$" } func caller5() { - i := 0 // ERROR "moved to heap: i$" + i := 0 // ERROR "moved to heap: i$" param5(&i) } @@ -192,8 +192,8 @@ func param6(i ***int) { // ERROR "leaking param content: i$" } func caller6a() { - i := 0 // ERROR "moved to heap: i$" - p := &i // ERROR "moved to heap: p$" + i := 0 // ERROR "moved to heap: i$" + p := &i // ERROR "moved to heap: p$" p2 := &p param6(&p2) } @@ -204,7 +204,7 @@ func param7(i ***int) { // ERROR "leaking param content: i$" } func caller7() { - i := 0 // ERROR "moved to heap: i$" + i := 0 // ERROR "moved to heap: i$" p := &i p2 := &p param7(&p2) @@ -234,8 +234,8 @@ func caller9a() { } func caller9b() { - i := 0 // ERROR "moved to heap: i$" - p := &i // ERROR "moved to heap: p$" + i := 0 // ERROR "moved to heap: i$" + p := &i // ERROR "moved to heap: p$" p2 := &p sink = param9(&p2) } @@ -253,7 +253,7 @@ func caller10a() { } func caller10b() { - i := 0 // ERROR "moved to heap: i$" + i := 0 // ERROR "moved to heap: i$" p := &i p2 := &p sink = param10(&p2) @@ -265,26 +265,26 @@ func param11(i **int) ***int { // ERROR "moved to heap: i$" } func caller11a() { - i := 0 // ERROR "moved to heap: i" - p := &i // ERROR "moved to heap: p" + i := 0 // ERROR "moved to heap: i" + p := &i // ERROR "moved to heap: p" _ = param11(&p) } func caller11b() { - i := 0 // ERROR "moved to heap: i$" - p := &i // ERROR "moved to heap: p$" + i := 0 // ERROR "moved to heap: i$" + p := &i // ERROR "moved to heap: p$" sink = param11(&p) } func caller11c() { // GOOD - i := 0 // ERROR "moved to heap: i$" - p := &i // ERROR "moved to heap: p" + i := 0 // ERROR "moved to heap: i$" + p := &i // ERROR "moved to heap: p" sink = *param11(&p) } func caller11d() { - i := 0 // ERROR "moved to heap: i$" - p := &i // ERROR "moved to heap: p" + i := 0 // ERROR "moved to heap: i$" + p := &i // ERROR "moved to heap: p" p2 := &p sink = param11(p2) } @@ -309,7 +309,7 @@ func caller12a() { func caller12b() { i := 0 // ERROR "moved to heap: i$" p := &i // ERROR "moved to heap: p$" - r := &Indir{} // ERROR "&Indir literal does not escape$" + r := &Indir{} // ERROR "&Indir{} does not escape$" r.param12(&p) _ = r } @@ -359,7 +359,7 @@ func caller13b() { func caller13c() { i := 0 // ERROR "moved to heap: i$" var p *int - v := &Val{&p} // ERROR "&Val literal does not escape$" + v := &Val{&p} // ERROR "&Val{...} does not escape$" v.param13(&i) _ = v } @@ -374,8 +374,8 @@ func caller13d() { } func caller13e() { - i := 0 // ERROR "moved to heap: i$" - var p *int // ERROR "moved to heap: p$" + i := 0 // ERROR "moved to heap: i$" + var p *int // ERROR "moved to heap: p$" v := Val{&p} v.param13(&i) sink = v @@ -384,7 +384,7 @@ func caller13e() { func caller13f() { i := 0 // ERROR "moved to heap: i$" var p *int // ERROR "moved to heap: p$" - v := &Val{&p} // ERROR "&Val literal escapes to heap$" + v := &Val{&p} // ERROR "&Val{...} escapes to heap$" v.param13(&i) sink = v } @@ -400,9 +400,9 @@ func caller13g() { func caller13h() { i := 0 // ERROR "moved to heap: i$" var p *int - v := &Val{&p} // ERROR "&Val literal does not escape$" + v := &Val{&p} // ERROR "&Val{...} does not escape$" v.param13(&i) - sink = **v.p // ERROR "\* \(\*v\.p\) escapes to heap" + sink = **v.p // ERROR "\* \(\*v\.p\) escapes to heap" } type Node struct { @@ -412,15 +412,15 @@ type Node struct { var Sink *Node func f(x *Node) { // ERROR "leaking param content: x" - Sink = &Node{x.p} // ERROR "&Node literal escapes to heap" + Sink = &Node{x.p} // ERROR "&Node{...} escapes to heap" } func g(x *Node) *Node { // ERROR "leaking param content: x" - return &Node{x.p} // ERROR "&Node literal escapes to heap" + return &Node{x.p} // ERROR "&Node{...} escapes to heap" } func h(x *Node) { // ERROR "leaking param: x" - y := &Node{x} // ERROR "&Node literal does not escape" + y := &Node{x} // ERROR "&Node{...} does not escape" Sink = g(y) f(y) } diff --git a/test/escape_slice.go b/test/escape_slice.go index d2cdaa6a01..6ce852e9c5 100644 --- a/test/escape_slice.go +++ b/test/escape_slice.go @@ -77,19 +77,19 @@ func slice7() *int { func slice8() { i := 0 - s := []*int{&i} // ERROR "literal does not escape" + s := []*int{&i} // ERROR "\[\]\*int{...} does not escape" _ = s } func slice9() *int { i := 0 // ERROR "moved to heap: i" - s := []*int{&i} // ERROR "literal does not escape" + s := []*int{&i} // ERROR "\[\]\*int{...} does not escape" return s[0] } func slice10() []*int { i := 0 // ERROR "moved to heap: i" - s := []*int{&i} // ERROR "literal escapes to heap" + s := []*int{&i} // ERROR "\[\]\*int{...} escapes to heap" return s } @@ -103,7 +103,7 @@ func slice11() { func envForDir(dir string) []string { // ERROR "dir does not escape" env := os.Environ() - return mergeEnvLists([]string{"PWD=" + dir}, env) // ERROR ".PWD=. \+ dir escapes to heap" "\[\]string literal does not escape" + return mergeEnvLists([]string{"PWD=" + dir}, env) // ERROR ".PWD=. \+ dir escapes to heap" "\[\]string{...} does not escape" } func mergeEnvLists(in, out []string) []string { // ERROR "leaking param content: in" "leaking param content: out" "leaking param: out to result ~r2 level=0" @@ -160,14 +160,14 @@ var resolveIPAddrTests = []resolveIPAddrTest{ func setupTestData() { resolveIPAddrTests = append(resolveIPAddrTests, - []resolveIPAddrTest{ // ERROR "\[\]resolveIPAddrTest literal does not escape" + []resolveIPAddrTest{ // ERROR "\[\]resolveIPAddrTest{...} does not escape" {"ip", "localhost", - &IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr literal escapes to heap" + &IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr{...} escapes to heap" nil}, {"ip4", "localhost", - &IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr literal escapes to heap" + &IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr{...} escapes to heap" nil}, }...) } diff --git a/test/escape_struct_param1.go b/test/escape_struct_param1.go index 70b36191ab..496172c166 100644 --- a/test/escape_struct_param1.go +++ b/test/escape_struct_param1.go @@ -35,27 +35,27 @@ func (u *U) SPPi() *string { // ERROR "leaking param: u to result ~r0 level=2$" } func tSPPi() { - s := "cat" // ERROR "moved to heap: s$" + s := "cat" // ERROR "moved to heap: s$" ps := &s pps := &ps - pu := &U{ps, pps} // ERROR "&U literal does not escape$" + pu := &U{ps, pps} // ERROR "&U{...} does not escape$" Ssink = pu.SPPi() } func tiSPP() { - s := "cat" // ERROR "moved to heap: s$" + s := "cat" // ERROR "moved to heap: s$" ps := &s pps := &ps - pu := &U{ps, pps} // ERROR "&U literal does not escape$" + pu := &U{ps, pps} // ERROR "&U{...} does not escape$" Ssink = *pu.SPP() } // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of ps func tSP() { - s := "cat" // ERROR "moved to heap: s$" - ps := &s // ERROR "moved to heap: ps$" + s := "cat" // ERROR "moved to heap: s$" + ps := &s // ERROR "moved to heap: ps$" pps := &ps - pu := &U{ps, pps} // ERROR "&U literal does not escape$" + pu := &U{ps, pps} // ERROR "&U{...} does not escape$" Ssink = pu.SP() } @@ -114,72 +114,72 @@ func (v *V) UPiSPd() *string { // ERROR "leaking param: v to result ~r0 level=2$ // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3 func tUPiSPa() { s1 := "ant" - s2 := "bat" // ERROR "moved to heap: s2$" - s3 := "cat" // ERROR "moved to heap: s3$" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s2 := "bat" // ERROR "moved to heap: s2$" + s3 := "cat" // ERROR "moved to heap: s3$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 - ps4 := &s4 // ERROR "moved to heap: ps4$" - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps4 := &s4 // ERROR "moved to heap: ps4$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPa() // Ssink = &s3 (only &s3 really escapes) } // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3 func tUPiSPb() { s1 := "ant" - s2 := "bat" // ERROR "moved to heap: s2$" - s3 := "cat" // ERROR "moved to heap: s3$" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s2 := "bat" // ERROR "moved to heap: s2$" + s3 := "cat" // ERROR "moved to heap: s3$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 - ps4 := &s4 // ERROR "moved to heap: ps4$" - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps4 := &s4 // ERROR "moved to heap: ps4$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPb() // Ssink = &s3 (only &s3 really escapes) } // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3 func tUPiSPc() { s1 := "ant" - s2 := "bat" // ERROR "moved to heap: s2$" - s3 := "cat" // ERROR "moved to heap: s3$" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s2 := "bat" // ERROR "moved to heap: s2$" + s3 := "cat" // ERROR "moved to heap: s3$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 - ps4 := &s4 // ERROR "moved to heap: ps4$" - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps4 := &s4 // ERROR "moved to heap: ps4$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPc() // Ssink = &s3 (only &s3 really escapes) } // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3 func tUPiSPd() { s1 := "ant" - s2 := "bat" // ERROR "moved to heap: s2$" - s3 := "cat" // ERROR "moved to heap: s3$" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s2 := "bat" // ERROR "moved to heap: s2$" + s3 := "cat" // ERROR "moved to heap: s3$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 - ps4 := &s4 // ERROR "moved to heap: ps4$" - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps4 := &s4 // ERROR "moved to heap: ps4$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPd() // Ssink = &s3 (only &s3 really escapes) } @@ -204,16 +204,16 @@ func tUPiSPPia() { s1 := "ant" s2 := "bat" s3 := "cat" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPPia() // Ssink = *&ps4 = &s4 (only &s4 really escapes) } @@ -222,16 +222,16 @@ func tUPiSPPib() { s1 := "ant" s2 := "bat" s3 := "cat" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPPib() // Ssink = *&ps4 = &s4 (only &s4 really escapes) } @@ -240,16 +240,16 @@ func tUPiSPPic() { s1 := "ant" s2 := "bat" s3 := "cat" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPPic() // Ssink = *&ps4 = &s4 (only &s4 really escapes) } @@ -258,16 +258,16 @@ func tUPiSPPid() { s1 := "ant" s2 := "bat" s3 := "cat" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPPid() // Ssink = *&ps4 = &s4 (only &s4 really escapes) } @@ -286,13 +286,13 @@ func tUPPiSPPia() { s3 := "cat" s4 := "dog" s5 := "emu" - s6 := "fox" // ERROR "moved to heap: s6$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 ps6 := &s6 u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPPiSPPia() // Ssink = *&ps6 = &s6 (only &s6 really escapes) } diff --git a/test/escape_struct_param2.go b/test/escape_struct_param2.go index e42be79793..946397ea9f 100644 --- a/test/escape_struct_param2.go +++ b/test/escape_struct_param2.go @@ -35,27 +35,27 @@ func (u U) SPPi() *string { // ERROR "leaking param: u to result ~r0 level=1$" } func tSPPi() { - s := "cat" // ERROR "moved to heap: s$" + s := "cat" // ERROR "moved to heap: s$" ps := &s pps := &ps - pu := &U{ps, pps} // ERROR "&U literal does not escape$" + pu := &U{ps, pps} // ERROR "&U{...} does not escape$" Ssink = pu.SPPi() } func tiSPP() { - s := "cat" // ERROR "moved to heap: s$" + s := "cat" // ERROR "moved to heap: s$" ps := &s pps := &ps - pu := &U{ps, pps} // ERROR "&U literal does not escape$" + pu := &U{ps, pps} // ERROR "&U{...} does not escape$" Ssink = *pu.SPP() } // BAD: need fine-grained analysis to avoid spurious escape of ps func tSP() { - s := "cat" // ERROR "moved to heap: s$" - ps := &s // ERROR "moved to heap: ps$" + s := "cat" // ERROR "moved to heap: s$" + ps := &s // ERROR "moved to heap: ps$" pps := &ps - pu := &U{ps, pps} // ERROR "&U literal does not escape$" + pu := &U{ps, pps} // ERROR "&U{...} does not escape$" Ssink = pu.SP() } @@ -114,72 +114,72 @@ func (v V) UPiSPd() *string { // ERROR "leaking param: v to result ~r0 level=1$" // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3 func tUPiSPa() { s1 := "ant" - s2 := "bat" // ERROR "moved to heap: s2$" - s3 := "cat" // ERROR "moved to heap: s3$" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s2 := "bat" // ERROR "moved to heap: s2$" + s3 := "cat" // ERROR "moved to heap: s3$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 - ps4 := &s4 // ERROR "moved to heap: ps4$" - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps4 := &s4 // ERROR "moved to heap: ps4$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPa() // Ssink = &s3 (only &s3 really escapes) } // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3 func tUPiSPb() { s1 := "ant" - s2 := "bat" // ERROR "moved to heap: s2$" - s3 := "cat" // ERROR "moved to heap: s3$" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s2 := "bat" // ERROR "moved to heap: s2$" + s3 := "cat" // ERROR "moved to heap: s3$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 - ps4 := &s4 // ERROR "moved to heap: ps4$" - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps4 := &s4 // ERROR "moved to heap: ps4$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPb() // Ssink = &s3 (only &s3 really escapes) } // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3 func tUPiSPc() { s1 := "ant" - s2 := "bat" // ERROR "moved to heap: s2$" - s3 := "cat" // ERROR "moved to heap: s3$" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s2 := "bat" // ERROR "moved to heap: s2$" + s3 := "cat" // ERROR "moved to heap: s3$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 - ps4 := &s4 // ERROR "moved to heap: ps4$" - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps4 := &s4 // ERROR "moved to heap: ps4$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPc() // Ssink = &s3 (only &s3 really escapes) } // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3 func tUPiSPd() { s1 := "ant" - s2 := "bat" // ERROR "moved to heap: s2$" - s3 := "cat" // ERROR "moved to heap: s3$" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s2 := "bat" // ERROR "moved to heap: s2$" + s3 := "cat" // ERROR "moved to heap: s3$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 - ps4 := &s4 // ERROR "moved to heap: ps4$" - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps4 := &s4 // ERROR "moved to heap: ps4$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPd() // Ssink = &s3 (only &s3 really escapes) } @@ -204,16 +204,16 @@ func tUPiSPPia() { s1 := "ant" s2 := "bat" s3 := "cat" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPPia() // Ssink = *&ps4 = &s4 (only &s4 really escapes) } @@ -222,16 +222,16 @@ func tUPiSPPib() { s1 := "ant" s2 := "bat" s3 := "cat" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPPib() // Ssink = *&ps4 = &s4 (only &s4 really escapes) } @@ -240,16 +240,16 @@ func tUPiSPPic() { s1 := "ant" s2 := "bat" s3 := "cat" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPPic() // Ssink = *&ps4 = &s4 (only &s4 really escapes) } @@ -258,16 +258,16 @@ func tUPiSPPid() { s1 := "ant" s2 := "bat" s3 := "cat" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPPid() // Ssink = *&ps4 = &s4 (only &s4 really escapes) } @@ -286,13 +286,13 @@ func tUPPiSPPia() { // This test is sensitive to the level cap in function summa s3 := "cat" s4 := "dog" s5 := "emu" - s6 := "fox" // ERROR "moved to heap: s6$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 ps6 := &s6 u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPPiSPPia() // Ssink = *&ps6 = &s6 (only &s6 really escapes) } diff --git a/test/fixedbugs/issue12006.go b/test/fixedbugs/issue12006.go index c44f2e5547..0a2ef8dad0 100644 --- a/test/fixedbugs/issue12006.go +++ b/test/fixedbugs/issue12006.go @@ -144,7 +144,7 @@ func TFooK2() { a := int32(1) // ERROR "moved to heap: a" b := "cat" c := &a - fs := fakeSlice{3, &[4]interface{}{a, b, c, nil}} // ERROR "a escapes to heap" "b escapes to heap" "&\[4\]interface {} literal does not escape" + fs := fakeSlice{3, &[4]interface{}{a, b, c, nil}} // ERROR "a escapes to heap" "b escapes to heap" "&\[4\]interface {}{...} does not escape" isink = FooK(fs) } @@ -169,6 +169,6 @@ func TFooL2() { a := int32(1) // ERROR "moved to heap: a" b := "cat" c := &a - s := []interface{}{a, b, c} // ERROR "a escapes to heap" "b escapes to heap" "\[\]interface {} literal does not escape" + s := []interface{}{a, b, c} // ERROR "a escapes to heap" "b escapes to heap" "\[\]interface {}{...} does not escape" isink = FooL(s) } diff --git a/test/fixedbugs/issue13799.go b/test/fixedbugs/issue13799.go index 5c57494777..fbdd4c32bc 100644 --- a/test/fixedbugs/issue13799.go +++ b/test/fixedbugs/issue13799.go @@ -162,7 +162,7 @@ func test5(iter int) { var fn *str for i := 0; i < maxI; i++ { // var fn *str // this makes it work, because fn stays off heap - fn = &str{m} // ERROR "&str literal escapes to heap" + fn = &str{m} // ERROR "&str{...} escapes to heap" recur1(0, fn) } @@ -180,7 +180,7 @@ func test6(iter int) { // var fn *str for i := 0; i < maxI; i++ { var fn *str // this makes it work, because fn stays off heap - fn = &str{m} // ERROR "&str literal does not escape" + fn = &str{m} // ERROR "&str{...} does not escape" recur1(0, fn) } diff --git a/test/fixedbugs/issue17645.go b/test/fixedbugs/issue17645.go index af785eae2a..95fcecd1e0 100644 --- a/test/fixedbugs/issue17645.go +++ b/test/fixedbugs/issue17645.go @@ -12,5 +12,5 @@ type Foo struct { func main() { var s []int - var _ string = append(s, Foo{""}) // ERROR "cannot use .. \(type untyped string\) as type int in field value" "cannot use Foo literal \(type Foo\) as type int in append" "cannot use append\(s\, Foo literal\) \(type \[\]int\) as type string in assignment" + var _ string = append(s, Foo{""}) // ERROR "cannot use .. \(type untyped string\) as type int in field value" "cannot use Foo{...} \(type Foo\) as type int in append" "cannot use append\(s\, Foo{...}\) \(type \[\]int\) as type string in assignment" } diff --git a/test/fixedbugs/issue21709.go b/test/fixedbugs/issue21709.go index cc5896ab53..20be10e792 100644 --- a/test/fixedbugs/issue21709.go +++ b/test/fixedbugs/issue21709.go @@ -16,7 +16,7 @@ var N int func F1() { var s S for i := 0; i < N; i++ { - fs := []func(){ // ERROR "\[\]func\(\) literal does not escape" + fs := []func(){ // ERROR "\[\]func\(\){...} does not escape" s.Inc, // ERROR "s.Inc does not escape" } for _, f := range fs { @@ -28,7 +28,7 @@ func F1() { func F2() { var s S for i := 0; i < N; i++ { - for _, f := range []func(){ // ERROR "\[\]func\(\) literal does not escape" + for _, f := range []func(){ // ERROR "\[\]func\(\){...} does not escape" s.Inc, // ERROR "s.Inc does not escape" } { f() diff --git a/test/fixedbugs/issue23732.go b/test/fixedbugs/issue23732.go index be17bf4f61..5e63eb2074 100644 --- a/test/fixedbugs/issue23732.go +++ b/test/fixedbugs/issue23732.go @@ -24,19 +24,19 @@ func main() { _ = Foo{ 1, 2, - 3, // ERROR "too few values in Foo literal" + 3, // ERROR "too few values in Foo{...}" } _ = Foo{ 1, 2, 3, - Bar{"A", "B"}, // ERROR "too many values in Bar literal" + Bar{"A", "B"}, // ERROR "too many values in Bar{...}" } _ = Foo{ 1, 2, - Bar{"A", "B"}, // ERROR "too many values in Bar literal" "too few values in Foo literal" + Bar{"A", "B"}, // ERROR "too many values in Bar{...}" "too few values in Foo{...}" } } diff --git a/test/fixedbugs/issue26855.go b/test/fixedbugs/issue26855.go index d5b95ddbf1..144e4415f7 100644 --- a/test/fixedbugs/issue26855.go +++ b/test/fixedbugs/issue26855.go @@ -20,9 +20,9 @@ type P struct { type T struct{} var _ = S{ - f: &T{}, // ERROR "cannot use &T literal" + f: &T{}, // ERROR "cannot use &T{}" } var _ = P{ - f: T{}, // ERROR "cannot use T literal" + f: T{}, // ERROR "cannot use T{}" } diff --git a/test/fixedbugs/issue30898.go b/test/fixedbugs/issue30898.go index 012d5a2634..b6376d3f9e 100644 --- a/test/fixedbugs/issue30898.go +++ b/test/fixedbugs/issue30898.go @@ -15,5 +15,5 @@ func debugf(format string, args ...interface{}) { // ERROR "can inline debugf" " func bar() { // ERROR "can inline bar" value := 10 - debugf("value is %d", value) // ERROR "inlining call to debugf" "value does not escape" "\[\]interface {} literal does not escape" + debugf("value is %d", value) // ERROR "inlining call to debugf" "value does not escape" "\[\]interface {}{...} does not escape" } diff --git a/test/fixedbugs/issue31573.go b/test/fixedbugs/issue31573.go index c9ea84bbae..005910e00d 100644 --- a/test/fixedbugs/issue31573.go +++ b/test/fixedbugs/issue31573.go @@ -14,18 +14,18 @@ func g() { defer f(new(int), new(int)) // ERROR "... argument does not escape$" "new\(int\) does not escape$" defer f(nil...) - defer f([]*int{}...) // ERROR "\[\]\*int literal does not escape$" - defer f([]*int{new(int)}...) // ERROR "\[\]\*int literal does not escape$" "new\(int\) does not escape$" - defer f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int literal does not escape$" "new\(int\) does not escape$" + defer f([]*int{}...) // ERROR "\[\]\*int{} does not escape$" + defer f([]*int{new(int)}...) // ERROR "\[\]\*int{...} does not escape$" "new\(int\) does not escape$" + defer f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} does not escape$" "new\(int\) does not escape$" go f() go f(new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$" go f(new(int), new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$" go f(nil...) - go f([]*int{}...) // ERROR "\[\]\*int literal escapes to heap$" - go f([]*int{new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$" - go f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$" + go f([]*int{}...) // ERROR "\[\]\*int{} escapes to heap$" + go f([]*int{new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" + go f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" for { defer f() @@ -33,17 +33,17 @@ func g() { defer f(new(int), new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$" defer f(nil...) - defer f([]*int{}...) // ERROR "\[\]\*int literal escapes to heap$" - defer f([]*int{new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$" - defer f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$" + defer f([]*int{}...) // ERROR "\[\]\*int{} escapes to heap$" + defer f([]*int{new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" + defer f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" go f() go f(new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$" go f(new(int), new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$" go f(nil...) - go f([]*int{}...) // ERROR "\[\]\*int literal escapes to heap$" - go f([]*int{new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$" - go f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$" + go f([]*int{}...) // ERROR "\[\]\*int{} escapes to heap$" + go f([]*int{new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" + go f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" } } diff --git a/test/fixedbugs/issue38745.go b/test/fixedbugs/issue38745.go new file mode 100644 index 0000000000..21bd1ff3a7 --- /dev/null +++ b/test/fixedbugs/issue38745.go @@ -0,0 +1,19 @@ +// errorcheck + +// 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 p + +type t struct{ x int } + +func f1() { + t{}.M() // ERROR "t{}.M undefined \(type t has no field or method M\)" + t{x: 1}.M() // ERROR "t{...}.M undefined \(type t has no field or method M\)" +} + +func f2() (*t, error) { + // BAD: should report undefined error only. + return t{}.M() // ERROR "t{}.M undefined \(type t has no field or method M\)" "not enough arguments to return" +} diff --git a/test/fixedbugs/issue39292.go b/test/fixedbugs/issue39292.go index 1be88653e9..7dac2e5fc6 100644 --- a/test/fixedbugs/issue39292.go +++ b/test/fixedbugs/issue39292.go @@ -12,18 +12,18 @@ func (t) f() { } func x() { - x := t{}.f // ERROR "t literal.f escapes to heap" + x := t{}.f // ERROR "t{}.f escapes to heap" x() } func y() { var i int // ERROR "moved to heap: i" - y := (&t{&i}).f // ERROR "\(&t literal\).f escapes to heap" "&t literal escapes to heap" + y := (&t{&i}).f // ERROR "\(&t{...}\).f escapes to heap" "&t{...} escapes to heap" y() } func z() { var i int // ERROR "moved to heap: i" - z := t{&i}.f // ERROR "t literal.f escapes to heap" + z := t{&i}.f // ERROR "t{...}.f escapes to heap" z() } diff --git a/test/fixedbugs/issue41247.go b/test/fixedbugs/issue41247.go index 2df919c9e6..b8bd81274f 100644 --- a/test/fixedbugs/issue41247.go +++ b/test/fixedbugs/issue41247.go @@ -7,5 +7,5 @@ package p func f() [2]int { - return [...]int{2: 0} // ERROR "cannot use \[\.\.\.\]int literal \(type \[3\]int\)" + return [...]int{2: 0} // ERROR "cannot use \[\.\.\.\]int{...} \(type \[3\]int\)" } diff --git a/test/fixedbugs/issue7921.go b/test/fixedbugs/issue7921.go index a8efc8dd9e..5dce557ca3 100644 --- a/test/fixedbugs/issue7921.go +++ b/test/fixedbugs/issue7921.go @@ -18,12 +18,12 @@ func bufferNotEscape() string { // can be stack-allocated. var b bytes.Buffer b.WriteString("123") - b.Write([]byte{'4'}) // ERROR "\[\]byte literal does not escape$" + b.Write([]byte{'4'}) // ERROR "\[\]byte{...} does not escape$" return b.String() // ERROR "inlining call to bytes.\(\*Buffer\).String$" "string\(bytes.b.buf\[bytes.b.off:\]\) escapes to heap$" } func bufferNoEscape2(xs []string) int { // ERROR "xs does not escape$" - b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "&bytes.Buffer literal does not escape$" "make\(\[\]byte, 0, 64\) does not escape$" "inlining call to bytes.NewBuffer$" + b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "&bytes.Buffer{...} does not escape$" "make\(\[\]byte, 0, 64\) does not escape$" "inlining call to bytes.NewBuffer$" for _, x := range xs { b.WriteString(x) } @@ -31,7 +31,7 @@ func bufferNoEscape2(xs []string) int { // ERROR "xs does not escape$" } func bufferNoEscape3(xs []string) string { // ERROR "xs does not escape$" - b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "&bytes.Buffer literal does not escape$" "make\(\[\]byte, 0, 64\) does not escape$" "inlining call to bytes.NewBuffer$" + b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "&bytes.Buffer{...} does not escape$" "make\(\[\]byte, 0, 64\) does not escape$" "inlining call to bytes.NewBuffer$" for _, x := range xs { b.WriteString(x) b.WriteByte(',') @@ -41,13 +41,13 @@ func bufferNoEscape3(xs []string) string { // ERROR "xs does not escape$" func bufferNoEscape4() []byte { var b bytes.Buffer - b.Grow(64) // ERROR "bufferNoEscape4 ignoring self-assignment in bytes.b.buf = bytes.b.buf\[:bytes.m·3\]$" "inlining call to bytes.\(\*Buffer\).Grow$" + b.Grow(64) // ERROR "bufferNoEscape4 ignoring self-assignment in bytes.b.buf = bytes.b.buf\[:bytes.m·3\]$" "inlining call to bytes.\(\*Buffer\).Grow$" useBuffer(&b) return b.Bytes() // ERROR "inlining call to bytes.\(\*Buffer\).Bytes$" } func bufferNoEscape5() { // ERROR "can inline bufferNoEscape5$" - b := bytes.NewBuffer(make([]byte, 0, 128)) // ERROR "&bytes.Buffer literal does not escape$" "make\(\[\]byte, 0, 128\) does not escape$" "inlining call to bytes.NewBuffer$" + b := bytes.NewBuffer(make([]byte, 0, 128)) // ERROR "&bytes.Buffer{...} does not escape$" "make\(\[\]byte, 0, 128\) does not escape$" "inlining call to bytes.NewBuffer$" useBuffer(b) } diff --git a/test/inline_variadic.go b/test/inline_variadic.go index fcc1cff1e8..687048a192 100644 --- a/test/inline_variadic.go +++ b/test/inline_variadic.go @@ -14,6 +14,6 @@ func head(xs ...string) string { // ERROR "can inline head" "leaking param: xs t } func f() string { // ERROR "can inline f" - x := head("hello", "world") // ERROR "inlining call to head" "\[\]string literal does not escape" + x := head("hello", "world") // ERROR "inlining call to head" "\[\]string{...} does not escape" return x } -- cgit v1.2.3-54-g00ecf From 806f478499b57c5167fb5301101961b7563903d2 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Wed, 9 Sep 2020 16:25:48 +0700 Subject: cmd/compile: don't report not enough args error if call is undefined Fixes #38745 Change-Id: I2fbd8b512a8cf911b81a087162c74416116efea5 Reviewed-on: https://go-review.googlesource.com/c/go/+/253678 Run-TryBot: Cuong Manh Le TryBot-Result: Gobot Gobot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/typecheck.go | 2 +- test/ddd1.go | 2 +- test/fixedbugs/issue38745.go | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index dec4b96fc4..fb169cfec8 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -2667,7 +2667,7 @@ func typecheckaste(op Op, call *Node, isddd bool, tstruct *types.Type, nl Nodes, return notenough: - if n == nil || !n.Diag() { + if n == nil || (!n.Diag() && n.Type != nil) { details := errorDetails(nl, tstruct, isddd) if call != nil { // call is the expression being called, not the overall call. diff --git a/test/ddd1.go b/test/ddd1.go index 2c7e83e374..9857814648 100644 --- a/test/ddd1.go +++ b/test/ddd1.go @@ -29,7 +29,7 @@ var ( _ = sum(tuple()) _ = sum(tuple()...) // ERROR "multiple-value" _ = sum3(tuple()) - _ = sum3(tuple()...) // ERROR "multiple-value" "not enough" + _ = sum3(tuple()...) // ERROR "multiple-value" ) type T []T diff --git a/test/fixedbugs/issue38745.go b/test/fixedbugs/issue38745.go index 21bd1ff3a7..83a3bc6fad 100644 --- a/test/fixedbugs/issue38745.go +++ b/test/fixedbugs/issue38745.go @@ -14,6 +14,5 @@ func f1() { } func f2() (*t, error) { - // BAD: should report undefined error only. - return t{}.M() // ERROR "t{}.M undefined \(type t has no field or method M\)" "not enough arguments to return" + return t{}.M() // ERROR "t{}.M undefined \(type t has no field or method M\)" } -- cgit v1.2.3-54-g00ecf From 92b2b8860dcc28461198c6125fbae2383161d2e5 Mon Sep 17 00:00:00 2001 From: Daniel Martí Date: Sat, 15 Aug 2020 16:20:50 +0200 Subject: cmd/go: avoid flag.FlagSet.VisitAll at init time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to error early if GOFLAGS contains any flag that isn't known to any cmd/go command. Thus, at init time we would recursively use VisitAll on each of the flagsets to populate a map of all registered flags. This was unfortunate, as populating said map constituted a whole 5% of the run-time of 'go env GOARCH'. This is because VisitAll is pretty expensive; it copies all the maps from the flagset's map to a slice, sorts the slice, then does one callback per flag. First, this was a bit wasteful. We only ever needed to query the knownFlag map if GOFLAGS wasn't empty. If it's empty, there's no work to do, thus we can skip the map populating work. Second and most important, we don't actually need the map at all. A flag.FlagSet already has a Lookup method, so we can simply recursively call those methods for each flag in GOFLAGS. Add a hasFlag func to make that evident. This mechanism is different; its upfront cost is none, but it will likely mean a handful of map lookups for each flag in GOFLAGS. However, that tradeoff is worth it; we don't expect GOFLAGS to contain thousands of flags. The most likely scenario is less than a dozen flags, in which case constructing a "unified" map is not at all a net win. One possible reason the previous mechanism was that way could be AddKnownFlag. Thankfully, the one and only use of that API was removed last year when Bryan cleaned up flag parsing in cmd/go. The wins for the existing benchmark with an empty GOFLAGS are significant: name old time/op new time/op delta ExecGoEnv-8 575µs ± 1% 549µs ± 2% -4.44% (p=0.000 n=7+8) name old sys-time/op new sys-time/op delta ExecGoEnv-8 1.69ms ± 1% 1.68ms ± 2% ~ (p=0.281 n=7+8) name old user-time/op new user-time/op delta ExecGoEnv-8 1.80ms ± 1% 1.66ms ± 2% -8.09% (p=0.000 n=7+8) To prove that a relatively large number of GOFLAGS isn't getting noticeably slower, we measured that as well, via benchcmd and GOFLAGS containing 50 valid flags: GOFLAGS=$(yes -- -race | sed 50q) benchcmd -n 500 GoEnvGOFLAGS go env GOARCH And the result, while noisy, shows no noticeable difference (note that it measures 3ms instead of 0.6ms since it's sequential): name old time/op new time/op delta GoEnvGOFLAGS 3.04ms ±32% 3.03ms ±35% ~ (p=0.156 n=487+481) Finally, we've improved the existing Go benchmark. Now it's parallel, and it also reports sys-time and user-time, which are useful metrics. Change-Id: I9b4551415cedf2f819eb184a02324b8bd919e2bd Reviewed-on: https://go-review.googlesource.com/c/go/+/248757 Reviewed-by: Bryan C. Mills Run-TryBot: Bryan C. Mills TryBot-Result: Gobot Gobot --- src/cmd/go/init_test.go | 26 +++++++++++++++++--------- src/cmd/go/internal/base/base.go | 14 ++++++++++++++ src/cmd/go/internal/base/goflags.go | 37 +++++++------------------------------ 3 files changed, 38 insertions(+), 39 deletions(-) diff --git a/src/cmd/go/init_test.go b/src/cmd/go/init_test.go index ed90a77841..5a5cbe5293 100644 --- a/src/cmd/go/init_test.go +++ b/src/cmd/go/init_test.go @@ -7,6 +7,7 @@ package main_test import ( "internal/testenv" "os/exec" + "sync/atomic" "testing" ) @@ -15,20 +16,27 @@ import ( // the benchmark if any changes were done. func BenchmarkExecGoEnv(b *testing.B) { testenv.MustHaveExec(b) - b.StopTimer() gotool, err := testenv.GoTool() if err != nil { b.Fatal(err) } - for i := 0; i < b.N; i++ { - cmd := exec.Command(gotool, "env", "GOARCH") - b.StartTimer() - err := cmd.Run() - b.StopTimer() + // We collect extra metrics. + var n, userTime, systemTime int64 - if err != nil { - b.Fatal(err) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + cmd := exec.Command(gotool, "env", "GOARCH") + + if err := cmd.Run(); err != nil { + b.Fatal(err) + } + atomic.AddInt64(&n, 1) + atomic.AddInt64(&userTime, int64(cmd.ProcessState.UserTime())) + atomic.AddInt64(&systemTime, int64(cmd.ProcessState.SystemTime())) } - } + }) + b.ReportMetric(float64(userTime)/float64(n), "user-ns/op") + b.ReportMetric(float64(systemTime)/float64(n), "sys-ns/op") } diff --git a/src/cmd/go/internal/base/base.go b/src/cmd/go/internal/base/base.go index db3ebef933..004588c732 100644 --- a/src/cmd/go/internal/base/base.go +++ b/src/cmd/go/internal/base/base.go @@ -56,6 +56,20 @@ var Go = &Command{ // Commands initialized in package main } +// hasFlag reports whether a command or any of its subcommands contain the given +// flag. +func hasFlag(c *Command, name string) bool { + if f := c.Flag.Lookup(name); f != nil { + return true + } + for _, sub := range c.Commands { + if hasFlag(sub, name) { + return true + } + } + return false +} + // LongName returns the command's long name: all the words in the usage line between "go" and a flag or argument, func (c *Command) LongName() string { name := c.UsageLine diff --git a/src/cmd/go/internal/base/goflags.go b/src/cmd/go/internal/base/goflags.go index 34766134b0..f11f9a5d33 100644 --- a/src/cmd/go/internal/base/goflags.go +++ b/src/cmd/go/internal/base/goflags.go @@ -13,15 +13,7 @@ import ( "cmd/go/internal/cfg" ) -var ( - goflags []string // cached $GOFLAGS list; can be -x or --x form - knownFlag = make(map[string]bool) // flags allowed to appear in $GOFLAGS; no leading dashes -) - -// AddKnownFlag adds name to the list of known flags for use in $GOFLAGS. -func AddKnownFlag(name string) { - knownFlag[name] = true -} +var goflags []string // cached $GOFLAGS list; can be -x or --x form // GOFLAGS returns the flags from $GOFLAGS. // The list can be assumed to contain one string per flag, @@ -38,22 +30,12 @@ func InitGOFLAGS() { return } - // Build list of all flags for all commands. - // If no command has that flag, then we report the problem. - // This catches typos while still letting users record flags in GOFLAGS - // that only apply to a subset of go commands. - // Commands using CustomFlags can report their flag names - // by calling AddKnownFlag instead. - var walkFlags func(*Command) - walkFlags = func(cmd *Command) { - for _, sub := range cmd.Commands { - walkFlags(sub) - } - cmd.Flag.VisitAll(func(f *flag.Flag) { - knownFlag[f.Name] = true - }) + goflags = strings.Fields(cfg.Getenv("GOFLAGS")) + if len(goflags) == 0 { + // nothing to do; avoid work on later InitGOFLAGS call + goflags = []string{} + return } - walkFlags(Go) // Ignore bad flag in go env and go bug, because // they are what people reach for when debugging @@ -61,11 +43,6 @@ func InitGOFLAGS() { // (Both will show the GOFLAGS setting if let succeed.) hideErrors := cfg.CmdName == "env" || cfg.CmdName == "bug" - goflags = strings.Fields(cfg.Getenv("GOFLAGS")) - if goflags == nil { - goflags = []string{} // avoid work on later InitGOFLAGS call - } - // Each of the words returned by strings.Fields must be its own flag. // To set flag arguments use -x=value instead of -x value. // For boolean flags, -x is fine instead of -x=true. @@ -85,7 +62,7 @@ func InitGOFLAGS() { if i := strings.Index(name, "="); i >= 0 { name = name[:i] } - if !knownFlag[name] { + if !hasFlag(Go, name) { if hideErrors { continue } -- cgit v1.2.3-54-g00ecf From b3ef90ec7304a28b89f616ced20b09f56be30cc4 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Fri, 11 Sep 2020 22:16:47 +0000 Subject: encoding/json: implement Is on SyntaxError Allows users to check: errors.Is(err, &json.SyntaxError{}) which is the recommended way of checking for kinds of errors. Change-Id: I20dc805f20212765e9936a82d9cb7822e73ec4ef GitHub-Last-Rev: e2627ccf8e2a00cc3459bb9fee86c3c8675a33af GitHub-Pull-Request: golang/go#41210 Reviewed-on: https://go-review.googlesource.com/c/go/+/253037 Reviewed-by: Emmanuel Odeke Run-TryBot: Emmanuel Odeke TryBot-Result: Gobot Gobot --- src/encoding/json/scanner.go | 6 ++++++ src/encoding/json/scanner_test.go | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/src/encoding/json/scanner.go b/src/encoding/json/scanner.go index 9dc1903e2d..05218f9cc3 100644 --- a/src/encoding/json/scanner.go +++ b/src/encoding/json/scanner.go @@ -49,6 +49,12 @@ type SyntaxError struct { func (e *SyntaxError) Error() string { return e.msg } +// Is returns true if target is a SyntaxError. +func (e *SyntaxError) Is(target error) bool { + _, ok := target.(*SyntaxError) + return ok +} + // A scanner is a JSON scanning state machine. // Callers call scan.reset and then pass bytes in one at a time // by calling scan.step(&scan, c) for each byte. diff --git a/src/encoding/json/scanner_test.go b/src/encoding/json/scanner_test.go index 3737516a45..c12d9bf3d7 100644 --- a/src/encoding/json/scanner_test.go +++ b/src/encoding/json/scanner_test.go @@ -6,6 +6,8 @@ package json import ( "bytes" + "errors" + "fmt" "math" "math/rand" "reflect" @@ -201,6 +203,13 @@ func TestIndentErrors(t *testing.T) { } } +func TestSyntaxErrorIs(t *testing.T) { + err := fmt.Errorf("apackage: %w: failed to parse struct", &SyntaxError{"some error", 43}) + if !errors.Is(err, &SyntaxError{}) { + t.Fatalf("%v should be unwrapped to a SyntaxError", err) + } +} + func diff(t *testing.T, a, b []byte) { for i := 0; ; i++ { if i >= len(a) || i >= len(b) || a[i] != b[i] { -- cgit v1.2.3-54-g00ecf From 95bb00d1088767ed14e3bd1a5f533a690d619a5f Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Sun, 13 Sep 2020 02:12:02 +0000 Subject: encoding/json: implement Is on all errors Allows users to check: errors.Is(err, &UnmarshalTypeError{}) errors.Is(err, &UnmarshalFieldError{}) errors.Is(err, &InvalidUnmarshalError{}) errors.Is(err, &UnsupportedValueError{}) errors.Is(err, &MarshalerError{}) which is the recommended way of checking for kinds of errors. SyntaxError.Is was implemented in CL 253037. As and Unwrap relevant methods will be added in future CLs. Change-Id: I1f8a503b8fdc0f3afdfe9669a91f3af8d960e028 GitHub-Last-Rev: 930cda5384c987a0b31f277ba3b4ab690ea74ac3 GitHub-Pull-Request: golang/go#41360 Reviewed-on: https://go-review.googlesource.com/c/go/+/254537 Run-TryBot: Emmanuel Odeke TryBot-Result: Gobot Gobot Reviewed-by: Emmanuel Odeke Trust: Emmanuel Odeke --- src/encoding/json/decode.go | 18 ++++++++++++++++++ src/encoding/json/decode_test.go | 31 +++++++++++++++++++++++++++++++ src/encoding/json/encode.go | 12 ++++++++++++ src/encoding/json/encode_test.go | 24 +++++++++++++++++++++++- 4 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go index 86d8a69db7..1b006ffb17 100644 --- a/src/encoding/json/decode.go +++ b/src/encoding/json/decode.go @@ -136,6 +136,12 @@ func (e *UnmarshalTypeError) Error() string { return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() } +// Is returns true if target is a UnmarshalTypeError. +func (e *UnmarshalTypeError) Is(target error) bool { + _, ok := target.(*UnmarshalTypeError) + return ok +} + // An UnmarshalFieldError describes a JSON object key that // led to an unexported (and therefore unwritable) struct field. // @@ -150,12 +156,24 @@ func (e *UnmarshalFieldError) Error() string { return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() } +// Is returns true if target is a UnmarshalFieldError. +func (e *UnmarshalFieldError) Is(target error) bool { + _, ok := target.(*UnmarshalFieldError) + return ok +} + // An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. // (The argument to Unmarshal must be a non-nil pointer.) type InvalidUnmarshalError struct { Type reflect.Type } +// Is returns true if target is a InvalidUnmarshalError. +func (e *InvalidUnmarshalError) Is(target error) bool { + _, ok := target.(*InvalidUnmarshalError) + return ok +} + func (e *InvalidUnmarshalError) Error() string { if e.Type == nil { return "json: Unmarshal(nil)" diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go index 219e845c7b..b707dcfa99 100644 --- a/src/encoding/json/decode_test.go +++ b/src/encoding/json/decode_test.go @@ -2572,3 +2572,34 @@ func TestUnmarshalMaxDepth(t *testing.T) { } } } + +func TestInvalidUnmarshalErrorIs(t *testing.T) { + err := fmt.Errorf("apackage: %w: failed to parse struct", &InvalidUnmarshalError{reflect.TypeOf("a")}) + if !errors.Is(err, &InvalidUnmarshalError{}) { + t.Fatalf("%v should be unwrapped to a InvalidUnmarshalError", err) + } +} + +func TestUnmarshalFieldErrorIs(t *testing.T) { + err := fmt.Errorf("apackage: %w: failed to parse struct", &UnmarshalFieldError{ + Key: "foo", + Type: reflect.TypeOf("a"), + Field: reflect.StructField{Name: "b"}, + }) + if !errors.Is(err, &UnmarshalFieldError{}) { + t.Fatalf("%v should be unwrapped to a UnmarshalFieldError", err) + } +} + +func TestUnmarshalTypeErrorIs(t *testing.T) { + err := fmt.Errorf("apackage: %w: failed to parse struct", &UnmarshalTypeError{ + Value: "foo", + Type: reflect.TypeOf("a"), + Offset: 1, + Struct: "Foo", + Field: "Bar", + }) + if !errors.Is(err, &UnmarshalTypeError{}) { + t.Fatalf("%v should be unwrapped to a UnmarshalTypeError", err) + } +} diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go index 578d551102..8e6b342b59 100644 --- a/src/encoding/json/encode.go +++ b/src/encoding/json/encode.go @@ -245,6 +245,12 @@ func (e *UnsupportedValueError) Error() string { return "json: unsupported value: " + e.Str } +// Is returns true if target is a UnsupportedValueError. +func (e *UnsupportedValueError) Is(target error) bool { + _, ok := target.(*UnsupportedValueError) + return ok +} + // Before Go 1.2, an InvalidUTF8Error was returned by Marshal when // attempting to encode a string value with invalid UTF-8 sequences. // As of Go 1.2, Marshal instead coerces the string to valid UTF-8 by @@ -279,6 +285,12 @@ func (e *MarshalerError) Error() string { // Unwrap returns the underlying error. func (e *MarshalerError) Unwrap() error { return e.Err } +// Is returns true if target is a MarshalerError. +func (e *MarshalerError) Is(target error) bool { + _, ok := target.(*MarshalerError) + return ok +} + var hex = "0123456789abcdef" // An encodeState encodes JSON into a bytes.Buffer. diff --git a/src/encoding/json/encode_test.go b/src/encoding/json/encode_test.go index 7290eca06f..90826a7f47 100644 --- a/src/encoding/json/encode_test.go +++ b/src/encoding/json/encode_test.go @@ -7,6 +7,7 @@ package json import ( "bytes" "encoding" + "errors" "fmt" "log" "math" @@ -211,7 +212,7 @@ var unsupportedValues = []interface{}{ func TestUnsupportedValues(t *testing.T) { for _, v := range unsupportedValues { if _, err := Marshal(v); err != nil { - if _, ok := err.(*UnsupportedValueError); !ok { + if !errors.Is(err, &UnsupportedValueError{}) { t.Errorf("for %v, got %T want UnsupportedValueError", v, err) } } else { @@ -1155,3 +1156,24 @@ func TestMarshalerError(t *testing.T) { } } } + +func TestMarshalerErrorIs(t *testing.T) { + err := fmt.Errorf("apackage: %w: failed to parse struct", &MarshalerError{ + reflect.TypeOf("a"), + fmt.Errorf("something"), + "TestMarshalerErrorIs", + }) + if !errors.Is(err, &MarshalerError{}) { + t.Fatalf("%v should be unwrapped to a MarshalerError", err) + } +} + +func TestUnsupportedValueErrorIs(t *testing.T) { + err := fmt.Errorf("apackage: %w: failed to parse struct", &UnsupportedValueError{ + Value: reflect.Value{}, + Str: "Foo", + }) + if !errors.Is(err, &UnsupportedValueError{}) { + t.Fatalf("%v should be unwrapped to a UnsupportedValueError", err) + } +} -- cgit v1.2.3-54-g00ecf From 1f4521669416a2e14fb0b84481447f4a93f19878 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Sat, 12 Sep 2020 01:57:27 +0700 Subject: cmd/compile: attach OVARLIVE nodes to OCALLxxx So we can insert theses OVARLIVE nodes right after OpStaticCall in SSA. This helps fixing issue that unsafe-uintptr arguments are not kept alive during return statement, or can be kept alive longer than expected. Fixes #24491 Change-Id: Ic04a5d1bbb5c90dcfae65bd95cdd1da393a66800 Reviewed-on: https://go-review.googlesource.com/c/go/+/254397 Run-TryBot: Cuong Manh Le TryBot-Result: Gobot Gobot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/order.go | 14 +++------ src/cmd/compile/internal/gc/ssa.go | 2 ++ src/cmd/compile/internal/gc/syntax.go | 4 +-- test/fixedbugs/issue24491.go | 45 ----------------------------- test/fixedbugs/issue24491a.go | 54 +++++++++++++++++++++++++++++++++++ test/fixedbugs/issue24491b.go | 46 +++++++++++++++++++++++++++++ 6 files changed, 107 insertions(+), 58 deletions(-) delete mode 100644 test/fixedbugs/issue24491.go create mode 100644 test/fixedbugs/issue24491a.go create mode 100644 test/fixedbugs/issue24491b.go diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index 412f073a8d..341f4ee66f 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -288,20 +288,13 @@ func (o *Order) popTemp(mark ordermarker) { o.temp = o.temp[:mark] } -// cleanTempNoPop emits VARKILL and if needed VARLIVE instructions -// to *out for each temporary above the mark on the temporary stack. +// cleanTempNoPop emits VARKILL instructions to *out +// for each temporary above the mark on the temporary stack. // It does not pop the temporaries from the stack. func (o *Order) cleanTempNoPop(mark ordermarker) []*Node { var out []*Node for i := len(o.temp) - 1; i >= int(mark); i-- { n := o.temp[i] - if n.Name.Keepalive() { - n.Name.SetKeepalive(false) - n.Name.SetAddrtaken(true) // ensure SSA keeps the n variable - live := nod(OVARLIVE, n, nil) - live = typecheck(live, ctxStmt) - out = append(out, live) - } kill := nod(OVARKILL, n, nil) kill = typecheck(kill, ctxStmt) out = append(out, kill) @@ -500,8 +493,9 @@ func (o *Order) call(n *Node) { // still alive when we pop the temp stack. if arg.Op == OCONVNOP && arg.Left.Type.IsUnsafePtr() { x := o.copyExpr(arg.Left, arg.Left.Type, false) - x.Name.SetKeepalive(true) arg.Left = x + x.Name.SetAddrtaken(true) // ensure SSA keeps the x variable + n.Nbody.Append(typecheck(nod(OVARLIVE, x, nil), ctxStmt)) n.SetNeedsWrapper(true) } } diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 89644cd3f2..3bdb5b0b9f 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4498,6 +4498,8 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { call.AuxInt = stksize // Call operations carry the argsize of the callee along with them } s.vars[&memVar] = call + // Insert OVARLIVE nodes + s.stmtList(n.Nbody) // Finish block for defers if k == callDefer || k == callDeferStack { diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index 5580f789c5..9592b7484c 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -374,7 +374,6 @@ const ( nameReadonly nameByval // is the variable captured by value or by reference nameNeedzero // if it contains pointers, needs to be zeroed on function entry - nameKeepalive // mark value live across unknown assembly call nameAutoTemp // is the variable a temporary (implies no dwarf info. reset if escapes to heap) nameUsed // for variable declared and not used error nameIsClosureVar // PAUTOHEAP closure pseudo-variable; original at n.Name.Defn @@ -391,7 +390,6 @@ func (n *Name) Captured() bool { return n.flags&nameCaptured != 0 } func (n *Name) Readonly() bool { return n.flags&nameReadonly != 0 } func (n *Name) Byval() bool { return n.flags&nameByval != 0 } func (n *Name) Needzero() bool { return n.flags&nameNeedzero != 0 } -func (n *Name) Keepalive() bool { return n.flags&nameKeepalive != 0 } func (n *Name) AutoTemp() bool { return n.flags&nameAutoTemp != 0 } func (n *Name) Used() bool { return n.flags&nameUsed != 0 } func (n *Name) IsClosureVar() bool { return n.flags&nameIsClosureVar != 0 } @@ -407,7 +405,6 @@ func (n *Name) SetCaptured(b bool) { n.flags.set(nameCaptured, b) } func (n *Name) SetReadonly(b bool) { n.flags.set(nameReadonly, b) } func (n *Name) SetByval(b bool) { n.flags.set(nameByval, b) } func (n *Name) SetNeedzero(b bool) { n.flags.set(nameNeedzero, b) } -func (n *Name) SetKeepalive(b bool) { n.flags.set(nameKeepalive, b) } func (n *Name) SetAutoTemp(b bool) { n.flags.set(nameAutoTemp, b) } func (n *Name) SetUsed(b bool) { n.flags.set(nameUsed, b) } func (n *Name) SetIsClosureVar(b bool) { n.flags.set(nameIsClosureVar, b) } @@ -707,6 +704,7 @@ const ( // Prior to walk, they are: Left(List), where List is all regular arguments. // After walk, List is a series of assignments to temporaries, // and Rlist is an updated set of arguments. + // Nbody is all OVARLIVE nodes that are attached to OCALLxxx. // TODO(josharian/khr): Use Ninit instead of List for the assignments to temporaries. See CL 114797. OCALLFUNC // Left(List/Rlist) (function call f(args)) OCALLMETH // Left(List/Rlist) (direct method call x.Method(args)) diff --git a/test/fixedbugs/issue24491.go b/test/fixedbugs/issue24491.go deleted file mode 100644 index 4703368793..0000000000 --- a/test/fixedbugs/issue24491.go +++ /dev/null @@ -1,45 +0,0 @@ -// run - -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This test makes sure unsafe-uintptr arguments are handled correctly. - -package main - -import ( - "runtime" - "unsafe" -) - -var done = make(chan bool, 1) - -func setup() unsafe.Pointer { - s := "ok" - runtime.SetFinalizer(&s, func(p *string) { *p = "FAIL" }) - return unsafe.Pointer(&s) -} - -//go:noinline -//go:uintptrescapes -func test(s string, p uintptr) { - runtime.GC() - if *(*string)(unsafe.Pointer(p)) != "ok" { - panic(s + " return unexpected result") - } - done <- true -} - -func main() { - test("normal", uintptr(setup())) - <-done - - go test("go", uintptr(setup())) - <-done - - func() { - defer test("defer", uintptr(setup())) - }() - <-done -} diff --git a/test/fixedbugs/issue24491a.go b/test/fixedbugs/issue24491a.go new file mode 100644 index 0000000000..148134d187 --- /dev/null +++ b/test/fixedbugs/issue24491a.go @@ -0,0 +1,54 @@ +// run + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This test makes sure unsafe-uintptr arguments are handled correctly. + +package main + +import ( + "runtime" + "unsafe" +) + +var done = make(chan bool, 1) + +func setup() unsafe.Pointer { + s := "ok" + runtime.SetFinalizer(&s, func(p *string) { *p = "FAIL" }) + return unsafe.Pointer(&s) +} + +//go:noinline +//go:uintptrescapes +func test(s string, p uintptr) int { + runtime.GC() + if *(*string)(unsafe.Pointer(p)) != "ok" { + panic(s + " return unexpected result") + } + done <- true + return 0 +} + +//go:noinline +func f() int { + return test("return", uintptr(setup())) +} + +func main() { + test("normal", uintptr(setup())) + <-done + + go test("go", uintptr(setup())) + <-done + + func() { + defer test("defer", uintptr(setup())) + }() + <-done + + f() + <-done +} diff --git a/test/fixedbugs/issue24491b.go b/test/fixedbugs/issue24491b.go new file mode 100644 index 0000000000..5f4a2f233e --- /dev/null +++ b/test/fixedbugs/issue24491b.go @@ -0,0 +1,46 @@ +// run + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This test makes sure unsafe-uintptr arguments are not +// kept alive longer than expected. + +package main + +import ( + "runtime" + "sync/atomic" + "unsafe" +) + +var done uint32 + +func setup() unsafe.Pointer { + s := "ok" + runtime.SetFinalizer(&s, func(p *string) { atomic.StoreUint32(&done, 1) }) + return unsafe.Pointer(&s) +} + +//go:noinline +//go:uintptrescapes +func before(p uintptr) int { + runtime.GC() + if atomic.LoadUint32(&done) != 0 { + panic("GC early") + } + return 0 +} + +func after() int { + runtime.GC() + if atomic.LoadUint32(&done) == 0 { + panic("GC late") + } + return 0 +} + +func main() { + _ = before(uintptr(setup())) + after() +} -- cgit v1.2.3-54-g00ecf From 5f1b12bfbeb04ca6dbecbf064f5e5a42d8ba4b5a Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Sat, 12 Sep 2020 07:19:22 +0700 Subject: cmd/compile: remove nodeNeedsWrapper flag CL 254397 attached OVARLIVE nodes to OCALLxxx nodes Nbody. The NeedsWrapper flag is now redundant with n.Nbody.Len() > 0 condition, so use that condition instead and remove the flag. Passes toolstash-check. Change-Id: Iebc3e674d3c0040a876ca4be05025943d2b4fb31 Reviewed-on: https://go-review.googlesource.com/c/go/+/254398 Run-TryBot: Cuong Manh Le TryBot-Result: Gobot Gobot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/order.go | 1 - src/cmd/compile/internal/gc/syntax.go | 41 +++++++++++------------------------ src/cmd/compile/internal/gc/walk.go | 7 ++++-- 3 files changed, 18 insertions(+), 31 deletions(-) diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index 341f4ee66f..75da154fe2 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -496,7 +496,6 @@ func (o *Order) call(n *Node) { arg.Left = x x.Name.SetAddrtaken(true) // ensure SSA keeps the x variable n.Nbody.Append(typecheck(nod(OVARLIVE, x, nil), ctxStmt)) - n.SetNeedsWrapper(true) } } diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index 9592b7484c..14d2710da4 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -141,20 +141,19 @@ const ( nodeInitorder, _ // tracks state during init1; two bits _, _ // second nodeInitorder bit _, nodeHasBreak - _, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only - _, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP; or ANDNOT lowered to OAND - _, nodeIsDDD // is the argument variadic - _, nodeDiag // already printed error about this - _, nodeColas // OAS resulting from := - _, nodeNonNil // guaranteed to be non-nil - _, nodeTransient // storage can be reused immediately after this statement - _, nodeBounded // bounds check unnecessary - _, nodeHasCall // expression contains a function call - _, nodeLikely // if statement condition likely - _, nodeHasVal // node.E contains a Val - _, nodeHasOpt // node.E contains an Opt - _, nodeEmbedded // ODCLFIELD embedded type - _, nodeNeedsWrapper // OCALLxxx node that needs to be wrapped + _, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only + _, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP; or ANDNOT lowered to OAND + _, nodeIsDDD // is the argument variadic + _, nodeDiag // already printed error about this + _, nodeColas // OAS resulting from := + _, nodeNonNil // guaranteed to be non-nil + _, nodeTransient // storage can be reused immediately after this statement + _, nodeBounded // bounds check unnecessary + _, nodeHasCall // expression contains a function call + _, nodeLikely // if statement condition likely + _, nodeHasVal // node.E contains a Val + _, nodeHasOpt // node.E contains an Opt + _, nodeEmbedded // ODCLFIELD embedded type ) func (n *Node) Class() Class { return Class(n.flags.get3(nodeClass)) } @@ -287,20 +286,6 @@ func (n *Node) SetIota(x int64) { n.Xoffset = x } -func (n *Node) NeedsWrapper() bool { - return n.flags&nodeNeedsWrapper != 0 -} - -// SetNeedsWrapper indicates that OCALLxxx node needs to be wrapped by a closure. -func (n *Node) SetNeedsWrapper(b bool) { - switch n.Op { - case OCALLFUNC, OCALLMETH, OCALLINTER: - default: - Fatalf("Node.SetNeedsWrapper %v", n.Op) - } - n.flags.set(nodeNeedsWrapper, b) -} - // mayBeShared reports whether n may occur in multiple places in the AST. // Extra care must be taken when mutating such a node. func (n *Node) mayBeShared() bool { diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 361de7e0f3..2d29366880 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -231,12 +231,15 @@ func walkstmt(n *Node) *Node { case OCOPY: n.Left = copyany(n.Left, &n.Ninit, true) - default: - if n.Left.NeedsWrapper() { + case OCALLFUNC, OCALLMETH, OCALLINTER: + if n.Left.Nbody.Len() > 0 { n.Left = wrapCall(n.Left, &n.Ninit) } else { n.Left = walkexpr(n.Left, &n.Ninit) } + + default: + n.Left = walkexpr(n.Left, &n.Ninit) } case OFOR, OFORUNTIL: -- cgit v1.2.3-54-g00ecf From afb5fca25a6f59e9045317727f6899a58471d5f0 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Sun, 13 Sep 2020 13:22:42 +0700 Subject: test: fix flaky test for issue24491 runtime.GC() doesn't guarantee the finalizer has run, so use a channel instead to make sure finalizer was run in call to "after()". Fixes #41361 Change-Id: I69c801e29aea49757ea72c52e8db13239de19ddc Reviewed-on: https://go-review.googlesource.com/c/go/+/254401 Run-TryBot: Cuong Manh Le TryBot-Result: Gobot Gobot Reviewed-by: Matthew Dempsky Trust: Cuong Manh Le --- test/fixedbugs/issue24491b.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/fixedbugs/issue24491b.go b/test/fixedbugs/issue24491b.go index 5f4a2f233e..142d798500 100644 --- a/test/fixedbugs/issue24491b.go +++ b/test/fixedbugs/issue24491b.go @@ -11,15 +11,14 @@ package main import ( "runtime" - "sync/atomic" "unsafe" ) -var done uint32 +var done = make(chan bool) func setup() unsafe.Pointer { s := "ok" - runtime.SetFinalizer(&s, func(p *string) { atomic.StoreUint32(&done, 1) }) + runtime.SetFinalizer(&s, func(p *string) { close(done) }) return unsafe.Pointer(&s) } @@ -27,17 +26,18 @@ func setup() unsafe.Pointer { //go:uintptrescapes func before(p uintptr) int { runtime.GC() - if atomic.LoadUint32(&done) != 0 { + select { + case <-done: panic("GC early") + default: } return 0 } func after() int { runtime.GC() - if atomic.LoadUint32(&done) == 0 { - panic("GC late") - } + runtime.GC() + <-done return 0 } -- cgit v1.2.3-54-g00ecf From 66e66e71132034aa620ffbae9008f951da0f9f27 Mon Sep 17 00:00:00 2001 From: Dominic Della Valle Date: Sun, 13 Sep 2020 17:03:48 +0000 Subject: make.bat: fix compare between GOROOT and srcdir paths, when either contains whitespace. CL 96455 brings CL 57753 to Windows However, a path comparison within it was left unquoted. If the Go source directory resides in a path containing whitespace, the interpreter will compare against the first portion of the path string, and treat the remainder as an expression. This patch amends that. For example, consider the path `C:\Users\Dominic Della Valle\Projects\Go\goroot\src` Issuing `make.bat` will print out `'Della' is not recognized as an internal or external command, operable program or batch file.` before proceeding. Change-Id: Ifcec159baeec940c29c61aa721c64c13c6fd8c14 GitHub-Last-Rev: 809ddbb4dbc80d834f8108ca44c2826016d78d1c GitHub-Pull-Request: golang/go#41319 Reviewed-on: https://go-review.googlesource.com/c/go/+/253898 Run-TryBot: Giovanni Bajo TryBot-Result: Gobot Gobot Reviewed-by: Giovanni Bajo Trust: Giovanni Bajo --- src/make.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/make.bat b/src/make.bat index 277a34d5d7..b4a8e70849 100644 --- a/src/make.bat +++ b/src/make.bat @@ -77,7 +77,7 @@ if not "x%GOROOT_BOOTSTRAP%"=="x" goto bootstrapset for /f "tokens=*" %%g in ('where go 2^>nul') do ( if "x%GOROOT_BOOTSTRAP%"=="x" ( for /f "tokens=*" %%i in ('%%g env GOROOT 2^>nul') do ( - if /I not %%i==%GOROOT_TEMP% ( + if /I not "%%i"=="%GOROOT_TEMP%" ( set GOROOT_BOOTSTRAP=%%i ) ) -- cgit v1.2.3-54-g00ecf From 4f5cd0c0331943c7ec72df3b827d972584f77833 Mon Sep 17 00:00:00 2001 From: Roberto Clapis Date: Wed, 26 Aug 2020 08:53:03 +0200 Subject: net/http/cgi,net/http/fcgi: add Content-Type detection This CL ensures that responses served via CGI and FastCGI have a Content-Type header based on the content of the response if not explicitly set by handlers. If the implementers of the handler did not explicitly specify a Content-Type both CGI implementations would default to "text/html", potentially causing cross-site scripting. Thanks to RedTeam Pentesting GmbH for reporting this. Fixes #40928 Fixes CVE-2020-24553 Change-Id: I82cfc396309b5ab2e8d6e9a87eda8ea7e3799473 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/823217 Reviewed-by: Russ Cox Reviewed-on: https://go-review.googlesource.com/c/go/+/252179 Run-TryBot: Filippo Valsorda TryBot-Result: Go Bot Reviewed-by: Katie Hockman --- src/net/http/cgi/child.go | 36 +++++++++++++++------- src/net/http/cgi/child_test.go | 58 ++++++++++++++++++++++++++++++++++++ src/net/http/cgi/integration_test.go | 53 +++++++++++++++++++++++++++++++- src/net/http/fcgi/child.go | 39 +++++++++++++++++------- src/net/http/fcgi/fcgi_test.go | 52 ++++++++++++++++++++++++++++++++ 5 files changed, 216 insertions(+), 22 deletions(-) diff --git a/src/net/http/cgi/child.go b/src/net/http/cgi/child.go index d7d813e68a..690986335c 100644 --- a/src/net/http/cgi/child.go +++ b/src/net/http/cgi/child.go @@ -166,10 +166,12 @@ func Serve(handler http.Handler) error { } type response struct { - req *http.Request - header http.Header - bufw *bufio.Writer - headerSent bool + req *http.Request + header http.Header + code int + wroteHeader bool + wroteCGIHeader bool + bufw *bufio.Writer } func (r *response) Flush() { @@ -181,26 +183,38 @@ func (r *response) Header() http.Header { } func (r *response) Write(p []byte) (n int, err error) { - if !r.headerSent { + if !r.wroteHeader { r.WriteHeader(http.StatusOK) } + if !r.wroteCGIHeader { + r.writeCGIHeader(p) + } return r.bufw.Write(p) } func (r *response) WriteHeader(code int) { - if r.headerSent { + if r.wroteHeader { // Note: explicitly using Stderr, as Stdout is our HTTP output. fmt.Fprintf(os.Stderr, "CGI attempted to write header twice on request for %s", r.req.URL) return } - r.headerSent = true - fmt.Fprintf(r.bufw, "Status: %d %s\r\n", code, http.StatusText(code)) + r.wroteHeader = true + r.code = code +} - // Set a default Content-Type +// writeCGIHeader finalizes the header sent to the client and writes it to the output. +// p is not written by writeHeader, but is the first chunk of the body +// that will be written. It is sniffed for a Content-Type if none is +// set explicitly. +func (r *response) writeCGIHeader(p []byte) { + if r.wroteCGIHeader { + return + } + r.wroteCGIHeader = true + fmt.Fprintf(r.bufw, "Status: %d %s\r\n", r.code, http.StatusText(r.code)) if _, hasType := r.header["Content-Type"]; !hasType { - r.header.Add("Content-Type", "text/html; charset=utf-8") + r.header.Set("Content-Type", http.DetectContentType(p)) } - r.header.Write(r.bufw) r.bufw.WriteString("\r\n") r.bufw.Flush() diff --git a/src/net/http/cgi/child_test.go b/src/net/http/cgi/child_test.go index 14e0af475f..18cf789bd5 100644 --- a/src/net/http/cgi/child_test.go +++ b/src/net/http/cgi/child_test.go @@ -7,6 +7,11 @@ package cgi import ( + "bufio" + "bytes" + "net/http" + "net/http/httptest" + "strings" "testing" ) @@ -148,3 +153,56 @@ func TestRequestWithoutRemotePort(t *testing.T) { t.Errorf("RemoteAddr: got %q; want %q", g, e) } } + +func TestResponse(t *testing.T) { + var tests = []struct { + name string + body string + wantCT string + }{ + { + name: "no body", + wantCT: "text/plain; charset=utf-8", + }, + { + name: "html", + body: "test pageThis is a body", + wantCT: "text/html; charset=utf-8", + }, + { + name: "text", + body: strings.Repeat("gopher", 86), + wantCT: "text/plain; charset=utf-8", + }, + { + name: "jpg", + body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024), + wantCT: "image/jpeg", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var buf bytes.Buffer + resp := response{ + req: httptest.NewRequest("GET", "/", nil), + header: http.Header{}, + bufw: bufio.NewWriter(&buf), + } + n, err := resp.Write([]byte(tt.body)) + if err != nil { + t.Errorf("Write: unexpected %v", err) + } + if want := len(tt.body); n != want { + t.Errorf("reported short Write: got %v want %v", n, want) + } + resp.writeCGIHeader(nil) + resp.Flush() + if got := resp.Header().Get("Content-Type"); got != tt.wantCT { + t.Errorf("wrong content-type: got %q, want %q", got, tt.wantCT) + } + if !bytes.HasSuffix(buf.Bytes(), []byte(tt.body)) { + t.Errorf("body was not correctly written") + } + }) + } +} diff --git a/src/net/http/cgi/integration_test.go b/src/net/http/cgi/integration_test.go index eaa090f6fe..76cbca8e60 100644 --- a/src/net/http/cgi/integration_test.go +++ b/src/net/http/cgi/integration_test.go @@ -16,7 +16,9 @@ import ( "io" "net/http" "net/http/httptest" + "net/url" "os" + "strings" "testing" "time" ) @@ -52,7 +54,7 @@ func TestHostingOurselves(t *testing.T) { } replay := runCgiTest(t, h, "GET /test.go?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap) - if expected, got := "text/html; charset=utf-8", replay.Header().Get("Content-Type"); got != expected { + if expected, got := "text/plain; charset=utf-8", replay.Header().Get("Content-Type"); got != expected { t.Errorf("got a Content-Type of %q; expected %q", got, expected) } if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected { @@ -169,6 +171,51 @@ func TestNilRequestBody(t *testing.T) { _ = runCgiTest(t, h, "POST /test.go?nil-request-body=1 HTTP/1.0\nHost: example.com\nContent-Length: 0\n\n", expectedMap) } +func TestChildContentType(t *testing.T) { + testenv.MustHaveExec(t) + + h := &Handler{ + Path: os.Args[0], + Root: "/test.go", + Args: []string{"-test.run=TestBeChildCGIProcess"}, + } + var tests = []struct { + name string + body string + wantCT string + }{ + { + name: "no body", + wantCT: "text/plain; charset=utf-8", + }, + { + name: "html", + body: "test pageThis is a body", + wantCT: "text/html; charset=utf-8", + }, + { + name: "text", + body: strings.Repeat("gopher", 86), + wantCT: "text/plain; charset=utf-8", + }, + { + name: "jpg", + body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024), + wantCT: "image/jpeg", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + expectedMap := map[string]string{"_body": tt.body} + req := fmt.Sprintf("GET /test.go?exact-body=%s HTTP/1.0\nHost: example.com\n\n", url.QueryEscape(tt.body)) + replay := runCgiTest(t, h, req, expectedMap) + if got := replay.Header().Get("Content-Type"); got != tt.wantCT { + t.Errorf("got a Content-Type of %q; expected it to start with %q", got, tt.wantCT) + } + }) + } +} + // golang.org/issue/7198 func Test500WithNoHeaders(t *testing.T) { want500Test(t, "/immediate-disconnect") } func Test500WithNoContentType(t *testing.T) { want500Test(t, "/no-content-type") } @@ -224,6 +271,10 @@ func TestBeChildCGIProcess(t *testing.T) { if req.FormValue("no-body") == "1" { return } + if eb, ok := req.Form["exact-body"]; ok { + io.WriteString(rw, eb[0]) + return + } if req.FormValue("write-forever") == "1" { io.Copy(rw, neverEnding('a')) for { diff --git a/src/net/http/fcgi/child.go b/src/net/http/fcgi/child.go index 0e91042543..34761f32ee 100644 --- a/src/net/http/fcgi/child.go +++ b/src/net/http/fcgi/child.go @@ -74,10 +74,12 @@ func (r *request) parseParams() { // response implements http.ResponseWriter. type response struct { - req *request - header http.Header - w *bufWriter - wroteHeader bool + req *request + header http.Header + code int + wroteHeader bool + wroteCGIHeader bool + w *bufWriter } func newResponse(c *child, req *request) *response { @@ -92,11 +94,14 @@ func (r *response) Header() http.Header { return r.header } -func (r *response) Write(data []byte) (int, error) { +func (r *response) Write(p []byte) (n int, err error) { if !r.wroteHeader { r.WriteHeader(http.StatusOK) } - return r.w.Write(data) + if !r.wroteCGIHeader { + r.writeCGIHeader(p) + } + return r.w.Write(p) } func (r *response) WriteHeader(code int) { @@ -104,22 +109,34 @@ func (r *response) WriteHeader(code int) { return } r.wroteHeader = true + r.code = code if code == http.StatusNotModified { // Must not have body. r.header.Del("Content-Type") r.header.Del("Content-Length") r.header.Del("Transfer-Encoding") - } else if r.header.Get("Content-Type") == "" { - r.header.Set("Content-Type", "text/html; charset=utf-8") } - if r.header.Get("Date") == "" { r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat)) } +} - fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code)) +// writeCGIHeader finalizes the header sent to the client and writes it to the output. +// p is not written by writeHeader, but is the first chunk of the body +// that will be written. It is sniffed for a Content-Type if none is +// set explicitly. +func (r *response) writeCGIHeader(p []byte) { + if r.wroteCGIHeader { + return + } + r.wroteCGIHeader = true + fmt.Fprintf(r.w, "Status: %d %s\r\n", r.code, http.StatusText(r.code)) + if _, hasType := r.header["Content-Type"]; r.code != http.StatusNotModified && !hasType { + r.header.Set("Content-Type", http.DetectContentType(p)) + } r.header.Write(r.w) r.w.WriteString("\r\n") + r.w.Flush() } func (r *response) Flush() { @@ -293,6 +310,8 @@ func (c *child) serveRequest(req *request, body io.ReadCloser) { httpReq = httpReq.WithContext(envVarCtx) c.handler.ServeHTTP(r, httpReq) } + // Make sure we serve something even if nothing was written to r + r.Write(nil) r.Close() c.mu.Lock() delete(c.requests, req.reqId) diff --git a/src/net/http/fcgi/fcgi_test.go b/src/net/http/fcgi/fcgi_test.go index e9d2b34023..4a27a12c35 100644 --- a/src/net/http/fcgi/fcgi_test.go +++ b/src/net/http/fcgi/fcgi_test.go @@ -10,6 +10,7 @@ import ( "io" "io/ioutil" "net/http" + "strings" "testing" ) @@ -344,3 +345,54 @@ func TestChildServeReadsEnvVars(t *testing.T) { <-done } } + +func TestResponseWriterSniffsContentType(t *testing.T) { + var tests = []struct { + name string + body string + wantCT string + }{ + { + name: "no body", + wantCT: "text/plain; charset=utf-8", + }, + { + name: "html", + body: "test pageThis is a body", + wantCT: "text/html; charset=utf-8", + }, + { + name: "text", + body: strings.Repeat("gopher", 86), + wantCT: "text/plain; charset=utf-8", + }, + { + name: "jpg", + body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024), + wantCT: "image/jpeg", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + input := make([]byte, len(streamFullRequestStdin)) + copy(input, streamFullRequestStdin) + rc := nopWriteCloser{bytes.NewBuffer(input)} + done := make(chan bool) + var resp *response + c := newChild(rc, http.HandlerFunc(func( + w http.ResponseWriter, + r *http.Request, + ) { + io.WriteString(w, tt.body) + resp = w.(*response) + done <- true + })) + defer c.cleanUp() + go c.serve() + <-done + if got := resp.Header().Get("Content-Type"); got != tt.wantCT { + t.Errorf("got a Content-Type of %q; expected it to start with %q", got, tt.wantCT) + } + }) + } +} -- cgit v1.2.3-54-g00ecf From 86dbeefe1f2770daad3c8d8b46a8b7f21b2c69e1 Mon Sep 17 00:00:00 2001 From: Clément Chigot Date: Mon, 14 Sep 2020 13:06:40 +0200 Subject: syscall: fix fsync for read-only files on aix AIX fsync syscall doesn't work on read-only files. Using fsync_range instead allows syscall.Fsync to work on any files. Fixes #41372 Change-Id: I66d33e847875496af53da60828c1bddf6c2b76b7 Reviewed-on: https://go-review.googlesource.com/c/go/+/254657 Run-TryBot: Tobias Klauser TryBot-Result: Go Bot Reviewed-by: Tobias Klauser Reviewed-by: Ian Lance Taylor --- src/syscall/syscall_aix.go | 6 +++++- src/syscall/zsyscall_aix_ppc64.go | 26 +++++++++++++------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/syscall/syscall_aix.go b/src/syscall/syscall_aix.go index 8bb5fa9ead..8837dd5a7f 100644 --- a/src/syscall/syscall_aix.go +++ b/src/syscall/syscall_aix.go @@ -214,6 +214,11 @@ func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, return } +//sys fsyncRange(fd int, how int, start int64, length int64) (err error) = fsync_range +func Fsync(fd int) error { + return fsyncRange(fd, O_SYNC, 0, 0) +} + /* * Socket */ @@ -600,7 +605,6 @@ func PtraceDetach(pid int) (err error) { return ptrace64(PT_DETACH, int64(pid), //sys Fstat(fd int, stat *Stat_t) (err error) //sys Fstatfs(fd int, buf *Statfs_t) (err error) //sys Ftruncate(fd int, length int64) (err error) -//sys Fsync(fd int) (err error) //sysnb Getgid() (gid int) //sysnb Getpid() (pid int) //sys Geteuid() (euid int) diff --git a/src/syscall/zsyscall_aix_ppc64.go b/src/syscall/zsyscall_aix_ppc64.go index 384fead4d2..20625c1a3e 100644 --- a/src/syscall/zsyscall_aix_ppc64.go +++ b/src/syscall/zsyscall_aix_ppc64.go @@ -19,6 +19,7 @@ import "unsafe" //go:cgo_import_dynamic libc_setgroups setgroups "libc.a/shr_64.o" //go:cgo_import_dynamic libc_getdirent getdirent "libc.a/shr_64.o" //go:cgo_import_dynamic libc_wait4 wait4 "libc.a/shr_64.o" +//go:cgo_import_dynamic libc_fsync_range fsync_range "libc.a/shr_64.o" //go:cgo_import_dynamic libc_bind bind "libc.a/shr_64.o" //go:cgo_import_dynamic libc_connect connect "libc.a/shr_64.o" //go:cgo_import_dynamic libc_Getkerninfo getkerninfo "libc.a/shr_64.o" @@ -54,7 +55,6 @@ import "unsafe" //go:cgo_import_dynamic libc_Fstat fstat "libc.a/shr_64.o" //go:cgo_import_dynamic libc_Fstatfs fstatfs "libc.a/shr_64.o" //go:cgo_import_dynamic libc_Ftruncate ftruncate "libc.a/shr_64.o" -//go:cgo_import_dynamic libc_Fsync fsync "libc.a/shr_64.o" //go:cgo_import_dynamic libc_Getgid getgid "libc.a/shr_64.o" //go:cgo_import_dynamic libc_Getpid getpid "libc.a/shr_64.o" //go:cgo_import_dynamic libc_Geteuid geteuid "libc.a/shr_64.o" @@ -111,6 +111,7 @@ import "unsafe" //go:linkname libc_setgroups libc_setgroups //go:linkname libc_getdirent libc_getdirent //go:linkname libc_wait4 libc_wait4 +//go:linkname libc_fsync_range libc_fsync_range //go:linkname libc_bind libc_bind //go:linkname libc_connect libc_connect //go:linkname libc_Getkerninfo libc_Getkerninfo @@ -146,7 +147,6 @@ import "unsafe" //go:linkname libc_Fstat libc_Fstat //go:linkname libc_Fstatfs libc_Fstatfs //go:linkname libc_Ftruncate libc_Ftruncate -//go:linkname libc_Fsync libc_Fsync //go:linkname libc_Getgid libc_Getgid //go:linkname libc_Getpid libc_Getpid //go:linkname libc_Geteuid libc_Geteuid @@ -206,6 +206,7 @@ var ( libc_setgroups, libc_getdirent, libc_wait4, + libc_fsync_range, libc_bind, libc_connect, libc_Getkerninfo, @@ -241,7 +242,6 @@ var ( libc_Fstat, libc_Fstatfs, libc_Ftruncate, - libc_Fsync, libc_Getgid, libc_Getpid, libc_Geteuid, @@ -442,6 +442,16 @@ func wait4(pid _Pid_t, status *_C_int, options int, rusage *Rusage) (wpid _Pid_t // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fsyncRange(fd int, how int, start int64, length int64) (err error) { + _, _, e1 := syscall6(uintptr(unsafe.Pointer(&libc_fsync_range)), 4, uintptr(fd), uintptr(how), uintptr(start), uintptr(length), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) { _, _, e1 := syscall6(uintptr(unsafe.Pointer(&libc_bind)), 3, uintptr(s), uintptr(addr), uintptr(addrlen), 0, 0, 0) if e1 != 0 { @@ -854,16 +864,6 @@ func Ftruncate(fd int, length int64) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Fsync(fd int) (err error) { - _, _, e1 := syscall6(uintptr(unsafe.Pointer(&libc_Fsync)), 1, uintptr(fd), 0, 0, 0, 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Getgid() (gid int) { r0, _, _ := rawSyscall6(uintptr(unsafe.Pointer(&libc_Getgid)), 0, 0, 0, 0, 0, 0, 0) gid = int(r0) -- cgit v1.2.3-54-g00ecf From c0c396bd6ad2aea40f7f302711c8b89e20feb371 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Fri, 11 Sep 2020 20:09:40 -0400 Subject: misc/ios: quote paths The paths may contain spaces. Quote them. Change-Id: I1f67085a1e7c40f60282c2fea7104fb44a01e310 Reviewed-on: https://go-review.googlesource.com/c/go/+/254739 Run-TryBot: Cherry Zhang TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor Reviewed-by: Dmitri Shuralyov Trust: Dmitri Shuralyov --- misc/ios/clangwrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/ios/clangwrap.sh b/misc/ios/clangwrap.sh index 5fdbb6db4a..1d6dee28a8 100755 --- a/misc/ios/clangwrap.sh +++ b/misc/ios/clangwrap.sh @@ -15,4 +15,4 @@ else exit 1 fi -exec $CLANG -arch $CLANGARCH -isysroot $SDK_PATH -mios-version-min=10.0 "$@" +exec "$CLANG" -arch $CLANGARCH -isysroot "$SDK_PATH" -mios-version-min=10.0 "$@" -- cgit v1.2.3-54-g00ecf From 14c7caae5074fdf0d97a3ad995e20c63e4065cbf Mon Sep 17 00:00:00 2001 From: Martin Möhrmann Date: Mon, 13 Jul 2020 18:12:20 +0200 Subject: runtime: add 24 byte allocation size class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This CL introduces a 24 byte allocation size class which fits 3 pointers on 64 bit and 6 pointers on 32 bit architectures. Notably this new size class fits a slice header on 64 bit architectures exactly while previously a 32 byte size class would have been used for allocating a slice header on the heap. The main complexity added with this CL is that heapBitsSetType needs to handle objects that aren't 16-byte aligned but contain more than a single pointer on 64-bit architectures. Due to having a non 16 byte aligned size class on 32 bit a h.shift of 2 is now possible which means a heap bitmap byte might only be partially written. Due to this already having been possible on 64 bit before the heap bitmap code only needed minor adjustments for 32 bit doublecheck code paths. Note that this CL changes the slice capacity allocated by append for slice growth to a target capacity of 17 to 24 bytes. On 64 bit architectures the capacity of the slice returned by append([]byte{}, make([]byte, 24)...)) is 32 bytes before and 24 bytes after this CL. Depending on allocation patterns of the specific Go program this can increase the number of total alloctions as subsequent appends to the slice can trigger slice growth earlier than before. On the other side if the slice is never appended to again above its capacity this will lower heap usage by 8 bytes. This CL changes the set of size classes reported in the runtime.MemStats.BySize array due to it being limited to a total of 61 size classes. The new 24 byte size class is now included and the 20480 byte size class is not included anymore. Fixes #8885 name old time/op new time/op delta Template 196ms ± 3% 194ms ± 2% ~ (p=0.247 n=10+10) Unicode 85.6ms ±16% 88.1ms ± 1% ~ (p=0.165 n=10+10) GoTypes 673ms ± 2% 668ms ± 2% ~ (p=0.258 n=9+9) Compiler 3.14s ± 6% 3.08s ± 1% ~ (p=0.243 n=10+9) SSA 6.82s ± 1% 6.76s ± 1% -0.87% (p=0.006 n=9+10) Flate 128ms ± 7% 127ms ± 3% ~ (p=0.739 n=10+10) GoParser 154ms ± 3% 153ms ± 4% ~ (p=0.730 n=9+9) Reflect 404ms ± 1% 412ms ± 4% +1.99% (p=0.022 n=9+10) Tar 172ms ± 4% 170ms ± 4% ~ (p=0.065 n=10+9) XML 231ms ± 4% 230ms ± 3% ~ (p=0.912 n=10+10) LinkCompiler 341ms ± 1% 339ms ± 1% ~ (p=0.243 n=9+10) ExternalLinkCompiler 1.72s ± 1% 1.72s ± 1% ~ (p=0.661 n=9+10) LinkWithoutDebugCompiler 221ms ± 2% 221ms ± 2% ~ (p=0.529 n=10+10) StdCmd 18.4s ± 3% 18.2s ± 1% ~ (p=0.515 n=10+8) name old user-time/op new user-time/op delta Template 238ms ± 4% 243ms ± 6% ~ (p=0.661 n=9+10) Unicode 116ms ± 6% 113ms ± 3% -3.37% (p=0.035 n=9+10) GoTypes 854ms ± 2% 848ms ± 2% ~ (p=0.604 n=9+10) Compiler 4.10s ± 1% 4.11s ± 1% ~ (p=0.481 n=8+9) SSA 9.49s ± 1% 9.41s ± 1% -0.92% (p=0.001 n=9+10) Flate 149ms ± 6% 151ms ± 7% ~ (p=0.481 n=10+10) GoParser 189ms ± 2% 190ms ± 2% ~ (p=0.497 n=9+10) Reflect 511ms ± 2% 508ms ± 2% ~ (p=0.211 n=9+10) Tar 215ms ± 4% 212ms ± 3% ~ (p=0.105 n=10+10) XML 288ms ± 2% 288ms ± 2% ~ (p=0.971 n=10+10) LinkCompiler 559ms ± 4% 557ms ± 1% ~ (p=0.968 n=9+10) ExternalLinkCompiler 1.78s ± 1% 1.77s ± 1% ~ (p=0.055 n=8+10) LinkWithoutDebugCompiler 245ms ± 3% 245ms ± 2% ~ (p=0.684 n=10+10) name old alloc/op new alloc/op delta Template 34.8MB ± 0% 34.4MB ± 0% -0.95% (p=0.000 n=9+10) Unicode 28.6MB ± 0% 28.3MB ± 0% -0.95% (p=0.000 n=10+10) GoTypes 115MB ± 0% 114MB ± 0% -1.02% (p=0.000 n=10+9) Compiler 554MB ± 0% 549MB ± 0% -0.86% (p=0.000 n=9+10) SSA 1.28GB ± 0% 1.27GB ± 0% -0.83% (p=0.000 n=10+10) Flate 21.8MB ± 0% 21.6MB ± 0% -0.87% (p=0.000 n=8+10) GoParser 26.7MB ± 0% 26.4MB ± 0% -0.97% (p=0.000 n=10+9) Reflect 75.0MB ± 0% 74.1MB ± 0% -1.18% (p=0.000 n=10+10) Tar 32.6MB ± 0% 32.3MB ± 0% -0.94% (p=0.000 n=10+7) XML 41.5MB ± 0% 41.2MB ± 0% -0.90% (p=0.000 n=10+8) LinkCompiler 105MB ± 0% 104MB ± 0% -0.94% (p=0.000 n=10+10) ExternalLinkCompiler 153MB ± 0% 152MB ± 0% -0.69% (p=0.000 n=10+10) LinkWithoutDebugCompiler 63.7MB ± 0% 63.6MB ± 0% -0.13% (p=0.000 n=10+10) name old allocs/op new allocs/op delta Template 336k ± 0% 336k ± 0% +0.02% (p=0.002 n=10+10) Unicode 332k ± 0% 332k ± 0% ~ (p=0.447 n=10+10) GoTypes 1.16M ± 0% 1.16M ± 0% +0.01% (p=0.001 n=10+10) Compiler 4.92M ± 0% 4.92M ± 0% +0.01% (p=0.000 n=10+10) SSA 11.9M ± 0% 11.9M ± 0% +0.02% (p=0.000 n=9+10) Flate 214k ± 0% 214k ± 0% +0.02% (p=0.032 n=10+8) GoParser 270k ± 0% 270k ± 0% +0.02% (p=0.004 n=10+9) Reflect 877k ± 0% 877k ± 0% +0.01% (p=0.000 n=10+10) Tar 313k ± 0% 313k ± 0% ~ (p=0.075 n=9+10) XML 387k ± 0% 387k ± 0% +0.02% (p=0.007 n=10+10) LinkCompiler 455k ± 0% 456k ± 0% +0.08% (p=0.000 n=10+9) ExternalLinkCompiler 670k ± 0% 671k ± 0% +0.06% (p=0.000 n=10+10) LinkWithoutDebugCompiler 113k ± 0% 113k ± 0% ~ (p=0.149 n=10+10) name old maxRSS/op new maxRSS/op delta Template 34.1M ± 1% 34.1M ± 1% ~ (p=0.853 n=10+10) Unicode 35.1M ± 1% 34.6M ± 1% -1.43% (p=0.000 n=10+10) GoTypes 72.8M ± 3% 73.3M ± 2% ~ (p=0.724 n=10+10) Compiler 288M ± 3% 295M ± 4% ~ (p=0.393 n=10+10) SSA 630M ± 1% 622M ± 1% -1.18% (p=0.001 n=10+10) Flate 26.0M ± 1% 26.2M ± 2% ~ (p=0.493 n=10+10) GoParser 28.6M ± 1% 28.5M ± 2% ~ (p=0.256 n=10+10) Reflect 55.5M ± 2% 55.4M ± 1% ~ (p=0.436 n=10+10) Tar 33.0M ± 1% 32.8M ± 2% ~ (p=0.075 n=10+10) XML 38.7M ± 1% 39.0M ± 1% ~ (p=0.053 n=9+10) LinkCompiler 164M ± 1% 164M ± 1% -0.27% (p=0.029 n=10+10) ExternalLinkCompiler 174M ± 0% 173M ± 0% -0.33% (p=0.002 n=9+10) LinkWithoutDebugCompiler 137M ± 0% 136M ± 2% ~ (p=0.825 n=9+10) Change-Id: I9ecf2a10024513abef8fbfbe519e44e0b29b6167 Reviewed-on: https://go-review.googlesource.com/c/go/+/242258 Trust: Martin Möhrmann Trust: Michael Knyszek Run-TryBot: Martin Möhrmann TryBot-Result: Go Bot Reviewed-by: Michael Knyszek Reviewed-by: Keith Randall --- src/runtime/mbitmap.go | 112 +++++++++++++++++++++++++++++----- src/runtime/mksizeclasses.go | 6 +- src/runtime/mstats.go | 4 ++ src/runtime/sizeclasses.go | 141 ++++++++++++++++++++++--------------------- 4 files changed, 174 insertions(+), 89 deletions(-) diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index 8de44c14b9..51c3625c3d 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -30,10 +30,9 @@ // indicates scanning can ignore the rest of the allocation. // // The 2-bit entries are split when written into the byte, so that the top half -// of the byte contains 4 high bits and the bottom half contains 4 low (pointer) -// bits. -// This form allows a copy from the 1-bit to the 4-bit form to keep the -// pointer bits contiguous, instead of having to space them out. +// of the byte contains 4 high (scan) bits and the bottom half contains 4 low +// (pointer) bits. This form allows a copy from the 1-bit to the 4-bit form to +// keep the pointer bits contiguous, instead of having to space them out. // // The code makes use of the fact that the zero value for a heap // bitmap means scalar/dead. This property must be preserved when @@ -816,6 +815,12 @@ func (s *mspan) countAlloc() int { func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { const doubleCheck = false // slow but helpful; enable to test modifications to this code + const ( + mask1 = bitPointer | bitScan // 00010001 + mask2 = bitPointer | bitScan | mask1<> 1 + + // For h.shift > 1 heap bits cross a byte boundary and need to be written part + // to h.bitp and part to the next h.bitp. + switch h.shift { + case 0: + *h.bitp &^= mask3 << 0 + *h.bitp |= hb << 0 + case 1: + *h.bitp &^= mask3 << 1 + *h.bitp |= hb << 1 + case 2: + *h.bitp &^= mask2 << 2 + *h.bitp |= (hb & mask2) << 2 + // Two words written to the first byte. + // Advance two words to get to the next byte. + h = h.next().next() + *h.bitp &^= mask1 + *h.bitp |= (hb >> 2) & mask1 + case 3: + *h.bitp &^= mask1 << 3 + *h.bitp |= (hb & mask1) << 3 + // One word written to the first byte. + // Advance one word to get to the next byte. + h = h.next() + *h.bitp &^= mask2 + *h.bitp |= (hb >> 1) & mask2 + } + return } // Copy from 1-bit ptrmask into 2-bit bitmap. @@ -1079,7 +1149,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { // word must be set to scan since there are pointers // somewhere in the object. // In all following words, we set the scan/dead - // appropriately to indicate that the object contains + // appropriately to indicate that the object continues // to the next 2-bit entry in the bitmap. // // We set four bits at a time here, but if the object @@ -1095,12 +1165,22 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { b >>= 4 nb -= 4 - case sys.PtrSize == 8 && h.shift == 2: + case h.shift == 2: // Ptrmask and heap bitmap are misaligned. + // + // On 32 bit architectures only the 6-word object that corresponds + // to a 24 bytes size class can start with h.shift of 2 here since + // all other non 16 byte aligned size classes have been handled by + // special code paths at the beginning of heapBitsSetType on 32 bit. + // + // Many size classes are only 16 byte aligned. On 64 bit architectures + // this results in a heap bitmap position starting with a h.shift of 2. + // // The bits for the first two words are in a byte shared // with another object, so we must be careful with the bits // already there. - // We took care of 1-word and 2-word objects above, + // + // We took care of 1-word, 2-word, and 3-word objects above, // so this is at least a 6-word object. hb = (b & (bitPointer | bitPointer<= nw { - // We know that there is more data, because we handled 2-word objects above. + // We know that there is more data, because we handled 2-word and 3-word objects above. // This must be at least a 6-word object. If we're out of pointer words, // mark no scan in next bitmap byte and finish. hb = 0 @@ -1248,12 +1328,12 @@ Phase4: // Handle the first byte specially if it's shared. See // Phase 1 for why this is the only special case we need. if doubleCheck { - if !(h.shift == 0 || (sys.PtrSize == 8 && h.shift == 2)) { + if !(h.shift == 0 || h.shift == 2) { print("x=", x, " size=", size, " cnw=", h.shift, "\n") throw("bad start shift") } } - if sys.PtrSize == 8 && h.shift == 2 { + if h.shift == 2 { *h.bitp = *h.bitp&^((bitPointer|bitScan|(bitPointer|bitScan)<= 128 { align = size / 8 - } else if size >= 16 { - align = 16 // required for x86 SSE instructions, if we want to use them + } else if size >= 32 { + align = 16 // heap bitmaps assume 16 byte alignment for allocations >= 32 bytes. } } if !powerOfTwo(align) { @@ -157,7 +157,7 @@ func makeClasses() []class { } } - if len(classes) != 67 { + if len(classes) != 68 { panic("number of size classes has changed") } diff --git a/src/runtime/mstats.go b/src/runtime/mstats.go index 6a8a34d1ed..b95b332134 100644 --- a/src/runtime/mstats.go +++ b/src/runtime/mstats.go @@ -78,6 +78,10 @@ type mstats struct { nfree uint64 } + // Add an uint32 for even number of size classes to align below fields + // to 64 bits for atomic operations on 32 bit platforms. + _ [1 - _NumSizeClasses%2]uint32 + // Statistics below here are not exported to MemStats directly. last_gc_nanotime uint64 // last gc (monotonic time) diff --git a/src/runtime/sizeclasses.go b/src/runtime/sizeclasses.go index 9c1b44fe0b..c5521ce1bd 100644 --- a/src/runtime/sizeclasses.go +++ b/src/runtime/sizeclasses.go @@ -6,82 +6,83 @@ package runtime // class bytes/obj bytes/span objects tail waste max waste // 1 8 8192 1024 0 87.50% // 2 16 8192 512 0 43.75% -// 3 32 8192 256 0 46.88% -// 4 48 8192 170 32 31.52% -// 5 64 8192 128 0 23.44% -// 6 80 8192 102 32 19.07% -// 7 96 8192 85 32 15.95% -// 8 112 8192 73 16 13.56% -// 9 128 8192 64 0 11.72% -// 10 144 8192 56 128 11.82% -// 11 160 8192 51 32 9.73% -// 12 176 8192 46 96 9.59% -// 13 192 8192 42 128 9.25% -// 14 208 8192 39 80 8.12% -// 15 224 8192 36 128 8.15% -// 16 240 8192 34 32 6.62% -// 17 256 8192 32 0 5.86% -// 18 288 8192 28 128 12.16% -// 19 320 8192 25 192 11.80% -// 20 352 8192 23 96 9.88% -// 21 384 8192 21 128 9.51% -// 22 416 8192 19 288 10.71% -// 23 448 8192 18 128 8.37% -// 24 480 8192 17 32 6.82% -// 25 512 8192 16 0 6.05% -// 26 576 8192 14 128 12.33% -// 27 640 8192 12 512 15.48% -// 28 704 8192 11 448 13.93% -// 29 768 8192 10 512 13.94% -// 30 896 8192 9 128 15.52% -// 31 1024 8192 8 0 12.40% -// 32 1152 8192 7 128 12.41% -// 33 1280 8192 6 512 15.55% -// 34 1408 16384 11 896 14.00% -// 35 1536 8192 5 512 14.00% -// 36 1792 16384 9 256 15.57% -// 37 2048 8192 4 0 12.45% -// 38 2304 16384 7 256 12.46% -// 39 2688 8192 3 128 15.59% -// 40 3072 24576 8 0 12.47% -// 41 3200 16384 5 384 6.22% -// 42 3456 24576 7 384 8.83% -// 43 4096 8192 2 0 15.60% -// 44 4864 24576 5 256 16.65% -// 45 5376 16384 3 256 10.92% -// 46 6144 24576 4 0 12.48% -// 47 6528 32768 5 128 6.23% -// 48 6784 40960 6 256 4.36% -// 49 6912 49152 7 768 3.37% -// 50 8192 8192 1 0 15.61% -// 51 9472 57344 6 512 14.28% -// 52 9728 49152 5 512 3.64% -// 53 10240 40960 4 0 4.99% -// 54 10880 32768 3 128 6.24% -// 55 12288 24576 2 0 11.45% -// 56 13568 40960 3 256 9.99% -// 57 14336 57344 4 0 5.35% -// 58 16384 16384 1 0 12.49% -// 59 18432 73728 4 0 11.11% -// 60 19072 57344 3 128 3.57% -// 61 20480 40960 2 0 6.87% -// 62 21760 65536 3 256 6.25% -// 63 24576 24576 1 0 11.45% -// 64 27264 81920 3 128 10.00% -// 65 28672 57344 2 0 4.91% -// 66 32768 32768 1 0 12.50% +// 3 24 8192 341 8 29.24% +// 4 32 8192 256 0 21.88% +// 5 48 8192 170 32 31.52% +// 6 64 8192 128 0 23.44% +// 7 80 8192 102 32 19.07% +// 8 96 8192 85 32 15.95% +// 9 112 8192 73 16 13.56% +// 10 128 8192 64 0 11.72% +// 11 144 8192 56 128 11.82% +// 12 160 8192 51 32 9.73% +// 13 176 8192 46 96 9.59% +// 14 192 8192 42 128 9.25% +// 15 208 8192 39 80 8.12% +// 16 224 8192 36 128 8.15% +// 17 240 8192 34 32 6.62% +// 18 256 8192 32 0 5.86% +// 19 288 8192 28 128 12.16% +// 20 320 8192 25 192 11.80% +// 21 352 8192 23 96 9.88% +// 22 384 8192 21 128 9.51% +// 23 416 8192 19 288 10.71% +// 24 448 8192 18 128 8.37% +// 25 480 8192 17 32 6.82% +// 26 512 8192 16 0 6.05% +// 27 576 8192 14 128 12.33% +// 28 640 8192 12 512 15.48% +// 29 704 8192 11 448 13.93% +// 30 768 8192 10 512 13.94% +// 31 896 8192 9 128 15.52% +// 32 1024 8192 8 0 12.40% +// 33 1152 8192 7 128 12.41% +// 34 1280 8192 6 512 15.55% +// 35 1408 16384 11 896 14.00% +// 36 1536 8192 5 512 14.00% +// 37 1792 16384 9 256 15.57% +// 38 2048 8192 4 0 12.45% +// 39 2304 16384 7 256 12.46% +// 40 2688 8192 3 128 15.59% +// 41 3072 24576 8 0 12.47% +// 42 3200 16384 5 384 6.22% +// 43 3456 24576 7 384 8.83% +// 44 4096 8192 2 0 15.60% +// 45 4864 24576 5 256 16.65% +// 46 5376 16384 3 256 10.92% +// 47 6144 24576 4 0 12.48% +// 48 6528 32768 5 128 6.23% +// 49 6784 40960 6 256 4.36% +// 50 6912 49152 7 768 3.37% +// 51 8192 8192 1 0 15.61% +// 52 9472 57344 6 512 14.28% +// 53 9728 49152 5 512 3.64% +// 54 10240 40960 4 0 4.99% +// 55 10880 32768 3 128 6.24% +// 56 12288 24576 2 0 11.45% +// 57 13568 40960 3 256 9.99% +// 58 14336 57344 4 0 5.35% +// 59 16384 16384 1 0 12.49% +// 60 18432 73728 4 0 11.11% +// 61 19072 57344 3 128 3.57% +// 62 20480 40960 2 0 6.87% +// 63 21760 65536 3 256 6.25% +// 64 24576 24576 1 0 11.45% +// 65 27264 81920 3 128 10.00% +// 66 28672 57344 2 0 4.91% +// 67 32768 32768 1 0 12.50% const ( _MaxSmallSize = 32768 smallSizeDiv = 8 smallSizeMax = 1024 largeSizeDiv = 128 - _NumSizeClasses = 67 + _NumSizeClasses = 68 _PageShift = 13 ) -var class_to_size = [_NumSizeClasses]uint16{0, 8, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256, 288, 320, 352, 384, 416, 448, 480, 512, 576, 640, 704, 768, 896, 1024, 1152, 1280, 1408, 1536, 1792, 2048, 2304, 2688, 3072, 3200, 3456, 4096, 4864, 5376, 6144, 6528, 6784, 6912, 8192, 9472, 9728, 10240, 10880, 12288, 13568, 14336, 16384, 18432, 19072, 20480, 21760, 24576, 27264, 28672, 32768} -var class_to_allocnpages = [_NumSizeClasses]uint8{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 3, 2, 3, 1, 3, 2, 3, 4, 5, 6, 1, 7, 6, 5, 4, 3, 5, 7, 2, 9, 7, 5, 8, 3, 10, 7, 4} +var class_to_size = [_NumSizeClasses]uint16{0, 8, 16, 24, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256, 288, 320, 352, 384, 416, 448, 480, 512, 576, 640, 704, 768, 896, 1024, 1152, 1280, 1408, 1536, 1792, 2048, 2304, 2688, 3072, 3200, 3456, 4096, 4864, 5376, 6144, 6528, 6784, 6912, 8192, 9472, 9728, 10240, 10880, 12288, 13568, 14336, 16384, 18432, 19072, 20480, 21760, 24576, 27264, 28672, 32768} +var class_to_allocnpages = [_NumSizeClasses]uint8{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 3, 2, 3, 1, 3, 2, 3, 4, 5, 6, 1, 7, 6, 5, 4, 3, 5, 7, 2, 9, 7, 5, 8, 3, 10, 7, 4} type divMagic struct { shift uint8 @@ -90,6 +91,6 @@ type divMagic struct { baseMask uint16 } -var class_to_divmagic = [_NumSizeClasses]divMagic{{0, 0, 0, 0}, {3, 0, 1, 65528}, {4, 0, 1, 65520}, {5, 0, 1, 65504}, {4, 11, 683, 0}, {6, 0, 1, 65472}, {4, 10, 205, 0}, {5, 9, 171, 0}, {4, 11, 293, 0}, {7, 0, 1, 65408}, {4, 13, 911, 0}, {5, 10, 205, 0}, {4, 12, 373, 0}, {6, 9, 171, 0}, {4, 13, 631, 0}, {5, 11, 293, 0}, {4, 13, 547, 0}, {8, 0, 1, 65280}, {5, 9, 57, 0}, {6, 9, 103, 0}, {5, 12, 373, 0}, {7, 7, 43, 0}, {5, 10, 79, 0}, {6, 10, 147, 0}, {5, 11, 137, 0}, {9, 0, 1, 65024}, {6, 9, 57, 0}, {7, 9, 103, 0}, {6, 11, 187, 0}, {8, 7, 43, 0}, {7, 8, 37, 0}, {10, 0, 1, 64512}, {7, 9, 57, 0}, {8, 6, 13, 0}, {7, 11, 187, 0}, {9, 5, 11, 0}, {8, 8, 37, 0}, {11, 0, 1, 63488}, {8, 9, 57, 0}, {7, 10, 49, 0}, {10, 5, 11, 0}, {7, 10, 41, 0}, {7, 9, 19, 0}, {12, 0, 1, 61440}, {8, 9, 27, 0}, {8, 10, 49, 0}, {11, 5, 11, 0}, {7, 13, 161, 0}, {7, 13, 155, 0}, {8, 9, 19, 0}, {13, 0, 1, 57344}, {8, 12, 111, 0}, {9, 9, 27, 0}, {11, 6, 13, 0}, {7, 14, 193, 0}, {12, 3, 3, 0}, {8, 13, 155, 0}, {11, 8, 37, 0}, {14, 0, 1, 49152}, {11, 8, 29, 0}, {7, 13, 55, 0}, {12, 5, 7, 0}, {8, 14, 193, 0}, {13, 3, 3, 0}, {7, 14, 77, 0}, {12, 7, 19, 0}, {15, 0, 1, 32768}} -var size_to_class8 = [smallSizeMax/smallSizeDiv + 1]uint8{0, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31} -var size_to_class128 = [(_MaxSmallSize-smallSizeMax)/largeSizeDiv + 1]uint8{31, 32, 33, 34, 35, 36, 36, 37, 37, 38, 38, 39, 39, 39, 40, 40, 40, 41, 42, 42, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 47, 47, 47, 48, 48, 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66} +var class_to_divmagic = [_NumSizeClasses]divMagic{{0, 0, 0, 0}, {3, 0, 1, 65528}, {4, 0, 1, 65520}, {3, 11, 683, 0}, {5, 0, 1, 65504}, {4, 11, 683, 0}, {6, 0, 1, 65472}, {4, 10, 205, 0}, {5, 9, 171, 0}, {4, 11, 293, 0}, {7, 0, 1, 65408}, {4, 13, 911, 0}, {5, 10, 205, 0}, {4, 12, 373, 0}, {6, 9, 171, 0}, {4, 13, 631, 0}, {5, 11, 293, 0}, {4, 13, 547, 0}, {8, 0, 1, 65280}, {5, 9, 57, 0}, {6, 9, 103, 0}, {5, 12, 373, 0}, {7, 7, 43, 0}, {5, 10, 79, 0}, {6, 10, 147, 0}, {5, 11, 137, 0}, {9, 0, 1, 65024}, {6, 9, 57, 0}, {7, 9, 103, 0}, {6, 11, 187, 0}, {8, 7, 43, 0}, {7, 8, 37, 0}, {10, 0, 1, 64512}, {7, 9, 57, 0}, {8, 6, 13, 0}, {7, 11, 187, 0}, {9, 5, 11, 0}, {8, 8, 37, 0}, {11, 0, 1, 63488}, {8, 9, 57, 0}, {7, 10, 49, 0}, {10, 5, 11, 0}, {7, 10, 41, 0}, {7, 9, 19, 0}, {12, 0, 1, 61440}, {8, 9, 27, 0}, {8, 10, 49, 0}, {11, 5, 11, 0}, {7, 13, 161, 0}, {7, 13, 155, 0}, {8, 9, 19, 0}, {13, 0, 1, 57344}, {8, 12, 111, 0}, {9, 9, 27, 0}, {11, 6, 13, 0}, {7, 14, 193, 0}, {12, 3, 3, 0}, {8, 13, 155, 0}, {11, 8, 37, 0}, {14, 0, 1, 49152}, {11, 8, 29, 0}, {7, 13, 55, 0}, {12, 5, 7, 0}, {8, 14, 193, 0}, {13, 3, 3, 0}, {7, 14, 77, 0}, {12, 7, 19, 0}, {15, 0, 1, 32768}} +var size_to_class8 = [smallSizeMax/smallSizeDiv + 1]uint8{0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32} +var size_to_class128 = [(_MaxSmallSize-smallSizeMax)/largeSizeDiv + 1]uint8{32, 33, 34, 35, 36, 37, 37, 38, 38, 39, 39, 40, 40, 40, 41, 41, 41, 42, 43, 43, 44, 44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, 49, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67} -- cgit v1.2.3-54-g00ecf From 57646534297a9bd193e6aaa4239c98984f371b97 Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Mon, 14 Sep 2020 10:19:47 -0400 Subject: cmd/api: omit outside dependencies when listing the packages in "std" As of CL 251159, when 'go list -deps std' is run within GOROOT/src, it treats the vendored external dependencies as real module dependencies, not standard-library "vendor/" packages (which still exist in that case, but are treated as distinct packages outside the "std" module). Fixes #41358 Updates #30241 Change-Id: Ic23eae9829d90e74a340d49ca9052e9191597410 Reviewed-on: https://go-review.googlesource.com/c/go/+/254738 Run-TryBot: Bryan C. Mills Trust: Bryan C. Mills TryBot-Result: Go Bot Reviewed-by: Michael Matloob Reviewed-by: Jay Conrod --- src/cmd/api/goapi.go | 17 +++++++++++++---- src/cmd/api/goapi_test.go | 13 +++++++++++++ src/cmd/go/internal/modload/load.go | 8 ++++++-- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go index 01b17b8839..6a80ed269b 100644 --- a/src/cmd/api/goapi.go +++ b/src/cmd/api/goapi.go @@ -87,7 +87,10 @@ var contexts = []*build.Context{ func contextName(c *build.Context) string { s := c.GOOS + "-" + c.GOARCH if c.CgoEnabled { - return s + "-cgo" + s += "-cgo" + } + if c.Dir != "" { + s += fmt.Sprintf(" [%s]", c.Dir) } return s } @@ -478,6 +481,9 @@ func (w *Walker) loadImports() { cmd := exec.Command(goCmd(), "list", "-e", "-deps", "-json", "std") cmd.Env = listEnv(w.context) + if w.context.Dir != "" { + cmd.Dir = w.context.Dir + } out, err := cmd.CombinedOutput() if err != nil { log.Fatalf("loading imports: %v\n%s", err, out) @@ -491,6 +497,7 @@ func (w *Walker) loadImports() { var pkg struct { ImportPath, Dir string ImportMap map[string]string + Standard bool } err := dec.Decode(&pkg) if err == io.EOF { @@ -503,11 +510,13 @@ func (w *Walker) loadImports() { // - Package "unsafe" contains special signatures requiring // extra care when printing them - ignore since it is not // going to change w/o a language change. - // - internal and vendored packages do not contribute to our - // API surface. + // - Internal and vendored packages do not contribute to our + // API surface. (If we are running within the "std" module, + // vendored dependencies appear as themselves instead of + // their "vendor/" standard-library copies.) // - 'go list std' does not include commands, which cannot be // imported anyway. - if ip := pkg.ImportPath; ip != "unsafe" && !strings.HasPrefix(ip, "vendor/") && !internalPkg.MatchString(ip) { + if ip := pkg.ImportPath; pkg.Standard && ip != "unsafe" && !strings.HasPrefix(ip, "vendor/") && !internalPkg.MatchString(ip) { stdPackages = append(stdPackages, ip) } importDir[pkg.ImportPath] = pkg.Dir diff --git a/src/cmd/api/goapi_test.go b/src/cmd/api/goapi_test.go index eaccc5ceb5..24620a94af 100644 --- a/src/cmd/api/goapi_test.go +++ b/src/cmd/api/goapi_test.go @@ -216,3 +216,16 @@ func TestIssue29837(t *testing.T) { } } } + +func TestIssue41358(t *testing.T) { + context := new(build.Context) + *context = build.Default + context.Dir = filepath.Join(context.GOROOT, "src") + + w := NewWalker(context, context.Dir) + for _, pkg := range w.stdPackages { + if strings.HasPrefix(pkg, "vendor/") || strings.HasPrefix(pkg, "golang.org/x/") { + t.Fatalf("stdPackages contains unexpected package %s", pkg) + } + } +} diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go index 1664d8c5be..2fe68e6f88 100644 --- a/src/cmd/go/internal/modload/load.go +++ b/src/cmd/go/internal/modload/load.go @@ -1106,8 +1106,12 @@ func (ld *loader) stdVendor(parentPath, path string) string { // Do the same for importers beginning with the prefix 'vendor/' even if we // are *inside* of the 'std' module: the 'vendor/' packages that resolve // globally from GOROOT/src/vendor (and are listed as part of 'go list std') - // are distinct from the real module dependencies, and cannot import internal - // packages from the real module. + // are distinct from the real module dependencies, and cannot import + // internal packages from the real module. + // + // (Note that although the 'vendor/' packages match the 'std' *package* + // pattern, they are not part of the std *module*, and do not affect + // 'go mod tidy' and similar module commands when working within std.) vendorPath := pathpkg.Join("vendor", path) if _, err := os.Stat(filepath.Join(cfg.GOROOTsrc, filepath.FromSlash(vendorPath))); err == nil { return vendorPath -- cgit v1.2.3-54-g00ecf From a408139bb0166f6e0a5d9fd17fc934da960c354e Mon Sep 17 00:00:00 2001 From: Changkun Ou Date: Mon, 14 Sep 2020 10:24:59 +0200 Subject: testing: fix panicking tests hang if Cleanup calls FailNow Previously, it was impossible to call FailNow in a Cleanup. Because it can terminate a panicking goroutine and cause its parent hangs on t.signal channel. This CL sends the signal in a deferred call to prevent the hang. Fixes #41355 Change-Id: I4552d3a7ea763ef86817bf9b50c0e37fb34bf20f Reviewed-on: https://go-review.googlesource.com/c/go/+/254637 Reviewed-by: Ian Lance Taylor Reviewed-by: Emmanuel Odeke Run-TryBot: Ian Lance Taylor TryBot-Result: Go Bot Trust: Emmanuel Odeke --- .../go/testdata/script/test_cleanup_failnow.txt | 33 ++++++++++++++++++++++ src/testing/testing.go | 12 +++++++- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/cmd/go/testdata/script/test_cleanup_failnow.txt diff --git a/src/cmd/go/testdata/script/test_cleanup_failnow.txt b/src/cmd/go/testdata/script/test_cleanup_failnow.txt new file mode 100644 index 0000000000..5ad4185fc1 --- /dev/null +++ b/src/cmd/go/testdata/script/test_cleanup_failnow.txt @@ -0,0 +1,33 @@ +# For issue 41355 +[short] skip + +! go test -v cleanup_failnow/panic_nocleanup_test.go +stdout '(?s)panic: die \[recovered\].*panic: die' +! stdout '(?s)panic: die \[recovered\].*panic: die.*panic: die' + +! go test -v cleanup_failnow/panic_withcleanup_test.go +stdout '(?s)panic: die \[recovered\].*panic: die' +! stdout '(?s)panic: die \[recovered\].*panic: die.*panic: die' + +-- cleanup_failnow/panic_nocleanup_test.go -- +package panic_nocleanup_test +import "testing" +func TestX(t *testing.T) { + t.Run("x", func(t *testing.T) { + panic("die") + }) +} + +-- cleanup_failnow/panic_withcleanup_test.go -- +package panic_withcleanup_test +import "testing" +func TestCleanupWithFailNow(t *testing.T) { + t.Cleanup(func() { + t.FailNow() + }) + t.Run("x", func(t *testing.T) { + t.Run("y", func(t *testing.T) { + panic("die") + }) + }) +} \ No newline at end of file diff --git a/src/testing/testing.go b/src/testing/testing.go index 66f296234a..d86354093a 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -1075,6 +1075,7 @@ func tRunner(t *T, fn func(t *T)) { // If the test panicked, print any test output before dying. err := recover() signal := true + if !t.finished && err == nil { err = errNilPanicOrGoexit for p := t.parent; p != nil; p = p.parent { @@ -1086,6 +1087,15 @@ func tRunner(t *T, fn func(t *T)) { } } } + // Use a deferred call to ensure that we report that the test is + // complete even if a cleanup function calls t.FailNow. See issue 41355. + didPanic := false + defer func() { + t.signal <- signal + if err != nil && !didPanic { + panic(err) + } + }() doPanic := func(err interface{}) { t.Fail() @@ -1103,6 +1113,7 @@ func tRunner(t *T, fn func(t *T)) { fmt.Fprintf(root.parent.w, "cleanup panicked with %v", r) } } + didPanic = true panic(err) } if err != nil { @@ -1144,7 +1155,6 @@ func tRunner(t *T, fn func(t *T)) { if t.parent != nil && atomic.LoadInt32(&t.hasSub) == 0 { t.setRan() } - t.signal <- signal }() defer func() { if len(t.sub) == 0 { -- cgit v1.2.3-54-g00ecf From 114719e16e9681bd1001326598ededa719c17944 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Mon, 14 Sep 2020 21:00:52 +0000 Subject: Revert "encoding/json: implement Is on all errors" This reverts CL 254537. Reason for revert: Reason for revert: The recommended way to check for a type of error is errors.As. API changes should also start with a proposal. Change-Id: I07c37428575e99c80b17525833a61831d10963bb Reviewed-on: https://go-review.googlesource.com/c/go/+/254857 Trust: Damien Neil Reviewed-by: Emmanuel Odeke --- src/encoding/json/decode.go | 18 ------------------ src/encoding/json/decode_test.go | 31 ------------------------------- src/encoding/json/encode.go | 12 ------------ src/encoding/json/encode_test.go | 24 +----------------------- 4 files changed, 1 insertion(+), 84 deletions(-) diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go index 1b006ffb17..86d8a69db7 100644 --- a/src/encoding/json/decode.go +++ b/src/encoding/json/decode.go @@ -136,12 +136,6 @@ func (e *UnmarshalTypeError) Error() string { return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() } -// Is returns true if target is a UnmarshalTypeError. -func (e *UnmarshalTypeError) Is(target error) bool { - _, ok := target.(*UnmarshalTypeError) - return ok -} - // An UnmarshalFieldError describes a JSON object key that // led to an unexported (and therefore unwritable) struct field. // @@ -156,24 +150,12 @@ func (e *UnmarshalFieldError) Error() string { return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() } -// Is returns true if target is a UnmarshalFieldError. -func (e *UnmarshalFieldError) Is(target error) bool { - _, ok := target.(*UnmarshalFieldError) - return ok -} - // An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. // (The argument to Unmarshal must be a non-nil pointer.) type InvalidUnmarshalError struct { Type reflect.Type } -// Is returns true if target is a InvalidUnmarshalError. -func (e *InvalidUnmarshalError) Is(target error) bool { - _, ok := target.(*InvalidUnmarshalError) - return ok -} - func (e *InvalidUnmarshalError) Error() string { if e.Type == nil { return "json: Unmarshal(nil)" diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go index b707dcfa99..219e845c7b 100644 --- a/src/encoding/json/decode_test.go +++ b/src/encoding/json/decode_test.go @@ -2572,34 +2572,3 @@ func TestUnmarshalMaxDepth(t *testing.T) { } } } - -func TestInvalidUnmarshalErrorIs(t *testing.T) { - err := fmt.Errorf("apackage: %w: failed to parse struct", &InvalidUnmarshalError{reflect.TypeOf("a")}) - if !errors.Is(err, &InvalidUnmarshalError{}) { - t.Fatalf("%v should be unwrapped to a InvalidUnmarshalError", err) - } -} - -func TestUnmarshalFieldErrorIs(t *testing.T) { - err := fmt.Errorf("apackage: %w: failed to parse struct", &UnmarshalFieldError{ - Key: "foo", - Type: reflect.TypeOf("a"), - Field: reflect.StructField{Name: "b"}, - }) - if !errors.Is(err, &UnmarshalFieldError{}) { - t.Fatalf("%v should be unwrapped to a UnmarshalFieldError", err) - } -} - -func TestUnmarshalTypeErrorIs(t *testing.T) { - err := fmt.Errorf("apackage: %w: failed to parse struct", &UnmarshalTypeError{ - Value: "foo", - Type: reflect.TypeOf("a"), - Offset: 1, - Struct: "Foo", - Field: "Bar", - }) - if !errors.Is(err, &UnmarshalTypeError{}) { - t.Fatalf("%v should be unwrapped to a UnmarshalTypeError", err) - } -} diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go index 8e6b342b59..578d551102 100644 --- a/src/encoding/json/encode.go +++ b/src/encoding/json/encode.go @@ -245,12 +245,6 @@ func (e *UnsupportedValueError) Error() string { return "json: unsupported value: " + e.Str } -// Is returns true if target is a UnsupportedValueError. -func (e *UnsupportedValueError) Is(target error) bool { - _, ok := target.(*UnsupportedValueError) - return ok -} - // Before Go 1.2, an InvalidUTF8Error was returned by Marshal when // attempting to encode a string value with invalid UTF-8 sequences. // As of Go 1.2, Marshal instead coerces the string to valid UTF-8 by @@ -285,12 +279,6 @@ func (e *MarshalerError) Error() string { // Unwrap returns the underlying error. func (e *MarshalerError) Unwrap() error { return e.Err } -// Is returns true if target is a MarshalerError. -func (e *MarshalerError) Is(target error) bool { - _, ok := target.(*MarshalerError) - return ok -} - var hex = "0123456789abcdef" // An encodeState encodes JSON into a bytes.Buffer. diff --git a/src/encoding/json/encode_test.go b/src/encoding/json/encode_test.go index 90826a7f47..7290eca06f 100644 --- a/src/encoding/json/encode_test.go +++ b/src/encoding/json/encode_test.go @@ -7,7 +7,6 @@ package json import ( "bytes" "encoding" - "errors" "fmt" "log" "math" @@ -212,7 +211,7 @@ var unsupportedValues = []interface{}{ func TestUnsupportedValues(t *testing.T) { for _, v := range unsupportedValues { if _, err := Marshal(v); err != nil { - if !errors.Is(err, &UnsupportedValueError{}) { + if _, ok := err.(*UnsupportedValueError); !ok { t.Errorf("for %v, got %T want UnsupportedValueError", v, err) } } else { @@ -1156,24 +1155,3 @@ func TestMarshalerError(t *testing.T) { } } } - -func TestMarshalerErrorIs(t *testing.T) { - err := fmt.Errorf("apackage: %w: failed to parse struct", &MarshalerError{ - reflect.TypeOf("a"), - fmt.Errorf("something"), - "TestMarshalerErrorIs", - }) - if !errors.Is(err, &MarshalerError{}) { - t.Fatalf("%v should be unwrapped to a MarshalerError", err) - } -} - -func TestUnsupportedValueErrorIs(t *testing.T) { - err := fmt.Errorf("apackage: %w: failed to parse struct", &UnsupportedValueError{ - Value: reflect.Value{}, - Str: "Foo", - }) - if !errors.Is(err, &UnsupportedValueError{}) { - t.Fatalf("%v should be unwrapped to a UnsupportedValueError", err) - } -} -- cgit v1.2.3-54-g00ecf From 506eb0a9b1a7051f64788f330ea26722fa293f3c Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Mon, 14 Sep 2020 20:59:50 +0000 Subject: Revert "encoding/json: implement Is on SyntaxError" This reverts CL 253037. Reason for revert: The recommended way to check for a type of error is errors.As. API changes should also start with a proposal. Change-Id: I62896717aa47ed491c2c4775d2b05d80e5e9cde3 Reviewed-on: https://go-review.googlesource.com/c/go/+/254837 Trust: Damien Neil Reviewed-by: Emmanuel Odeke --- src/encoding/json/scanner.go | 6 ------ src/encoding/json/scanner_test.go | 9 --------- 2 files changed, 15 deletions(-) diff --git a/src/encoding/json/scanner.go b/src/encoding/json/scanner.go index 05218f9cc3..9dc1903e2d 100644 --- a/src/encoding/json/scanner.go +++ b/src/encoding/json/scanner.go @@ -49,12 +49,6 @@ type SyntaxError struct { func (e *SyntaxError) Error() string { return e.msg } -// Is returns true if target is a SyntaxError. -func (e *SyntaxError) Is(target error) bool { - _, ok := target.(*SyntaxError) - return ok -} - // A scanner is a JSON scanning state machine. // Callers call scan.reset and then pass bytes in one at a time // by calling scan.step(&scan, c) for each byte. diff --git a/src/encoding/json/scanner_test.go b/src/encoding/json/scanner_test.go index c12d9bf3d7..3737516a45 100644 --- a/src/encoding/json/scanner_test.go +++ b/src/encoding/json/scanner_test.go @@ -6,8 +6,6 @@ package json import ( "bytes" - "errors" - "fmt" "math" "math/rand" "reflect" @@ -203,13 +201,6 @@ func TestIndentErrors(t *testing.T) { } } -func TestSyntaxErrorIs(t *testing.T) { - err := fmt.Errorf("apackage: %w: failed to parse struct", &SyntaxError{"some error", 43}) - if !errors.Is(err, &SyntaxError{}) { - t.Fatalf("%v should be unwrapped to a SyntaxError", err) - } -} - func diff(t *testing.T, a, b []byte) { for i := 0; ; i++ { if i >= len(a) || i >= len(b) || a[i] != b[i] { -- cgit v1.2.3-54-g00ecf From 237410547bb81ae3c58e9c5bf0cf59edc989e243 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 14 Sep 2020 12:53:36 -0700 Subject: cmd/compile: better dclcontext handling in func{hdr,body} funchdr and funcbody currently assume that either (1) Curfn == nil && dclcontext == PEXTERN, or (2) Curfn != nil && dclcontext == PAUTO. This is a reasonable assumption during parsing. However, these functions end up getting used in other contexts, and not all callers are so disciplined about Curfn/dclcontext handling. This CL changes them to save/restore arbitrary Curfn/dclcontext pairs instead. This is necessary for the followup CL, which pushes fninit earlier. Otherwise, Curfn/dclcontext fall out of sync, and funchdr panics. Passes toolstash-check. Updates #33485. Change-Id: I19b1be23db1bad6475345ae5c81bbdc66291a3a7 Reviewed-on: https://go-review.googlesource.com/c/go/+/254838 Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Keith Randall Trust: Matthew Dempsky --- src/cmd/compile/internal/gc/dcl.go | 28 +++++++++++++--------------- src/cmd/compile/internal/gc/main.go | 3 +++ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go index 69eb13f607..a362d1a643 100644 --- a/src/cmd/compile/internal/gc/dcl.go +++ b/src/cmd/compile/internal/gc/dcl.go @@ -382,14 +382,11 @@ func ifacedcl(n *Node) { // returns in auto-declaration context. func funchdr(n *Node) { // change the declaration context from extern to auto - if Curfn == nil && dclcontext != PEXTERN { - Fatalf("funchdr: dclcontext = %d", dclcontext) - } - + funcStack = append(funcStack, funcStackEnt{Curfn, dclcontext}) + Curfn = n dclcontext = PAUTO + types.Markdcl() - funcstack = append(funcstack, Curfn) - Curfn = n if n.Func.Nname != nil { funcargs(n.Func.Nname.Name.Param.Ntype) @@ -497,21 +494,22 @@ func funcarg2(f *types.Field, ctxt Class) { declare(n, ctxt) } -var funcstack []*Node // stack of previous values of Curfn +var funcStack []funcStackEnt // stack of previous values of Curfn/dclcontext + +type funcStackEnt struct { + curfn *Node + dclcontext Class +} // finish the body. // called in auto-declaration context. // returns in extern-declaration context. func funcbody() { - // change the declaration context from auto to extern - if dclcontext != PAUTO { - Fatalf("funcbody: unexpected dclcontext %d", dclcontext) - } + // change the declaration context from auto to previous context types.Popdcl() - funcstack, Curfn = funcstack[:len(funcstack)-1], funcstack[len(funcstack)-1] - if Curfn == nil { - dclcontext = PEXTERN - } + var e funcStackEnt + funcStack, e = funcStack[:len(funcStack)-1], funcStack[len(funcStack)-1] + Curfn, dclcontext = e.curfn, e.dclcontext } // structs, functions, and methods. diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index eedfc4bb25..9bce6cf8cb 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -809,6 +809,9 @@ func Main(archInit func(*Arch)) { } } + if len(funcStack) != 0 { + Fatalf("funcStack is non-empty: %v", len(funcStack)) + } if len(compilequeue) != 0 { Fatalf("%d uncompiled functions", len(compilequeue)) } -- cgit v1.2.3-54-g00ecf From f4936d09fd5a1fff890d63ee2ab9543243dc4da6 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 14 Sep 2020 12:56:37 -0700 Subject: cmd/compile: call fninit earlier This allows the global initializers function to go through normal mid-end optimizations (e.g., inlining, escape analysis) like any other function. Updates #33485. Change-Id: I9bcfe98b8628d1aca09b4c238d8d3b74c69010a5 Reviewed-on: https://go-review.googlesource.com/c/go/+/254839 Reviewed-by: Keith Randall Trust: Matthew Dempsky --- src/cmd/compile/internal/gc/init.go | 8 +++----- src/cmd/compile/internal/gc/main.go | 6 ++---- test/inline.go | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/cmd/compile/internal/gc/init.go b/src/cmd/compile/internal/gc/init.go index 94cbcf9846..ec9cc4bddc 100644 --- a/src/cmd/compile/internal/gc/init.go +++ b/src/cmd/compile/internal/gc/init.go @@ -59,7 +59,7 @@ func fninit(n []*Node) { Curfn = fn typecheckslice(nf, ctxStmt) Curfn = nil - funccompile(fn) + xtop = append(xtop, fn) fns = append(fns, initializers.Linksym()) } if dummyInitFn.Func.Dcl != nil { @@ -68,16 +68,14 @@ func fninit(n []*Node) { // something's weird if we get here. Fatalf("dummyInitFn still has declarations") } + dummyInitFn = nil // Record user init functions. for i := 0; i < renameinitgen; i++ { s := lookupN("init.", i) fn := asNode(s.Def).Name.Defn // Skip init functions with empty bodies. - // noder.go doesn't allow external init functions, and - // order.go has already removed any OEMPTY nodes, so - // checking Len() == 0 is sufficient here. - if fn.Nbody.Len() == 0 { + if fn.Nbody.Len() == 1 && fn.Nbody.First().Op == OEMPTY { continue } fns = append(fns, s.Linksym()) diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 9bce6cf8cb..8783cb4e46 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -642,6 +642,8 @@ func Main(archInit func(*Arch)) { errorexit() } + fninit(xtop) + // Phase 4: Decide how to capture closed variables. // This needs to run before escape analysis, // because variables captured by value do not escape. @@ -751,10 +753,6 @@ func Main(archInit func(*Arch)) { } timings.AddEvent(fcount, "funcs") - if nsavederrors+nerrors == 0 { - fninit(xtop) - } - compileFunctions() if nowritebarrierrecCheck != nil { diff --git a/test/inline.go b/test/inline.go index 0b3ad55d46..1c5c1bc8d3 100644 --- a/test/inline.go +++ b/test/inline.go @@ -50,7 +50,7 @@ func j(x int) int { // ERROR "can inline j" } } -var somethingWrong error = errors.New("something went wrong") +var somethingWrong error = errors.New("something went wrong") // ERROR "can inline init" "inlining call to errors.New" "errors.errorString.* escapes to heap" // local closures can be inlined func l(x, y int) (int, int, error) { -- cgit v1.2.3-54-g00ecf From d20298e1c7d1df794a11ce7768e027c6759df2a4 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Mon, 14 Sep 2020 10:38:45 +0700 Subject: cmd/compile: make funccompile non-reentrant Currently, there's awkward reentrancy issue with funccompile: funccompile -> compile -> dtypesym -> geneq/genhash/genwrapper -> funccompile Though it's not a problem at this moment, some attempts by @mdempsky to move order/walk/instrument into buildssa was failed, due to SSA cache corruption. This commit fixes that reentrancy issue, by making generated functions to be pumped through the same compile workqueue that normal functions are compiled. We do this by adding them to xtop, instead of calling funccompile directly in geneq/genhash/genwrapper. In dumpdata, we look for uncompiled functions in xtop instead of compilequeue, then finish compiling them. Updates #38463 Fixes #33485 Change-Id: Ic9f0ce45b56ae2ff3862f17fd979253ddc144bb5 Reviewed-on: https://go-review.googlesource.com/c/go/+/254617 Run-TryBot: Cuong Manh Le Reviewed-by: Matthew Dempsky TryBot-Result: Go Bot Trust: Keith Randall Trust: Cuong Manh Le --- src/cmd/compile/internal/gc/alg.go | 4 ++-- src/cmd/compile/internal/gc/obj.go | 26 +++++++++++++++++++++++++- src/cmd/compile/internal/gc/subr.go | 2 +- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go index c9d71ea00b..6302b88f59 100644 --- a/src/cmd/compile/internal/gc/alg.go +++ b/src/cmd/compile/internal/gc/alg.go @@ -392,7 +392,7 @@ func genhash(t *types.Type) *obj.LSym { } fn.Func.SetNilCheckDisabled(true) - funccompile(fn) + xtop = append(xtop, fn) // Build closure. It doesn't close over any variables, so // it contains just the function pointer. @@ -754,7 +754,7 @@ func geneq(t *types.Type) *obj.LSym { // neither of which can be nil, and our comparisons // are shallow. fn.Func.SetNilCheckDisabled(true) - funccompile(fn) + xtop = append(xtop, fn) // Generate a closure which points at the function we just generated. dsymptr(closure, 0, sym.Linksym(), 0) diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index af5037c5a8..b55331a948 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -113,12 +113,16 @@ func dumpCompilerObj(bout *bio.Writer) { func dumpdata() { externs := len(externdcl) + xtops := len(xtop) dumpglobls() addptabs() + exportlistLen := len(exportlist) addsignats(externdcl) dumpsignats() dumptabs() + ptabsLen := len(ptabs) + itabsLen := len(itabs) dumpimportstrings() dumpbasictypes() @@ -129,9 +133,19 @@ func dumpdata() { // number of types in a finite amount of code. // In the typical case, we loop 0 or 1 times. // It was not until issue 24761 that we found any code that required a loop at all. - for len(compilequeue) > 0 { + for { + for i := xtops; i < len(xtop); i++ { + n := xtop[i] + if n.Op == ODCLFUNC { + funccompile(n) + } + } + xtops = len(xtop) compileFunctions() dumpsignats() + if xtops == len(xtop) { + break + } } // Dump extra globals. @@ -149,6 +163,16 @@ func dumpdata() { } addGCLocals() + + if exportlistLen != len(exportlist) { + Fatalf("exportlist changed after compile functions loop") + } + if ptabsLen != len(ptabs) { + Fatalf("ptabs changed after compile functions loop") + } + if itabsLen != len(itabs) { + Fatalf("itabs changed after compile functions loop") + } } func dumpLinkerObj(bout *bio.Writer) { diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index d3ba53ff0c..5a5833d19f 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -1615,7 +1615,7 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) { escapeFuncs([]*Node{fn}, false) Curfn = nil - funccompile(fn) + xtop = append(xtop, fn) } func paramNnames(ft *types.Type) []*Node { -- cgit v1.2.3-54-g00ecf From bae9cf651796db898b1e4bd77a1a47c5f2d7b04d Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 14 Sep 2020 19:24:55 -0700 Subject: test: fix inline.go to pass linux-amd64-noopt Updates #33485. Change-Id: I3330860cdff1e9797466a7630bcdb7792c465b06 Reviewed-on: https://go-review.googlesource.com/c/go/+/254938 Run-TryBot: Matthew Dempsky Reviewed-by: Cuong Manh Le TryBot-Result: Go Bot Trust: Matthew Dempsky --- test/inline.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/inline.go b/test/inline.go index 1c5c1bc8d3..3edcf2edfd 100644 --- a/test/inline.go +++ b/test/inline.go @@ -10,7 +10,6 @@ package foo import ( - "errors" "runtime" "unsafe" ) @@ -50,7 +49,7 @@ func j(x int) int { // ERROR "can inline j" } } -var somethingWrong error = errors.New("something went wrong") // ERROR "can inline init" "inlining call to errors.New" "errors.errorString.* escapes to heap" +var somethingWrong error // local closures can be inlined func l(x, y int) (int, int, error) { -- cgit v1.2.3-54-g00ecf From 3b64e6b010775839f2daef4ac3fb607bf1519e05 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Mon, 14 Sep 2020 11:06:39 +0200 Subject: internal/poll, internal/syscall/unix, net: enable writev on illumos Illumos supports iovec read/write. Add the writev wrapper to internal/syscall/unix and use it to implement internal/poll.writev for net.(*netFD).writeBuffers. Change-Id: Ie256c2f96aba8e61fb21991788789a049425f792 Reviewed-on: https://go-review.googlesource.com/c/go/+/254638 Run-TryBot: Tobias Klauser TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor Trust: Tobias Klauser --- src/internal/poll/fd_writev_illumos.go | 16 +++++++++++++++ src/internal/poll/iovec_illumos.go | 16 +++++++++++++++ src/internal/poll/iovec_unix.go | 13 +++++++++++++ src/internal/poll/writev.go | 4 ++-- src/internal/syscall/unix/writev_illumos.go | 30 +++++++++++++++++++++++++++++ src/net/writev_test.go | 2 +- src/net/writev_unix.go | 2 +- 7 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 src/internal/poll/fd_writev_illumos.go create mode 100644 src/internal/poll/iovec_illumos.go create mode 100644 src/internal/poll/iovec_unix.go create mode 100644 src/internal/syscall/unix/writev_illumos.go diff --git a/src/internal/poll/fd_writev_illumos.go b/src/internal/poll/fd_writev_illumos.go new file mode 100644 index 0000000000..1fa47ab1a3 --- /dev/null +++ b/src/internal/poll/fd_writev_illumos.go @@ -0,0 +1,16 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build illumos + +package poll + +import ( + "internal/syscall/unix" + "syscall" +) + +func writev(fd int, iovecs []syscall.Iovec) (uintptr, error) { + return unix.Writev(fd, iovecs) +} diff --git a/src/internal/poll/iovec_illumos.go b/src/internal/poll/iovec_illumos.go new file mode 100644 index 0000000000..057067465b --- /dev/null +++ b/src/internal/poll/iovec_illumos.go @@ -0,0 +1,16 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build illumos + +package poll + +import ( + "syscall" + "unsafe" +) + +func newIovecWithBase(base *byte) syscall.Iovec { + return syscall.Iovec{Base: (*int8)(unsafe.Pointer(base))} +} diff --git a/src/internal/poll/iovec_unix.go b/src/internal/poll/iovec_unix.go new file mode 100644 index 0000000000..6f98947866 --- /dev/null +++ b/src/internal/poll/iovec_unix.go @@ -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. + +// +build darwin dragonfly freebsd linux netbsd openbsd + +package poll + +import "syscall" + +func newIovecWithBase(base *byte) syscall.Iovec { + return syscall.Iovec{Base: base} +} diff --git a/src/internal/poll/writev.go b/src/internal/poll/writev.go index 305e2fd209..0123fc33de 100644 --- a/src/internal/poll/writev.go +++ b/src/internal/poll/writev.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd illumos linux netbsd openbsd package poll @@ -38,7 +38,7 @@ func (fd *FD) Writev(v *[][]byte) (int64, error) { if len(chunk) == 0 { continue } - iovecs = append(iovecs, syscall.Iovec{Base: &chunk[0]}) + iovecs = append(iovecs, newIovecWithBase(&chunk[0])) if fd.IsStream && len(chunk) > 1<<30 { iovecs[len(iovecs)-1].SetLen(1 << 30) break // continue chunk on next writev diff --git a/src/internal/syscall/unix/writev_illumos.go b/src/internal/syscall/unix/writev_illumos.go new file mode 100644 index 0000000000..eb7973d65b --- /dev/null +++ b/src/internal/syscall/unix/writev_illumos.go @@ -0,0 +1,30 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build illumos + +package unix + +import ( + "syscall" + "unsafe" +) + +//go:cgo_import_dynamic libc_writev writev "libc.so" + +//go:linkname procwritev libc_writev + +var procwritev uintptr + +func Writev(fd int, iovs []syscall.Iovec) (uintptr, error) { + var p *syscall.Iovec + if len(iovs) > 0 { + p = &iovs[0] + } + n, _, errno := syscall6(uintptr(unsafe.Pointer(&procwritev)), 3, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(len(iovs)), 0, 0, 0) + if errno != 0 { + return 0, errno + } + return n, nil +} diff --git a/src/net/writev_test.go b/src/net/writev_test.go index c43be84418..d6dce3cc69 100644 --- a/src/net/writev_test.go +++ b/src/net/writev_test.go @@ -154,7 +154,7 @@ func testBuffer_writeTo(t *testing.T, chunks int, useCopy bool) { var wantSum int switch runtime.GOOS { - case "android", "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd": + case "android", "darwin", "dragonfly", "freebsd", "illumos", "linux", "netbsd", "openbsd": var wantMinCalls int wantSum = want.Len() v := chunks diff --git a/src/net/writev_unix.go b/src/net/writev_unix.go index bf0fbf8a13..8b20f42b34 100644 --- a/src/net/writev_unix.go +++ b/src/net/writev_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd illumos linux netbsd openbsd package net -- cgit v1.2.3-54-g00ecf From ea33523877e4c7a136f2db94a8b5bc4e40220be2 Mon Sep 17 00:00:00 2001 From: Xiangdong Ji Date: Tue, 8 Sep 2020 09:55:20 +0000 Subject: cmd/compile: rewrite some ARM64 rules to use typed aux Passes toolstash-check -all. Change-Id: I7ec36bc048f3031c8201107e6fc5d1257271dbf1 Reviewed-on: https://go-review.googlesource.com/c/go/+/234379 Run-TryBot: Alberto Donizetti TryBot-Result: Go Bot Trust: Alberto Donizetti Reviewed-by: Keith Randall --- src/cmd/compile/internal/ssa/gen/ARM64.rules | 496 +++++++-------- src/cmd/compile/internal/ssa/rewriteARM64.go | 912 +++++++++++++-------------- 2 files changed, 704 insertions(+), 704 deletions(-) diff --git a/src/cmd/compile/internal/ssa/gen/ARM64.rules b/src/cmd/compile/internal/ssa/gen/ARM64.rules index 311067e87a..c4a3532632 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64.rules +++ b/src/cmd/compile/internal/ssa/gen/ARM64.rules @@ -1116,280 +1116,280 @@ // if a register move has only 1 use, just use the same register without emitting instruction // MOVDnop doesn't emit instruction, only for ensuring the type. -(MOVDreg x) && x.Uses == 1 -> (MOVDnop x) +(MOVDreg x) && x.Uses == 1 => (MOVDnop x) // fold constant into arithmatic ops -(ADD x (MOVDconst [c])) -> (ADDconst [c] x) -(SUB x (MOVDconst [c])) -> (SUBconst [c] x) -(AND x (MOVDconst [c])) -> (ANDconst [c] x) -(OR x (MOVDconst [c])) -> (ORconst [c] x) -(XOR x (MOVDconst [c])) -> (XORconst [c] x) -(TST x (MOVDconst [c])) -> (TSTconst [c] x) -(TSTW x (MOVDconst [c])) -> (TSTWconst [c] x) -(CMN x (MOVDconst [c])) -> (CMNconst [c] x) -(CMNW x (MOVDconst [c])) -> (CMNWconst [c] x) -(BIC x (MOVDconst [c])) -> (ANDconst [^c] x) -(EON x (MOVDconst [c])) -> (XORconst [^c] x) -(ORN x (MOVDconst [c])) -> (ORconst [^c] x) - -(SLL x (MOVDconst [c])) -> (SLLconst x [c&63]) // Note: I don't think we ever generate bad constant shifts (i.e. c>=64) -(SRL x (MOVDconst [c])) -> (SRLconst x [c&63]) -(SRA x (MOVDconst [c])) -> (SRAconst x [c&63]) - -(CMP x (MOVDconst [c])) -> (CMPconst [c] x) -(CMP (MOVDconst [c]) x) -> (InvertFlags (CMPconst [c] x)) +(ADD x (MOVDconst [c])) => (ADDconst [c] x) +(SUB x (MOVDconst [c])) => (SUBconst [c] x) +(AND x (MOVDconst [c])) => (ANDconst [c] x) +(OR x (MOVDconst [c])) => (ORconst [c] x) +(XOR x (MOVDconst [c])) => (XORconst [c] x) +(TST x (MOVDconst [c])) => (TSTconst [c] x) +(TSTW x (MOVDconst [c])) => (TSTWconst [int32(c)] x) +(CMN x (MOVDconst [c])) => (CMNconst [c] x) +(CMNW x (MOVDconst [c])) => (CMNWconst [int32(c)] x) +(BIC x (MOVDconst [c])) => (ANDconst [^c] x) +(EON x (MOVDconst [c])) => (XORconst [^c] x) +(ORN x (MOVDconst [c])) => (ORconst [^c] x) + +(SLL x (MOVDconst [c])) => (SLLconst x [c&63]) // Note: I don't think we ever generate bad constant shifts (i.e. c>=64) +(SRL x (MOVDconst [c])) => (SRLconst x [c&63]) +(SRA x (MOVDconst [c])) => (SRAconst x [c&63]) + +(CMP x (MOVDconst [c])) => (CMPconst [c] x) +(CMP (MOVDconst [c]) x) => (InvertFlags (CMPconst [c] x)) (CMPW x (MOVDconst [c])) => (CMPWconst [int32(c)] x) (CMPW (MOVDconst [c]) x) => (InvertFlags (CMPWconst [int32(c)] x)) // Canonicalize the order of arguments to comparisons - helps with CSE. -((CMP|CMPW) x y) && x.ID > y.ID -> (InvertFlags ((CMP|CMPW) y x)) +((CMP|CMPW) x y) && x.ID > y.ID => (InvertFlags ((CMP|CMPW) y x)) -// mul-neg -> mneg -(NEG (MUL x y)) -> (MNEG x y) -(NEG (MULW x y)) -> (MNEGW x y) -(MUL (NEG x) y) -> (MNEG x y) -(MULW (NEG x) y) -> (MNEGW x y) +// mul-neg => mneg +(NEG (MUL x y)) => (MNEG x y) +(NEG (MULW x y)) => (MNEGW x y) +(MUL (NEG x) y) => (MNEG x y) +(MULW (NEG x) y) => (MNEGW x y) // madd/msub -(ADD a l:(MUL x y)) && l.Uses==1 && clobber(l) -> (MADD a x y) -(SUB a l:(MUL x y)) && l.Uses==1 && clobber(l) -> (MSUB a x y) -(ADD a l:(MNEG x y)) && l.Uses==1 && clobber(l) -> (MSUB a x y) -(SUB a l:(MNEG x y)) && l.Uses==1 && clobber(l) -> (MADD a x y) +(ADD a l:(MUL x y)) && l.Uses==1 && clobber(l) => (MADD a x y) +(SUB a l:(MUL x y)) && l.Uses==1 && clobber(l) => (MSUB a x y) +(ADD a l:(MNEG x y)) && l.Uses==1 && clobber(l) => (MSUB a x y) +(SUB a l:(MNEG x y)) && l.Uses==1 && clobber(l) => (MADD a x y) -(ADD a l:(MULW x y)) && a.Type.Size() != 8 && l.Uses==1 && clobber(l) -> (MADDW a x y) -(SUB a l:(MULW x y)) && a.Type.Size() != 8 && l.Uses==1 && clobber(l) -> (MSUBW a x y) -(ADD a l:(MNEGW x y)) && a.Type.Size() != 8 && l.Uses==1 && clobber(l) -> (MSUBW a x y) -(SUB a l:(MNEGW x y)) && a.Type.Size() != 8 && l.Uses==1 && clobber(l) -> (MADDW a x y) +(ADD a l:(MULW x y)) && a.Type.Size() != 8 && l.Uses==1 && clobber(l) => (MADDW a x y) +(SUB a l:(MULW x y)) && a.Type.Size() != 8 && l.Uses==1 && clobber(l) => (MSUBW a x y) +(ADD a l:(MNEGW x y)) && a.Type.Size() != 8 && l.Uses==1 && clobber(l) => (MSUBW a x y) +(SUB a l:(MNEGW x y)) && a.Type.Size() != 8 && l.Uses==1 && clobber(l) => (MADDW a x y) // optimize ADCSflags, SBCSflags and friends -(ADCSflags x y (Select1 (ADDSconstflags [-1] (ADCzerocarry c)))) -> (ADCSflags x y c) -(ADCSflags x y (Select1 (ADDSconstflags [-1] (MOVDconst [0])))) -> (ADDSflags x y) -(SBCSflags x y (Select1 (NEGSflags (NEG (NGCzerocarry bo))))) -> (SBCSflags x y bo) -(SBCSflags x y (Select1 (NEGSflags (MOVDconst [0])))) -> (SUBSflags x y) +(ADCSflags x y (Select1 (ADDSconstflags [-1] (ADCzerocarry c)))) => (ADCSflags x y c) +(ADCSflags x y (Select1 (ADDSconstflags [-1] (MOVDconst [0])))) => (ADDSflags x y) +(SBCSflags x y (Select1 (NEGSflags (NEG (NGCzerocarry bo))))) => (SBCSflags x y bo) +(SBCSflags x y (Select1 (NEGSflags (MOVDconst [0])))) => (SUBSflags x y) // mul by constant -(MUL x (MOVDconst [-1])) -> (NEG x) -(MUL _ (MOVDconst [0])) -> (MOVDconst [0]) -(MUL x (MOVDconst [1])) -> x -(MUL x (MOVDconst [c])) && isPowerOfTwo(c) -> (SLLconst [log2(c)] x) -(MUL x (MOVDconst [c])) && isPowerOfTwo(c-1) && c >= 3 -> (ADDshiftLL x x [log2(c-1)]) -(MUL x (MOVDconst [c])) && isPowerOfTwo(c+1) && c >= 7 -> (ADDshiftLL (NEG x) x [log2(c+1)]) -(MUL x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) -> (SLLconst [log2(c/3)] (ADDshiftLL x x [1])) -(MUL x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) -> (SLLconst [log2(c/5)] (ADDshiftLL x x [2])) -(MUL x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) -> (SLLconst [log2(c/7)] (ADDshiftLL (NEG x) x [3])) -(MUL x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) -> (SLLconst [log2(c/9)] (ADDshiftLL x x [3])) - -(MULW x (MOVDconst [c])) && int32(c)==-1 -> (NEG x) -(MULW _ (MOVDconst [c])) && int32(c)==0 -> (MOVDconst [0]) -(MULW x (MOVDconst [c])) && int32(c)==1 -> x -(MULW x (MOVDconst [c])) && isPowerOfTwo(c) -> (SLLconst [log2(c)] x) -(MULW x (MOVDconst [c])) && isPowerOfTwo(c-1) && int32(c) >= 3 -> (ADDshiftLL x x [log2(c-1)]) -(MULW x (MOVDconst [c])) && isPowerOfTwo(c+1) && int32(c) >= 7 -> (ADDshiftLL (NEG x) x [log2(c+1)]) -(MULW x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) -> (SLLconst [log2(c/3)] (ADDshiftLL x x [1])) -(MULW x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) -> (SLLconst [log2(c/5)] (ADDshiftLL x x [2])) -(MULW x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) -> (SLLconst [log2(c/7)] (ADDshiftLL (NEG x) x [3])) -(MULW x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) -> (SLLconst [log2(c/9)] (ADDshiftLL x x [3])) +(MUL x (MOVDconst [-1])) => (NEG x) +(MUL _ (MOVDconst [0])) => (MOVDconst [0]) +(MUL x (MOVDconst [1])) => x +(MUL x (MOVDconst [c])) && isPowerOfTwo(c) => (SLLconst [log2(c)] x) +(MUL x (MOVDconst [c])) && isPowerOfTwo(c-1) && c >= 3 => (ADDshiftLL x x [log2(c-1)]) +(MUL x (MOVDconst [c])) && isPowerOfTwo(c+1) && c >= 7 => (ADDshiftLL (NEG x) x [log2(c+1)]) +(MUL x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) => (SLLconst [log2(c/3)] (ADDshiftLL x x [1])) +(MUL x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) => (SLLconst [log2(c/5)] (ADDshiftLL x x [2])) +(MUL x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) => (SLLconst [log2(c/7)] (ADDshiftLL (NEG x) x [3])) +(MUL x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) => (SLLconst [log2(c/9)] (ADDshiftLL x x [3])) + +(MULW x (MOVDconst [c])) && int32(c)==-1 => (NEG x) +(MULW _ (MOVDconst [c])) && int32(c)==0 => (MOVDconst [0]) +(MULW x (MOVDconst [c])) && int32(c)==1 => x +(MULW x (MOVDconst [c])) && isPowerOfTwo(c) => (SLLconst [log2(c)] x) +(MULW x (MOVDconst [c])) && isPowerOfTwo(c-1) && int32(c) >= 3 => (ADDshiftLL x x [log2(c-1)]) +(MULW x (MOVDconst [c])) && isPowerOfTwo(c+1) && int32(c) >= 7 => (ADDshiftLL (NEG x) x [log2(c+1)]) +(MULW x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) => (SLLconst [log2(c/3)] (ADDshiftLL x x [1])) +(MULW x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) => (SLLconst [log2(c/5)] (ADDshiftLL x x [2])) +(MULW x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) => (SLLconst [log2(c/7)] (ADDshiftLL (NEG x) x [3])) +(MULW x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) => (SLLconst [log2(c/9)] (ADDshiftLL x x [3])) // mneg by constant -(MNEG x (MOVDconst [-1])) -> x -(MNEG _ (MOVDconst [0])) -> (MOVDconst [0]) -(MNEG x (MOVDconst [1])) -> (NEG x) -(MNEG x (MOVDconst [c])) && isPowerOfTwo(c) -> (NEG (SLLconst [log2(c)] x)) -(MNEG x (MOVDconst [c])) && isPowerOfTwo(c-1) && c >= 3 -> (NEG (ADDshiftLL x x [log2(c-1)])) -(MNEG x (MOVDconst [c])) && isPowerOfTwo(c+1) && c >= 7 -> (NEG (ADDshiftLL (NEG x) x [log2(c+1)])) -(MNEG x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) -> (SLLconst [log2(c/3)] (SUBshiftLL x x [2])) -(MNEG x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) -> (NEG (SLLconst [log2(c/5)] (ADDshiftLL x x [2]))) -(MNEG x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) -> (SLLconst [log2(c/7)] (SUBshiftLL x x [3])) -(MNEG x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) -> (NEG (SLLconst [log2(c/9)] (ADDshiftLL x x [3]))) - -(MNEGW x (MOVDconst [c])) && int32(c)==-1 -> x -(MNEGW _ (MOVDconst [c])) && int32(c)==0 -> (MOVDconst [0]) -(MNEGW x (MOVDconst [c])) && int32(c)==1 -> (NEG x) -(MNEGW x (MOVDconst [c])) && isPowerOfTwo(c) -> (NEG (SLLconst [log2(c)] x)) -(MNEGW x (MOVDconst [c])) && isPowerOfTwo(c-1) && int32(c) >= 3 -> (NEG (ADDshiftLL x x [log2(c-1)])) -(MNEGW x (MOVDconst [c])) && isPowerOfTwo(c+1) && int32(c) >= 7 -> (NEG (ADDshiftLL (NEG x) x [log2(c+1)])) -(MNEGW x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) -> (SLLconst [log2(c/3)] (SUBshiftLL x x [2])) -(MNEGW x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) -> (NEG (SLLconst [log2(c/5)] (ADDshiftLL x x [2]))) -(MNEGW x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) -> (SLLconst [log2(c/7)] (SUBshiftLL x x [3])) -(MNEGW x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) -> (NEG (SLLconst [log2(c/9)] (ADDshiftLL x x [3]))) - -(MADD a x (MOVDconst [-1])) -> (SUB a x) -(MADD a _ (MOVDconst [0])) -> a -(MADD a x (MOVDconst [1])) -> (ADD a x) -(MADD a x (MOVDconst [c])) && isPowerOfTwo(c) -> (ADDshiftLL a x [log2(c)]) -(MADD a x (MOVDconst [c])) && isPowerOfTwo(c-1) && c>=3 -> (ADD a (ADDshiftLL x x [log2(c-1)])) -(MADD a x (MOVDconst [c])) && isPowerOfTwo(c+1) && c>=7 -> (SUB a (SUBshiftLL x x [log2(c+1)])) -(MADD a x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) -> (SUBshiftLL a (SUBshiftLL x x [2]) [log2(c/3)]) -(MADD a x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) -> (ADDshiftLL a (ADDshiftLL x x [2]) [log2(c/5)]) -(MADD a x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) -> (SUBshiftLL a (SUBshiftLL x x [3]) [log2(c/7)]) -(MADD a x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) -> (ADDshiftLL a (ADDshiftLL x x [3]) [log2(c/9)]) - -(MADD a (MOVDconst [-1]) x) -> (SUB a x) -(MADD a (MOVDconst [0]) _) -> a -(MADD a (MOVDconst [1]) x) -> (ADD a x) -(MADD a (MOVDconst [c]) x) && isPowerOfTwo(c) -> (ADDshiftLL a x [log2(c)]) -(MADD a (MOVDconst [c]) x) && isPowerOfTwo(c-1) && c>=3 -> (ADD a (ADDshiftLL x x [log2(c-1)])) -(MADD a (MOVDconst [c]) x) && isPowerOfTwo(c+1) && c>=7 -> (SUB a (SUBshiftLL x x [log2(c+1)])) -(MADD a (MOVDconst [c]) x) && c%3 == 0 && isPowerOfTwo(c/3) -> (SUBshiftLL a (SUBshiftLL x x [2]) [log2(c/3)]) -(MADD a (MOVDconst [c]) x) && c%5 == 0 && isPowerOfTwo(c/5) -> (ADDshiftLL a (ADDshiftLL x x [2]) [log2(c/5)]) -(MADD a (MOVDconst [c]) x) && c%7 == 0 && isPowerOfTwo(c/7) -> (SUBshiftLL a (SUBshiftLL x x [3]) [log2(c/7)]) -(MADD a (MOVDconst [c]) x) && c%9 == 0 && isPowerOfTwo(c/9) -> (ADDshiftLL a (ADDshiftLL x x [3]) [log2(c/9)]) - -(MADDW a x (MOVDconst [c])) && int32(c)==-1 -> (SUB a x) -(MADDW a _ (MOVDconst [c])) && int32(c)==0 -> a -(MADDW a x (MOVDconst [c])) && int32(c)==1 -> (ADD a x) -(MADDW a x (MOVDconst [c])) && isPowerOfTwo(c) -> (ADDshiftLL a x [log2(c)]) -(MADDW a x (MOVDconst [c])) && isPowerOfTwo(c-1) && int32(c)>=3 -> (ADD a (ADDshiftLL x x [log2(c-1)])) -(MADDW a x (MOVDconst [c])) && isPowerOfTwo(c+1) && int32(c)>=7 -> (SUB a (SUBshiftLL x x [log2(c+1)])) -(MADDW a x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) -> (SUBshiftLL a (SUBshiftLL x x [2]) [log2(c/3)]) -(MADDW a x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) -> (ADDshiftLL a (ADDshiftLL x x [2]) [log2(c/5)]) -(MADDW a x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) -> (SUBshiftLL a (SUBshiftLL x x [3]) [log2(c/7)]) -(MADDW a x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) -> (ADDshiftLL a (ADDshiftLL x x [3]) [log2(c/9)]) - -(MADDW a (MOVDconst [c]) x) && int32(c)==-1 -> (SUB a x) -(MADDW a (MOVDconst [c]) _) && int32(c)==0 -> a -(MADDW a (MOVDconst [c]) x) && int32(c)==1 -> (ADD a x) -(MADDW a (MOVDconst [c]) x) && isPowerOfTwo(c) -> (ADDshiftLL a x [log2(c)]) -(MADDW a (MOVDconst [c]) x) && isPowerOfTwo(c-1) && int32(c)>=3 -> (ADD a (ADDshiftLL x x [log2(c-1)])) -(MADDW a (MOVDconst [c]) x) && isPowerOfTwo(c+1) && int32(c)>=7 -> (SUB a (SUBshiftLL x x [log2(c+1)])) -(MADDW a (MOVDconst [c]) x) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) -> (SUBshiftLL a (SUBshiftLL x x [2]) [log2(c/3)]) -(MADDW a (MOVDconst [c]) x) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) -> (ADDshiftLL a (ADDshiftLL x x [2]) [log2(c/5)]) -(MADDW a (MOVDconst [c]) x) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) -> (SUBshiftLL a (SUBshiftLL x x [3]) [log2(c/7)]) -(MADDW a (MOVDconst [c]) x) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) -> (ADDshiftLL a (ADDshiftLL x x [3]) [log2(c/9)]) - -(MSUB a x (MOVDconst [-1])) -> (ADD a x) -(MSUB a _ (MOVDconst [0])) -> a -(MSUB a x (MOVDconst [1])) -> (SUB a x) -(MSUB a x (MOVDconst [c])) && isPowerOfTwo(c) -> (SUBshiftLL a x [log2(c)]) -(MSUB a x (MOVDconst [c])) && isPowerOfTwo(c-1) && c>=3 -> (SUB a (ADDshiftLL x x [log2(c-1)])) -(MSUB a x (MOVDconst [c])) && isPowerOfTwo(c+1) && c>=7 -> (ADD a (SUBshiftLL x x [log2(c+1)])) -(MSUB a x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) -> (ADDshiftLL a (SUBshiftLL x x [2]) [log2(c/3)]) -(MSUB a x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) -> (SUBshiftLL a (ADDshiftLL x x [2]) [log2(c/5)]) -(MSUB a x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) -> (ADDshiftLL a (SUBshiftLL x x [3]) [log2(c/7)]) -(MSUB a x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) -> (SUBshiftLL a (ADDshiftLL x x [3]) [log2(c/9)]) - -(MSUB a (MOVDconst [-1]) x) -> (ADD a x) -(MSUB a (MOVDconst [0]) _) -> a -(MSUB a (MOVDconst [1]) x) -> (SUB a x) -(MSUB a (MOVDconst [c]) x) && isPowerOfTwo(c) -> (SUBshiftLL a x [log2(c)]) -(MSUB a (MOVDconst [c]) x) && isPowerOfTwo(c-1) && c>=3 -> (SUB a (ADDshiftLL x x [log2(c-1)])) -(MSUB a (MOVDconst [c]) x) && isPowerOfTwo(c+1) && c>=7 -> (ADD a (SUBshiftLL x x [log2(c+1)])) -(MSUB a (MOVDconst [c]) x) && c%3 == 0 && isPowerOfTwo(c/3) -> (ADDshiftLL a (SUBshiftLL x x [2]) [log2(c/3)]) -(MSUB a (MOVDconst [c]) x) && c%5 == 0 && isPowerOfTwo(c/5) -> (SUBshiftLL a (ADDshiftLL x x [2]) [log2(c/5)]) -(MSUB a (MOVDconst [c]) x) && c%7 == 0 && isPowerOfTwo(c/7) -> (ADDshiftLL a (SUBshiftLL x x [3]) [log2(c/7)]) -(MSUB a (MOVDconst [c]) x) && c%9 == 0 && isPowerOfTwo(c/9) -> (SUBshiftLL a (ADDshiftLL x x [3]) [log2(c/9)]) - -(MSUBW a x (MOVDconst [c])) && int32(c)==-1 -> (ADD a x) -(MSUBW a _ (MOVDconst [c])) && int32(c)==0 -> a -(MSUBW a x (MOVDconst [c])) && int32(c)==1 -> (SUB a x) -(MSUBW a x (MOVDconst [c])) && isPowerOfTwo(c) -> (SUBshiftLL a x [log2(c)]) -(MSUBW a x (MOVDconst [c])) && isPowerOfTwo(c-1) && int32(c)>=3 -> (SUB a (ADDshiftLL x x [log2(c-1)])) -(MSUBW a x (MOVDconst [c])) && isPowerOfTwo(c+1) && int32(c)>=7 -> (ADD a (SUBshiftLL x x [log2(c+1)])) -(MSUBW a x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) -> (ADDshiftLL a (SUBshiftLL x x [2]) [log2(c/3)]) -(MSUBW a x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) -> (SUBshiftLL a (ADDshiftLL x x [2]) [log2(c/5)]) -(MSUBW a x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) -> (ADDshiftLL a (SUBshiftLL x x [3]) [log2(c/7)]) -(MSUBW a x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) -> (SUBshiftLL a (ADDshiftLL x x [3]) [log2(c/9)]) - -(MSUBW a (MOVDconst [c]) x) && int32(c)==-1 -> (ADD a x) -(MSUBW a (MOVDconst [c]) _) && int32(c)==0 -> a -(MSUBW a (MOVDconst [c]) x) && int32(c)==1 -> (SUB a x) -(MSUBW a (MOVDconst [c]) x) && isPowerOfTwo(c) -> (SUBshiftLL a x [log2(c)]) -(MSUBW a (MOVDconst [c]) x) && isPowerOfTwo(c-1) && int32(c)>=3 -> (SUB a (ADDshiftLL x x [log2(c-1)])) -(MSUBW a (MOVDconst [c]) x) && isPowerOfTwo(c+1) && int32(c)>=7 -> (ADD a (SUBshiftLL x x [log2(c+1)])) -(MSUBW a (MOVDconst [c]) x) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) -> (ADDshiftLL a (SUBshiftLL x x [2]) [log2(c/3)]) -(MSUBW a (MOVDconst [c]) x) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) -> (SUBshiftLL a (ADDshiftLL x x [2]) [log2(c/5)]) -(MSUBW a (MOVDconst [c]) x) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) -> (ADDshiftLL a (SUBshiftLL x x [3]) [log2(c/7)]) -(MSUBW a (MOVDconst [c]) x) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) -> (SUBshiftLL a (ADDshiftLL x x [3]) [log2(c/9)]) +(MNEG x (MOVDconst [-1])) => x +(MNEG _ (MOVDconst [0])) => (MOVDconst [0]) +(MNEG x (MOVDconst [1])) => (NEG x) +(MNEG x (MOVDconst [c])) && isPowerOfTwo(c) => (NEG (SLLconst [log2(c)] x)) +(MNEG x (MOVDconst [c])) && isPowerOfTwo(c-1) && c >= 3 => (NEG (ADDshiftLL x x [log2(c-1)])) +(MNEG x (MOVDconst [c])) && isPowerOfTwo(c+1) && c >= 7 => (NEG (ADDshiftLL (NEG x) x [log2(c+1)])) +(MNEG x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) => (SLLconst [log2(c/3)] (SUBshiftLL x x [2])) +(MNEG x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) => (NEG (SLLconst [log2(c/5)] (ADDshiftLL x x [2]))) +(MNEG x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) => (SLLconst [log2(c/7)] (SUBshiftLL x x [3])) +(MNEG x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) => (NEG (SLLconst [log2(c/9)] (ADDshiftLL x x [3]))) + +(MNEGW x (MOVDconst [c])) && int32(c)==-1 => x +(MNEGW _ (MOVDconst [c])) && int32(c)==0 => (MOVDconst [0]) +(MNEGW x (MOVDconst [c])) && int32(c)==1 => (NEG x) +(MNEGW x (MOVDconst [c])) && isPowerOfTwo(c) => (NEG (SLLconst [log2(c)] x)) +(MNEGW x (MOVDconst [c])) && isPowerOfTwo(c-1) && int32(c) >= 3 => (NEG (ADDshiftLL x x [log2(c-1)])) +(MNEGW x (MOVDconst [c])) && isPowerOfTwo(c+1) && int32(c) >= 7 => (NEG (ADDshiftLL (NEG x) x [log2(c+1)])) +(MNEGW x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) => (SLLconst [log2(c/3)] (SUBshiftLL x x [2])) +(MNEGW x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) => (NEG (SLLconst [log2(c/5)] (ADDshiftLL x x [2]))) +(MNEGW x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) => (SLLconst [log2(c/7)] (SUBshiftLL x x [3])) +(MNEGW x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) => (NEG (SLLconst [log2(c/9)] (ADDshiftLL x x [3]))) + +(MADD a x (MOVDconst [-1])) => (SUB a x) +(MADD a _ (MOVDconst [0])) => a +(MADD a x (MOVDconst [1])) => (ADD a x) +(MADD a x (MOVDconst [c])) && isPowerOfTwo(c) => (ADDshiftLL a x [log2(c)]) +(MADD a x (MOVDconst [c])) && isPowerOfTwo(c-1) && c>=3 => (ADD a (ADDshiftLL x x [log2(c-1)])) +(MADD a x (MOVDconst [c])) && isPowerOfTwo(c+1) && c>=7 => (SUB a (SUBshiftLL x x [log2(c+1)])) +(MADD a x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) => (SUBshiftLL a (SUBshiftLL x x [2]) [log2(c/3)]) +(MADD a x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) => (ADDshiftLL a (ADDshiftLL x x [2]) [log2(c/5)]) +(MADD a x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) => (SUBshiftLL a (SUBshiftLL x x [3]) [log2(c/7)]) +(MADD a x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) => (ADDshiftLL a (ADDshiftLL x x [3]) [log2(c/9)]) + +(MADD a (MOVDconst [-1]) x) => (SUB a x) +(MADD a (MOVDconst [0]) _) => a +(MADD a (MOVDconst [1]) x) => (ADD a x) +(MADD a (MOVDconst [c]) x) && isPowerOfTwo(c) => (ADDshiftLL a x [log2(c)]) +(MADD a (MOVDconst [c]) x) && isPowerOfTwo(c-1) && c>=3 => (ADD a (ADDshiftLL x x [log2(c-1)])) +(MADD a (MOVDconst [c]) x) && isPowerOfTwo(c+1) && c>=7 => (SUB a (SUBshiftLL x x [log2(c+1)])) +(MADD a (MOVDconst [c]) x) && c%3 == 0 && isPowerOfTwo(c/3) => (SUBshiftLL a (SUBshiftLL x x [2]) [log2(c/3)]) +(MADD a (MOVDconst [c]) x) && c%5 == 0 && isPowerOfTwo(c/5) => (ADDshiftLL a (ADDshiftLL x x [2]) [log2(c/5)]) +(MADD a (MOVDconst [c]) x) && c%7 == 0 && isPowerOfTwo(c/7) => (SUBshiftLL a (SUBshiftLL x x [3]) [log2(c/7)]) +(MADD a (MOVDconst [c]) x) && c%9 == 0 && isPowerOfTwo(c/9) => (ADDshiftLL a (ADDshiftLL x x [3]) [log2(c/9)]) + +(MADDW a x (MOVDconst [c])) && int32(c)==-1 => (SUB a x) +(MADDW a _ (MOVDconst [c])) && int32(c)==0 => a +(MADDW a x (MOVDconst [c])) && int32(c)==1 => (ADD a x) +(MADDW a x (MOVDconst [c])) && isPowerOfTwo(c) => (ADDshiftLL a x [log2(c)]) +(MADDW a x (MOVDconst [c])) && isPowerOfTwo(c-1) && int32(c)>=3 => (ADD a (ADDshiftLL x x [log2(c-1)])) +(MADDW a x (MOVDconst [c])) && isPowerOfTwo(c+1) && int32(c)>=7 => (SUB a (SUBshiftLL x x [log2(c+1)])) +(MADDW a x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) => (SUBshiftLL a (SUBshiftLL x x [2]) [log2(c/3)]) +(MADDW a x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) => (ADDshiftLL a (ADDshiftLL x x [2]) [log2(c/5)]) +(MADDW a x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) => (SUBshiftLL a (SUBshiftLL x x [3]) [log2(c/7)]) +(MADDW a x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) => (ADDshiftLL a (ADDshiftLL x x [3]) [log2(c/9)]) + +(MADDW a (MOVDconst [c]) x) && int32(c)==-1 => (SUB a x) +(MADDW a (MOVDconst [c]) _) && int32(c)==0 => a +(MADDW a (MOVDconst [c]) x) && int32(c)==1 => (ADD a x) +(MADDW a (MOVDconst [c]) x) && isPowerOfTwo(c) => (ADDshiftLL a x [log2(c)]) +(MADDW a (MOVDconst [c]) x) && isPowerOfTwo(c-1) && int32(c)>=3 => (ADD a (ADDshiftLL x x [log2(c-1)])) +(MADDW a (MOVDconst [c]) x) && isPowerOfTwo(c+1) && int32(c)>=7 => (SUB a (SUBshiftLL x x [log2(c+1)])) +(MADDW a (MOVDconst [c]) x) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) => (SUBshiftLL a (SUBshiftLL x x [2]) [log2(c/3)]) +(MADDW a (MOVDconst [c]) x) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) => (ADDshiftLL a (ADDshiftLL x x [2]) [log2(c/5)]) +(MADDW a (MOVDconst [c]) x) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) => (SUBshiftLL a (SUBshiftLL x x [3]) [log2(c/7)]) +(MADDW a (MOVDconst [c]) x) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) => (ADDshiftLL a (ADDshiftLL x x [3]) [log2(c/9)]) + +(MSUB a x (MOVDconst [-1])) => (ADD a x) +(MSUB a _ (MOVDconst [0])) => a +(MSUB a x (MOVDconst [1])) => (SUB a x) +(MSUB a x (MOVDconst [c])) && isPowerOfTwo(c) => (SUBshiftLL a x [log2(c)]) +(MSUB a x (MOVDconst [c])) && isPowerOfTwo(c-1) && c>=3 => (SUB a (ADDshiftLL x x [log2(c-1)])) +(MSUB a x (MOVDconst [c])) && isPowerOfTwo(c+1) && c>=7 => (ADD a (SUBshiftLL x x [log2(c+1)])) +(MSUB a x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) => (ADDshiftLL a (SUBshiftLL x x [2]) [log2(c/3)]) +(MSUB a x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) => (SUBshiftLL a (ADDshiftLL x x [2]) [log2(c/5)]) +(MSUB a x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) => (ADDshiftLL a (SUBshiftLL x x [3]) [log2(c/7)]) +(MSUB a x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) => (SUBshiftLL a (ADDshiftLL x x [3]) [log2(c/9)]) + +(MSUB a (MOVDconst [-1]) x) => (ADD a x) +(MSUB a (MOVDconst [0]) _) => a +(MSUB a (MOVDconst [1]) x) => (SUB a x) +(MSUB a (MOVDconst [c]) x) && isPowerOfTwo(c) => (SUBshiftLL a x [log2(c)]) +(MSUB a (MOVDconst [c]) x) && isPowerOfTwo(c-1) && c>=3 => (SUB a (ADDshiftLL x x [log2(c-1)])) +(MSUB a (MOVDconst [c]) x) && isPowerOfTwo(c+1) && c>=7 => (ADD a (SUBshiftLL x x [log2(c+1)])) +(MSUB a (MOVDconst [c]) x) && c%3 == 0 && isPowerOfTwo(c/3) => (ADDshiftLL a (SUBshiftLL x x [2]) [log2(c/3)]) +(MSUB a (MOVDconst [c]) x) && c%5 == 0 && isPowerOfTwo(c/5) => (SUBshiftLL a (ADDshiftLL x x [2]) [log2(c/5)]) +(MSUB a (MOVDconst [c]) x) && c%7 == 0 && isPowerOfTwo(c/7) => (ADDshiftLL a (SUBshiftLL x x [3]) [log2(c/7)]) +(MSUB a (MOVDconst [c]) x) && c%9 == 0 && isPowerOfTwo(c/9) => (SUBshiftLL a (ADDshiftLL x x [3]) [log2(c/9)]) + +(MSUBW a x (MOVDconst [c])) && int32(c)==-1 => (ADD a x) +(MSUBW a _ (MOVDconst [c])) && int32(c)==0 => a +(MSUBW a x (MOVDconst [c])) && int32(c)==1 => (SUB a x) +(MSUBW a x (MOVDconst [c])) && isPowerOfTwo(c) => (SUBshiftLL a x [log2(c)]) +(MSUBW a x (MOVDconst [c])) && isPowerOfTwo(c-1) && int32(c)>=3 => (SUB a (ADDshiftLL x x [log2(c-1)])) +(MSUBW a x (MOVDconst [c])) && isPowerOfTwo(c+1) && int32(c)>=7 => (ADD a (SUBshiftLL x x [log2(c+1)])) +(MSUBW a x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) => (ADDshiftLL a (SUBshiftLL x x [2]) [log2(c/3)]) +(MSUBW a x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) => (SUBshiftLL a (ADDshiftLL x x [2]) [log2(c/5)]) +(MSUBW a x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) => (ADDshiftLL a (SUBshiftLL x x [3]) [log2(c/7)]) +(MSUBW a x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) => (SUBshiftLL a (ADDshiftLL x x [3]) [log2(c/9)]) + +(MSUBW a (MOVDconst [c]) x) && int32(c)==-1 => (ADD a x) +(MSUBW a (MOVDconst [c]) _) && int32(c)==0 => a +(MSUBW a (MOVDconst [c]) x) && int32(c)==1 => (SUB a x) +(MSUBW a (MOVDconst [c]) x) && isPowerOfTwo(c) => (SUBshiftLL a x [log2(c)]) +(MSUBW a (MOVDconst [c]) x) && isPowerOfTwo(c-1) && int32(c)>=3 => (SUB a (ADDshiftLL x x [log2(c-1)])) +(MSUBW a (MOVDconst [c]) x) && isPowerOfTwo(c+1) && int32(c)>=7 => (ADD a (SUBshiftLL x x [log2(c+1)])) +(MSUBW a (MOVDconst [c]) x) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) => (ADDshiftLL a (SUBshiftLL x x [2]) [log2(c/3)]) +(MSUBW a (MOVDconst [c]) x) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) => (SUBshiftLL a (ADDshiftLL x x [2]) [log2(c/5)]) +(MSUBW a (MOVDconst [c]) x) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) => (ADDshiftLL a (SUBshiftLL x x [3]) [log2(c/7)]) +(MSUBW a (MOVDconst [c]) x) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) => (SUBshiftLL a (ADDshiftLL x x [3]) [log2(c/9)]) // div by constant -(UDIV x (MOVDconst [1])) -> x -(UDIV x (MOVDconst [c])) && isPowerOfTwo(c) -> (SRLconst [log2(c)] x) -(UDIVW x (MOVDconst [c])) && uint32(c)==1 -> x -(UDIVW x (MOVDconst [c])) && isPowerOfTwo(c) && is32Bit(c) -> (SRLconst [log2(c)] x) -(UMOD _ (MOVDconst [1])) -> (MOVDconst [0]) -(UMOD x (MOVDconst [c])) && isPowerOfTwo(c) -> (ANDconst [c-1] x) -(UMODW _ (MOVDconst [c])) && uint32(c)==1 -> (MOVDconst [0]) -(UMODW x (MOVDconst [c])) && isPowerOfTwo(c) && is32Bit(c) -> (ANDconst [c-1] x) +(UDIV x (MOVDconst [1])) => x +(UDIV x (MOVDconst [c])) && isPowerOfTwo(c) => (SRLconst [log2(c)] x) +(UDIVW x (MOVDconst [c])) && uint32(c)==1 => x +(UDIVW x (MOVDconst [c])) && isPowerOfTwo(c) && is32Bit(c) => (SRLconst [log2(c)] x) +(UMOD _ (MOVDconst [1])) => (MOVDconst [0]) +(UMOD x (MOVDconst [c])) && isPowerOfTwo(c) => (ANDconst [c-1] x) +(UMODW _ (MOVDconst [c])) && uint32(c)==1 => (MOVDconst [0]) +(UMODW x (MOVDconst [c])) && isPowerOfTwo(c) && is32Bit(c) => (ANDconst [c-1] x) // generic simplifications -(ADD x (NEG y)) -> (SUB x y) -(SUB x x) -> (MOVDconst [0]) -(AND x x) -> x -(OR x x) -> x -(XOR x x) -> (MOVDconst [0]) -(BIC x x) -> (MOVDconst [0]) -(EON x x) -> (MOVDconst [-1]) -(ORN x x) -> (MOVDconst [-1]) -(AND x (MVN y)) -> (BIC x y) -(XOR x (MVN y)) -> (EON x y) -(OR x (MVN y)) -> (ORN x y) -(MVN (XOR x y)) -> (EON x y) +(ADD x (NEG y)) => (SUB x y) +(SUB x x) => (MOVDconst [0]) +(AND x x) => x +(OR x x) => x +(XOR x x) => (MOVDconst [0]) +(BIC x x) => (MOVDconst [0]) +(EON x x) => (MOVDconst [-1]) +(ORN x x) => (MOVDconst [-1]) +(AND x (MVN y)) => (BIC x y) +(XOR x (MVN y)) => (EON x y) +(OR x (MVN y)) => (ORN x y) +(MVN (XOR x y)) => (EON x y) (CSEL [cc] x (MOVDconst [0]) flag) => (CSEL0 [cc] x flag) (CSEL [cc] (MOVDconst [0]) y flag) => (CSEL0 [arm64Negate(cc)] y flag) -(SUB x (SUB y z)) -> (SUB (ADD x z) y) -(SUB (SUB x y) z) -> (SUB x (ADD y z)) +(SUB x (SUB y z)) => (SUB (ADD x z) y) +(SUB (SUB x y) z) => (SUB x (ADD y z)) // remove redundant *const ops -(ADDconst [0] x) -> x -(SUBconst [0] x) -> x -(ANDconst [0] _) -> (MOVDconst [0]) -(ANDconst [-1] x) -> x -(ORconst [0] x) -> x -(ORconst [-1] _) -> (MOVDconst [-1]) -(XORconst [0] x) -> x -(XORconst [-1] x) -> (MVN x) +(ADDconst [0] x) => x +(SUBconst [0] x) => x +(ANDconst [0] _) => (MOVDconst [0]) +(ANDconst [-1] x) => x +(ORconst [0] x) => x +(ORconst [-1] _) => (MOVDconst [-1]) +(XORconst [0] x) => x +(XORconst [-1] x) => (MVN x) // generic constant folding -(ADDconst [c] (MOVDconst [d])) -> (MOVDconst [c+d]) -(ADDconst [c] (ADDconst [d] x)) -> (ADDconst [c+d] x) -(ADDconst [c] (SUBconst [d] x)) -> (ADDconst [c-d] x) -(SUBconst [c] (MOVDconst [d])) -> (MOVDconst [d-c]) -(SUBconst [c] (SUBconst [d] x)) -> (ADDconst [-c-d] x) -(SUBconst [c] (ADDconst [d] x)) -> (ADDconst [-c+d] x) -(SLLconst [c] (MOVDconst [d])) -> (MOVDconst [d< (MOVDconst [int64(uint64(d)>>uint64(c))]) -(SRAconst [c] (MOVDconst [d])) -> (MOVDconst [d>>uint64(c)]) -(MUL (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [c*d]) -(MULW (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [int64(int32(c)*int32(d))]) -(MNEG (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [-c*d]) -(MNEGW (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [-int64(int32(c)*int32(d))]) -(MADD (MOVDconst [c]) x y) -> (ADDconst [c] (MUL x y)) -(MADDW (MOVDconst [c]) x y) -> (ADDconst [c] (MULW x y)) -(MSUB (MOVDconst [c]) x y) -> (ADDconst [c] (MNEG x y)) -(MSUBW (MOVDconst [c]) x y) -> (ADDconst [c] (MNEGW x y)) -(MADD a (MOVDconst [c]) (MOVDconst [d])) -> (ADDconst [c*d] a) -(MADDW a (MOVDconst [c]) (MOVDconst [d])) -> (ADDconst [int64(int32(c)*int32(d))] a) -(MSUB a (MOVDconst [c]) (MOVDconst [d])) -> (SUBconst [c*d] a) -(MSUBW a (MOVDconst [c]) (MOVDconst [d])) -> (SUBconst [int64(int32(c)*int32(d))] a) -(DIV (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [c/d]) -(UDIV (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [int64(uint64(c)/uint64(d))]) -(DIVW (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [int64(int32(c)/int32(d))]) -(UDIVW (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [int64(uint32(c)/uint32(d))]) -(MOD (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [c%d]) -(UMOD (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [int64(uint64(c)%uint64(d))]) -(MODW (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [int64(int32(c)%int32(d))]) -(UMODW (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [int64(uint32(c)%uint32(d))]) -(ANDconst [c] (MOVDconst [d])) -> (MOVDconst [c&d]) -(ANDconst [c] (ANDconst [d] x)) -> (ANDconst [c&d] x) -(ANDconst [c] (MOVWUreg x)) -> (ANDconst [c&(1<<32-1)] x) -(ANDconst [c] (MOVHUreg x)) -> (ANDconst [c&(1<<16-1)] x) -(ANDconst [c] (MOVBUreg x)) -> (ANDconst [c&(1<<8-1)] x) -(MOVWUreg (ANDconst [c] x)) -> (ANDconst [c&(1<<32-1)] x) -(MOVHUreg (ANDconst [c] x)) -> (ANDconst [c&(1<<16-1)] x) -(MOVBUreg (ANDconst [c] x)) -> (ANDconst [c&(1<<8-1)] x) -(ORconst [c] (MOVDconst [d])) -> (MOVDconst [c|d]) -(ORconst [c] (ORconst [d] x)) -> (ORconst [c|d] x) -(XORconst [c] (MOVDconst [d])) -> (MOVDconst [c^d]) -(XORconst [c] (XORconst [d] x)) -> (XORconst [c^d] x) -(MVN (MOVDconst [c])) -> (MOVDconst [^c]) -(NEG (MOVDconst [c])) -> (MOVDconst [-c]) -(MOVBreg (MOVDconst [c])) -> (MOVDconst [int64(int8(c))]) -(MOVBUreg (MOVDconst [c])) -> (MOVDconst [int64(uint8(c))]) -(MOVHreg (MOVDconst [c])) -> (MOVDconst [int64(int16(c))]) -(MOVHUreg (MOVDconst [c])) -> (MOVDconst [int64(uint16(c))]) -(MOVWreg (MOVDconst [c])) -> (MOVDconst [int64(int32(c))]) -(MOVWUreg (MOVDconst [c])) -> (MOVDconst [int64(uint32(c))]) -(MOVDreg (MOVDconst [c])) -> (MOVDconst [c]) +(ADDconst [c] (MOVDconst [d])) => (MOVDconst [c+d]) +(ADDconst [c] (ADDconst [d] x)) => (ADDconst [c+d] x) +(ADDconst [c] (SUBconst [d] x)) => (ADDconst [c-d] x) +(SUBconst [c] (MOVDconst [d])) => (MOVDconst [d-c]) +(SUBconst [c] (SUBconst [d] x)) => (ADDconst [-c-d] x) +(SUBconst [c] (ADDconst [d] x)) => (ADDconst [-c+d] x) +(SLLconst [c] (MOVDconst [d])) => (MOVDconst [d< (MOVDconst [int64(uint64(d)>>uint64(c))]) +(SRAconst [c] (MOVDconst [d])) => (MOVDconst [d>>uint64(c)]) +(MUL (MOVDconst [c]) (MOVDconst [d])) => (MOVDconst [c*d]) +(MULW (MOVDconst [c]) (MOVDconst [d])) => (MOVDconst [int64(int32(c)*int32(d))]) +(MNEG (MOVDconst [c]) (MOVDconst [d])) => (MOVDconst [-c*d]) +(MNEGW (MOVDconst [c]) (MOVDconst [d])) => (MOVDconst [-int64(int32(c)*int32(d))]) +(MADD (MOVDconst [c]) x y) => (ADDconst [c] (MUL x y)) +(MADDW (MOVDconst [c]) x y) => (ADDconst [c] (MULW x y)) +(MSUB (MOVDconst [c]) x y) => (ADDconst [c] (MNEG x y)) +(MSUBW (MOVDconst [c]) x y) => (ADDconst [c] (MNEGW x y)) +(MADD a (MOVDconst [c]) (MOVDconst [d])) => (ADDconst [c*d] a) +(MADDW a (MOVDconst [c]) (MOVDconst [d])) => (ADDconst [int64(int32(c)*int32(d))] a) +(MSUB a (MOVDconst [c]) (MOVDconst [d])) => (SUBconst [c*d] a) +(MSUBW a (MOVDconst [c]) (MOVDconst [d])) => (SUBconst [int64(int32(c)*int32(d))] a) +(DIV (MOVDconst [c]) (MOVDconst [d])) => (MOVDconst [c/d]) +(UDIV (MOVDconst [c]) (MOVDconst [d])) => (MOVDconst [int64(uint64(c)/uint64(d))]) +(DIVW (MOVDconst [c]) (MOVDconst [d])) => (MOVDconst [int64(int32(c)/int32(d))]) +(UDIVW (MOVDconst [c]) (MOVDconst [d])) => (MOVDconst [int64(uint32(c)/uint32(d))]) +(MOD (MOVDconst [c]) (MOVDconst [d])) => (MOVDconst [c%d]) +(UMOD (MOVDconst [c]) (MOVDconst [d])) => (MOVDconst [int64(uint64(c)%uint64(d))]) +(MODW (MOVDconst [c]) (MOVDconst [d])) => (MOVDconst [int64(int32(c)%int32(d))]) +(UMODW (MOVDconst [c]) (MOVDconst [d])) => (MOVDconst [int64(uint32(c)%uint32(d))]) +(ANDconst [c] (MOVDconst [d])) => (MOVDconst [c&d]) +(ANDconst [c] (ANDconst [d] x)) => (ANDconst [c&d] x) +(ANDconst [c] (MOVWUreg x)) => (ANDconst [c&(1<<32-1)] x) +(ANDconst [c] (MOVHUreg x)) => (ANDconst [c&(1<<16-1)] x) +(ANDconst [c] (MOVBUreg x)) => (ANDconst [c&(1<<8-1)] x) +(MOVWUreg (ANDconst [c] x)) => (ANDconst [c&(1<<32-1)] x) +(MOVHUreg (ANDconst [c] x)) => (ANDconst [c&(1<<16-1)] x) +(MOVBUreg (ANDconst [c] x)) => (ANDconst [c&(1<<8-1)] x) +(ORconst [c] (MOVDconst [d])) => (MOVDconst [c|d]) +(ORconst [c] (ORconst [d] x)) => (ORconst [c|d] x) +(XORconst [c] (MOVDconst [d])) => (MOVDconst [c^d]) +(XORconst [c] (XORconst [d] x)) => (XORconst [c^d] x) +(MVN (MOVDconst [c])) => (MOVDconst [^c]) +(NEG (MOVDconst [c])) => (MOVDconst [-c]) +(MOVBreg (MOVDconst [c])) => (MOVDconst [int64(int8(c))]) +(MOVBUreg (MOVDconst [c])) => (MOVDconst [int64(uint8(c))]) +(MOVHreg (MOVDconst [c])) => (MOVDconst [int64(int16(c))]) +(MOVHUreg (MOVDconst [c])) => (MOVDconst [int64(uint16(c))]) +(MOVWreg (MOVDconst [c])) => (MOVDconst [int64(int32(c))]) +(MOVWUreg (MOVDconst [c])) => (MOVDconst [int64(uint32(c))]) +(MOVDreg (MOVDconst [c])) => (MOVDconst [c]) // constant comparisons (CMPconst (MOVDconst [x]) [y]) => (FlagConstant [subFlags64(x,y)]) diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go index 0fb86b6bdd..6c48812121 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM64.go +++ b/src/cmd/compile/internal/ssa/rewriteARM64.go @@ -1065,7 +1065,7 @@ func rewriteValueARM64_OpARM64ADCSflags(v *Value) bool { break } v_2_0 := v_2.Args[0] - if v_2_0.Op != OpARM64ADDSconstflags || v_2_0.AuxInt != -1 { + if v_2_0.Op != OpARM64ADDSconstflags || auxIntToInt64(v_2_0.AuxInt) != -1 { break } v_2_0_0 := v_2_0.Args[0] @@ -1086,11 +1086,11 @@ func rewriteValueARM64_OpARM64ADCSflags(v *Value) bool { break } v_2_0 := v_2.Args[0] - if v_2_0.Op != OpARM64ADDSconstflags || v_2_0.AuxInt != -1 { + if v_2_0.Op != OpARM64ADDSconstflags || auxIntToInt64(v_2_0.AuxInt) != -1 { break } v_2_0_0 := v_2_0.Args[0] - if v_2_0_0.Op != OpARM64MOVDconst || v_2_0_0.AuxInt != 0 { + if v_2_0_0.Op != OpARM64MOVDconst || auxIntToInt64(v_2_0_0.AuxInt) != 0 { break } v.reset(OpARM64ADDSflags) @@ -1112,9 +1112,9 @@ func rewriteValueARM64_OpARM64ADD(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64ADDconst) - v.AuxInt = c + v.AuxInt = int64ToAuxInt(c) v.AddArg(x) return true } @@ -1593,7 +1593,7 @@ func rewriteValueARM64_OpARM64ADDconst(v *Value) bool { // match: (ADDconst [0] x) // result: x for { - if v.AuxInt != 0 { + if auxIntToInt64(v.AuxInt) != 0 { break } x := v_0 @@ -1603,40 +1603,40 @@ func rewriteValueARM64_OpARM64ADDconst(v *Value) bool { // match: (ADDconst [c] (MOVDconst [d])) // result: (MOVDconst [c+d]) for { - c := v.AuxInt + c := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64MOVDconst { break } - d := v_0.AuxInt + d := auxIntToInt64(v_0.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = c + d + v.AuxInt = int64ToAuxInt(c + d) return true } // match: (ADDconst [c] (ADDconst [d] x)) // result: (ADDconst [c+d] x) for { - c := v.AuxInt + c := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64ADDconst { break } - d := v_0.AuxInt + d := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] v.reset(OpARM64ADDconst) - v.AuxInt = c + d + v.AuxInt = int64ToAuxInt(c + d) v.AddArg(x) return true } // match: (ADDconst [c] (SUBconst [d] x)) // result: (ADDconst [c-d] x) for { - c := v.AuxInt + c := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64SUBconst { break } - d := v_0.AuxInt + d := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] v.reset(OpARM64ADDconst) - v.AuxInt = c - d + v.AuxInt = int64ToAuxInt(c - d) v.AddArg(x) return true } @@ -1882,9 +1882,9 @@ func rewriteValueARM64_OpARM64AND(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64ANDconst) - v.AuxInt = c + v.AuxInt = int64ToAuxInt(c) v.AddArg(x) return true } @@ -1988,17 +1988,17 @@ func rewriteValueARM64_OpARM64ANDconst(v *Value) bool { // match: (ANDconst [0] _) // result: (MOVDconst [0]) for { - if v.AuxInt != 0 { + if auxIntToInt64(v.AuxInt) != 0 { break } v.reset(OpARM64MOVDconst) - v.AuxInt = 0 + v.AuxInt = int64ToAuxInt(0) return true } // match: (ANDconst [-1] x) // result: x for { - if v.AuxInt != -1 { + if auxIntToInt64(v.AuxInt) != -1 { break } x := v_0 @@ -2008,65 +2008,65 @@ func rewriteValueARM64_OpARM64ANDconst(v *Value) bool { // match: (ANDconst [c] (MOVDconst [d])) // result: (MOVDconst [c&d]) for { - c := v.AuxInt + c := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64MOVDconst { break } - d := v_0.AuxInt + d := auxIntToInt64(v_0.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = c & d + v.AuxInt = int64ToAuxInt(c & d) return true } // match: (ANDconst [c] (ANDconst [d] x)) // result: (ANDconst [c&d] x) for { - c := v.AuxInt + c := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64ANDconst { break } - d := v_0.AuxInt + d := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] v.reset(OpARM64ANDconst) - v.AuxInt = c & d + v.AuxInt = int64ToAuxInt(c & d) v.AddArg(x) return true } // match: (ANDconst [c] (MOVWUreg x)) // result: (ANDconst [c&(1<<32-1)] x) for { - c := v.AuxInt + c := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64MOVWUreg { break } x := v_0.Args[0] v.reset(OpARM64ANDconst) - v.AuxInt = c & (1<<32 - 1) + v.AuxInt = int64ToAuxInt(c & (1<<32 - 1)) v.AddArg(x) return true } // match: (ANDconst [c] (MOVHUreg x)) // result: (ANDconst [c&(1<<16-1)] x) for { - c := v.AuxInt + c := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64MOVHUreg { break } x := v_0.Args[0] v.reset(OpARM64ANDconst) - v.AuxInt = c & (1<<16 - 1) + v.AuxInt = int64ToAuxInt(c & (1<<16 - 1)) v.AddArg(x) return true } // match: (ANDconst [c] (MOVBUreg x)) // result: (ANDconst [c&(1<<8-1)] x) for { - c := v.AuxInt + c := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64MOVBUreg { break } x := v_0.Args[0] v.reset(OpARM64ANDconst) - v.AuxInt = c & (1<<8 - 1) + v.AuxInt = int64ToAuxInt(c & (1<<8 - 1)) v.AddArg(x) return true } @@ -2280,9 +2280,9 @@ func rewriteValueARM64_OpARM64BIC(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64ANDconst) - v.AuxInt = ^c + v.AuxInt = int64ToAuxInt(^c) v.AddArg(x) return true } @@ -2294,7 +2294,7 @@ func rewriteValueARM64_OpARM64BIC(v *Value) bool { break } v.reset(OpARM64MOVDconst) - v.AuxInt = 0 + v.AuxInt = int64ToAuxInt(0) return true } // match: (BIC x0 x1:(SLLconst [c] y)) @@ -2475,9 +2475,9 @@ func rewriteValueARM64_OpARM64CMN(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64CMNconst) - v.AuxInt = c + v.AuxInt = int64ToAuxInt(c) v.AddArg(x) return true } @@ -2555,16 +2555,16 @@ func rewriteValueARM64_OpARM64CMNW(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (CMNW x (MOVDconst [c])) - // result: (CMNWconst [c] x) + // result: (CMNWconst [int32(c)] x) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { x := v_0 if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64CMNWconst) - v.AuxInt = c + v.AuxInt = int32ToAuxInt(int32(c)) v.AddArg(x) return true } @@ -2726,9 +2726,9 @@ func rewriteValueARM64_OpARM64CMP(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64CMPconst) - v.AuxInt = c + v.AuxInt = int64ToAuxInt(c) v.AddArg(x) return true } @@ -2738,11 +2738,11 @@ func rewriteValueARM64_OpARM64CMP(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) x := v_1 v.reset(OpARM64InvertFlags) v0 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v0.AuxInt = c + v0.AuxInt = int64ToAuxInt(c) v0.AddArg(x) v.AddArg(v0) return true @@ -3377,13 +3377,13 @@ func rewriteValueARM64_OpARM64DIV(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) if v_1.Op != OpARM64MOVDconst { break } - d := v_1.AuxInt + d := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = c / d + v.AuxInt = int64ToAuxInt(c / d) return true } return false @@ -3397,13 +3397,13 @@ func rewriteValueARM64_OpARM64DIVW(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) if v_1.Op != OpARM64MOVDconst { break } - d := v_1.AuxInt + d := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = int64(int32(c) / int32(d)) + v.AuxInt = int64ToAuxInt(int64(int32(c) / int32(d))) return true } return false @@ -3418,9 +3418,9 @@ func rewriteValueARM64_OpARM64EON(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64XORconst) - v.AuxInt = ^c + v.AuxInt = int64ToAuxInt(^c) v.AddArg(x) return true } @@ -3432,7 +3432,7 @@ func rewriteValueARM64_OpARM64EON(v *Value) bool { break } v.reset(OpARM64MOVDconst) - v.AuxInt = -1 + v.AuxInt = int64ToAuxInt(-1) return true } // match: (EON x0 x1:(SLLconst [c] y)) @@ -4858,7 +4858,7 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { for { a := v_0 x := v_1 - if v_2.Op != OpARM64MOVDconst || v_2.AuxInt != -1 { + if v_2.Op != OpARM64MOVDconst || auxIntToInt64(v_2.AuxInt) != -1 { break } v.reset(OpARM64SUB) @@ -4869,7 +4869,7 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { // result: a for { a := v_0 - if v_2.Op != OpARM64MOVDconst || v_2.AuxInt != 0 { + if v_2.Op != OpARM64MOVDconst || auxIntToInt64(v_2.AuxInt) != 0 { break } v.copyOf(a) @@ -4880,7 +4880,7 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { for { a := v_0 x := v_1 - if v_2.Op != OpARM64MOVDconst || v_2.AuxInt != 1 { + if v_2.Op != OpARM64MOVDconst || auxIntToInt64(v_2.AuxInt) != 1 { break } v.reset(OpARM64ADD) @@ -4896,12 +4896,12 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(isPowerOfTwo(c)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c) + v.AuxInt = int64ToAuxInt(log2(c)) v.AddArg2(a, x) return true } @@ -4914,13 +4914,13 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(isPowerOfTwo(c-1) && c >= 3) { break } v.reset(OpARM64ADD) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = log2(c - 1) + v0.AuxInt = int64ToAuxInt(log2(c - 1)) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -4934,13 +4934,13 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(isPowerOfTwo(c+1) && c >= 7) { break } v.reset(OpARM64SUB) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = log2(c + 1) + v0.AuxInt = int64ToAuxInt(log2(c + 1)) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -4954,14 +4954,14 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(c%3 == 0 && isPowerOfTwo(c/3)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c / 3) + v.AuxInt = int64ToAuxInt(log2(c / 3)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -4975,14 +4975,14 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(c%5 == 0 && isPowerOfTwo(c/5)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c / 5) + v.AuxInt = int64ToAuxInt(log2(c / 5)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -4996,14 +4996,14 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(c%7 == 0 && isPowerOfTwo(c/7)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c / 7) + v.AuxInt = int64ToAuxInt(log2(c / 7)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5017,14 +5017,14 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(c%9 == 0 && isPowerOfTwo(c/9)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c / 9) + v.AuxInt = int64ToAuxInt(log2(c / 9)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5033,7 +5033,7 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { // result: (SUB a x) for { a := v_0 - if v_1.Op != OpARM64MOVDconst || v_1.AuxInt != -1 { + if v_1.Op != OpARM64MOVDconst || auxIntToInt64(v_1.AuxInt) != -1 { break } x := v_2 @@ -5045,7 +5045,7 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { // result: a for { a := v_0 - if v_1.Op != OpARM64MOVDconst || v_1.AuxInt != 0 { + if v_1.Op != OpARM64MOVDconst || auxIntToInt64(v_1.AuxInt) != 0 { break } v.copyOf(a) @@ -5055,7 +5055,7 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { // result: (ADD a x) for { a := v_0 - if v_1.Op != OpARM64MOVDconst || v_1.AuxInt != 1 { + if v_1.Op != OpARM64MOVDconst || auxIntToInt64(v_1.AuxInt) != 1 { break } x := v_2 @@ -5071,13 +5071,13 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(isPowerOfTwo(c)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c) + v.AuxInt = int64ToAuxInt(log2(c)) v.AddArg2(a, x) return true } @@ -5089,14 +5089,14 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(isPowerOfTwo(c-1) && c >= 3) { break } v.reset(OpARM64ADD) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = log2(c - 1) + v0.AuxInt = int64ToAuxInt(log2(c - 1)) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5109,14 +5109,14 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(isPowerOfTwo(c+1) && c >= 7) { break } v.reset(OpARM64SUB) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = log2(c + 1) + v0.AuxInt = int64ToAuxInt(log2(c + 1)) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5129,15 +5129,15 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(c%3 == 0 && isPowerOfTwo(c/3)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c / 3) + v.AuxInt = int64ToAuxInt(log2(c / 3)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5150,15 +5150,15 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(c%5 == 0 && isPowerOfTwo(c/5)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c / 5) + v.AuxInt = int64ToAuxInt(log2(c / 5)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5171,15 +5171,15 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(c%7 == 0 && isPowerOfTwo(c/7)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c / 7) + v.AuxInt = int64ToAuxInt(log2(c / 7)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5192,15 +5192,15 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(c%9 == 0 && isPowerOfTwo(c/9)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c / 9) + v.AuxInt = int64ToAuxInt(log2(c / 9)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5211,11 +5211,11 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) x := v_1 y := v_2 v.reset(OpARM64ADDconst) - v.AuxInt = c + v.AuxInt = int64ToAuxInt(c) v0 := b.NewValue0(v.Pos, OpARM64MUL, x.Type) v0.AddArg2(x, y) v.AddArg(v0) @@ -5228,13 +5228,13 @@ func rewriteValueARM64_OpARM64MADD(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if v_2.Op != OpARM64MOVDconst { break } - d := v_2.AuxInt + d := auxIntToInt64(v_2.AuxInt) v.reset(OpARM64ADDconst) - v.AuxInt = c * d + v.AuxInt = int64ToAuxInt(c * d) v.AddArg(a) return true } @@ -5254,7 +5254,7 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(int32(c) == -1) { break } @@ -5270,7 +5270,7 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(int32(c) == 0) { break } @@ -5286,7 +5286,7 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(int32(c) == 1) { break } @@ -5303,12 +5303,12 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(isPowerOfTwo(c)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c) + v.AuxInt = int64ToAuxInt(log2(c)) v.AddArg2(a, x) return true } @@ -5321,13 +5321,13 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(isPowerOfTwo(c-1) && int32(c) >= 3) { break } v.reset(OpARM64ADD) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = log2(c - 1) + v0.AuxInt = int64ToAuxInt(log2(c - 1)) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5341,13 +5341,13 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(isPowerOfTwo(c+1) && int32(c) >= 7) { break } v.reset(OpARM64SUB) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = log2(c + 1) + v0.AuxInt = int64ToAuxInt(log2(c + 1)) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5361,14 +5361,14 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c / 3) + v.AuxInt = int64ToAuxInt(log2(c / 3)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5382,14 +5382,14 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c / 5) + v.AuxInt = int64ToAuxInt(log2(c / 5)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5403,14 +5403,14 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c / 7) + v.AuxInt = int64ToAuxInt(log2(c / 7)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5424,14 +5424,14 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c / 9) + v.AuxInt = int64ToAuxInt(log2(c / 9)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5444,7 +5444,7 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(int32(c) == -1) { break @@ -5461,7 +5461,7 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(int32(c) == 0) { break } @@ -5476,7 +5476,7 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(int32(c) == 1) { break @@ -5493,13 +5493,13 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(isPowerOfTwo(c)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c) + v.AuxInt = int64ToAuxInt(log2(c)) v.AddArg2(a, x) return true } @@ -5511,14 +5511,14 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(isPowerOfTwo(c-1) && int32(c) >= 3) { break } v.reset(OpARM64ADD) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = log2(c - 1) + v0.AuxInt = int64ToAuxInt(log2(c - 1)) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5531,14 +5531,14 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(isPowerOfTwo(c+1) && int32(c) >= 7) { break } v.reset(OpARM64SUB) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = log2(c + 1) + v0.AuxInt = int64ToAuxInt(log2(c + 1)) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5551,15 +5551,15 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c / 3) + v.AuxInt = int64ToAuxInt(log2(c / 3)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5572,15 +5572,15 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c / 5) + v.AuxInt = int64ToAuxInt(log2(c / 5)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5593,15 +5593,15 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c / 7) + v.AuxInt = int64ToAuxInt(log2(c / 7)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5614,15 +5614,15 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c / 9) + v.AuxInt = int64ToAuxInt(log2(c / 9)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -5633,11 +5633,11 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) x := v_1 y := v_2 v.reset(OpARM64ADDconst) - v.AuxInt = c + v.AuxInt = int64ToAuxInt(c) v0 := b.NewValue0(v.Pos, OpARM64MULW, x.Type) v0.AddArg2(x, y) v.AddArg(v0) @@ -5650,13 +5650,13 @@ func rewriteValueARM64_OpARM64MADDW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if v_2.Op != OpARM64MOVDconst { break } - d := v_2.AuxInt + d := auxIntToInt64(v_2.AuxInt) v.reset(OpARM64ADDconst) - v.AuxInt = int64(int32(c) * int32(d)) + v.AuxInt = int64ToAuxInt(int64(int32(c) * int32(d))) v.AddArg(a) return true } @@ -5671,7 +5671,7 @@ func rewriteValueARM64_OpARM64MNEG(v *Value) bool { for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { x := v_0 - if v_1.Op != OpARM64MOVDconst || v_1.AuxInt != -1 { + if v_1.Op != OpARM64MOVDconst || auxIntToInt64(v_1.AuxInt) != -1 { continue } v.copyOf(x) @@ -5683,11 +5683,11 @@ func rewriteValueARM64_OpARM64MNEG(v *Value) bool { // result: (MOVDconst [0]) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_1.Op != OpARM64MOVDconst || v_1.AuxInt != 0 { + if v_1.Op != OpARM64MOVDconst || auxIntToInt64(v_1.AuxInt) != 0 { continue } v.reset(OpARM64MOVDconst) - v.AuxInt = 0 + v.AuxInt = int64ToAuxInt(0) return true } break @@ -5697,7 +5697,7 @@ func rewriteValueARM64_OpARM64MNEG(v *Value) bool { for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { x := v_0 - if v_1.Op != OpARM64MOVDconst || v_1.AuxInt != 1 { + if v_1.Op != OpARM64MOVDconst || auxIntToInt64(v_1.AuxInt) != 1 { continue } v.reset(OpARM64NEG) @@ -5715,13 +5715,13 @@ func rewriteValueARM64_OpARM64MNEG(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(isPowerOfTwo(c)) { continue } v.reset(OpARM64NEG) v0 := b.NewValue0(v.Pos, OpARM64SLLconst, x.Type) - v0.AuxInt = log2(c) + v0.AuxInt = int64ToAuxInt(log2(c)) v0.AddArg(x) v.AddArg(v0) return true @@ -5737,13 +5737,13 @@ func rewriteValueARM64_OpARM64MNEG(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(isPowerOfTwo(c-1) && c >= 3) { continue } v.reset(OpARM64NEG) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = log2(c - 1) + v0.AuxInt = int64ToAuxInt(log2(c - 1)) v0.AddArg2(x, x) v.AddArg(v0) return true @@ -5759,13 +5759,13 @@ func rewriteValueARM64_OpARM64MNEG(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(isPowerOfTwo(c+1) && c >= 7) { continue } v.reset(OpARM64NEG) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = log2(c + 1) + v0.AuxInt = int64ToAuxInt(log2(c + 1)) v1 := b.NewValue0(v.Pos, OpARM64NEG, x.Type) v1.AddArg(x) v0.AddArg2(v1, x) @@ -5783,15 +5783,15 @@ func rewriteValueARM64_OpARM64MNEG(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(c%3 == 0 && isPowerOfTwo(c/3)) { continue } v.reset(OpARM64SLLconst) v.Type = x.Type - v.AuxInt = log2(c / 3) + v.AuxInt = int64ToAuxInt(log2(c / 3)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg(v0) return true @@ -5807,15 +5807,15 @@ func rewriteValueARM64_OpARM64MNEG(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(c%5 == 0 && isPowerOfTwo(c/5)) { continue } v.reset(OpARM64NEG) v0 := b.NewValue0(v.Pos, OpARM64SLLconst, x.Type) - v0.AuxInt = log2(c / 5) + v0.AuxInt = int64ToAuxInt(log2(c / 5)) v1 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v1.AuxInt = 2 + v1.AuxInt = int64ToAuxInt(2) v1.AddArg2(x, x) v0.AddArg(v1) v.AddArg(v0) @@ -5832,15 +5832,15 @@ func rewriteValueARM64_OpARM64MNEG(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(c%7 == 0 && isPowerOfTwo(c/7)) { continue } v.reset(OpARM64SLLconst) v.Type = x.Type - v.AuxInt = log2(c / 7) + v.AuxInt = int64ToAuxInt(log2(c / 7)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg(v0) return true @@ -5856,15 +5856,15 @@ func rewriteValueARM64_OpARM64MNEG(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(c%9 == 0 && isPowerOfTwo(c/9)) { continue } v.reset(OpARM64NEG) v0 := b.NewValue0(v.Pos, OpARM64SLLconst, x.Type) - v0.AuxInt = log2(c / 9) + v0.AuxInt = int64ToAuxInt(log2(c / 9)) v1 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v1.AuxInt = 3 + v1.AuxInt = int64ToAuxInt(3) v1.AddArg2(x, x) v0.AddArg(v1) v.AddArg(v0) @@ -5879,13 +5879,13 @@ func rewriteValueARM64_OpARM64MNEG(v *Value) bool { if v_0.Op != OpARM64MOVDconst { continue } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) if v_1.Op != OpARM64MOVDconst { continue } - d := v_1.AuxInt + d := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = -c * d + v.AuxInt = int64ToAuxInt(-c * d) return true } break @@ -5905,7 +5905,7 @@ func rewriteValueARM64_OpARM64MNEGW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(int32(c) == -1) { continue } @@ -5922,12 +5922,12 @@ func rewriteValueARM64_OpARM64MNEGW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(int32(c) == 0) { continue } v.reset(OpARM64MOVDconst) - v.AuxInt = 0 + v.AuxInt = int64ToAuxInt(0) return true } break @@ -5941,7 +5941,7 @@ func rewriteValueARM64_OpARM64MNEGW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(int32(c) == 1) { continue } @@ -5960,13 +5960,13 @@ func rewriteValueARM64_OpARM64MNEGW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(isPowerOfTwo(c)) { continue } v.reset(OpARM64NEG) v0 := b.NewValue0(v.Pos, OpARM64SLLconst, x.Type) - v0.AuxInt = log2(c) + v0.AuxInt = int64ToAuxInt(log2(c)) v0.AddArg(x) v.AddArg(v0) return true @@ -5982,13 +5982,13 @@ func rewriteValueARM64_OpARM64MNEGW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(isPowerOfTwo(c-1) && int32(c) >= 3) { continue } v.reset(OpARM64NEG) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = log2(c - 1) + v0.AuxInt = int64ToAuxInt(log2(c - 1)) v0.AddArg2(x, x) v.AddArg(v0) return true @@ -6004,13 +6004,13 @@ func rewriteValueARM64_OpARM64MNEGW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(isPowerOfTwo(c+1) && int32(c) >= 7) { continue } v.reset(OpARM64NEG) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = log2(c + 1) + v0.AuxInt = int64ToAuxInt(log2(c + 1)) v1 := b.NewValue0(v.Pos, OpARM64NEG, x.Type) v1.AddArg(x) v0.AddArg2(v1, x) @@ -6028,15 +6028,15 @@ func rewriteValueARM64_OpARM64MNEGW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)) { continue } v.reset(OpARM64SLLconst) v.Type = x.Type - v.AuxInt = log2(c / 3) + v.AuxInt = int64ToAuxInt(log2(c / 3)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg(v0) return true @@ -6052,15 +6052,15 @@ func rewriteValueARM64_OpARM64MNEGW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)) { continue } v.reset(OpARM64NEG) v0 := b.NewValue0(v.Pos, OpARM64SLLconst, x.Type) - v0.AuxInt = log2(c / 5) + v0.AuxInt = int64ToAuxInt(log2(c / 5)) v1 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v1.AuxInt = 2 + v1.AuxInt = int64ToAuxInt(2) v1.AddArg2(x, x) v0.AddArg(v1) v.AddArg(v0) @@ -6077,15 +6077,15 @@ func rewriteValueARM64_OpARM64MNEGW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)) { continue } v.reset(OpARM64SLLconst) v.Type = x.Type - v.AuxInt = log2(c / 7) + v.AuxInt = int64ToAuxInt(log2(c / 7)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg(v0) return true @@ -6101,15 +6101,15 @@ func rewriteValueARM64_OpARM64MNEGW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)) { continue } v.reset(OpARM64NEG) v0 := b.NewValue0(v.Pos, OpARM64SLLconst, x.Type) - v0.AuxInt = log2(c / 9) + v0.AuxInt = int64ToAuxInt(log2(c / 9)) v1 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v1.AuxInt = 3 + v1.AuxInt = int64ToAuxInt(3) v1.AddArg2(x, x) v0.AddArg(v1) v.AddArg(v0) @@ -6124,13 +6124,13 @@ func rewriteValueARM64_OpARM64MNEGW(v *Value) bool { if v_0.Op != OpARM64MOVDconst { continue } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) if v_1.Op != OpARM64MOVDconst { continue } - d := v_1.AuxInt + d := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = -int64(int32(c) * int32(d)) + v.AuxInt = int64ToAuxInt(-int64(int32(c) * int32(d))) return true } break @@ -6146,13 +6146,13 @@ func rewriteValueARM64_OpARM64MOD(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) if v_1.Op != OpARM64MOVDconst { break } - d := v_1.AuxInt + d := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = c % d + v.AuxInt = int64ToAuxInt(c % d) return true } return false @@ -6166,13 +6166,13 @@ func rewriteValueARM64_OpARM64MODW(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) if v_1.Op != OpARM64MOVDconst { break } - d := v_1.AuxInt + d := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = int64(int32(c) % int32(d)) + v.AuxInt = int64ToAuxInt(int64(int32(c) % int32(d))) return true } return false @@ -6380,10 +6380,10 @@ func rewriteValueARM64_OpARM64MOVBUreg(v *Value) bool { if v_0.Op != OpARM64ANDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] v.reset(OpARM64ANDconst) - v.AuxInt = c & (1<<8 - 1) + v.AuxInt = int64ToAuxInt(c & (1<<8 - 1)) v.AddArg(x) return true } @@ -6393,9 +6393,9 @@ func rewriteValueARM64_OpARM64MOVBUreg(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = int64(uint8(c)) + v.AuxInt = int64ToAuxInt(int64(uint8(c))) return true } // match: (MOVBUreg x) @@ -6636,9 +6636,9 @@ func rewriteValueARM64_OpARM64MOVBreg(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = int64(int8(c)) + v.AuxInt = int64ToAuxInt(int64(int8(c))) return true } // match: (MOVBreg (SLLconst [lc] x)) @@ -8991,9 +8991,9 @@ func rewriteValueARM64_OpARM64MOVDreg(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = c + v.AuxInt = int64ToAuxInt(c) return true } return false @@ -9874,10 +9874,10 @@ func rewriteValueARM64_OpARM64MOVHUreg(v *Value) bool { if v_0.Op != OpARM64ANDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] v.reset(OpARM64ANDconst) - v.AuxInt = c & (1<<16 - 1) + v.AuxInt = int64ToAuxInt(c & (1<<16 - 1)) v.AddArg(x) return true } @@ -9887,9 +9887,9 @@ func rewriteValueARM64_OpARM64MOVHUreg(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = int64(uint16(c)) + v.AuxInt = int64ToAuxInt(int64(uint16(c))) return true } // match: (MOVHUreg (SLLconst [sc] x)) @@ -10301,9 +10301,9 @@ func rewriteValueARM64_OpARM64MOVHreg(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = int64(int16(c)) + v.AuxInt = int64ToAuxInt(int64(int16(c))) return true } // match: (MOVHreg (SLLconst [lc] x)) @@ -11971,10 +11971,10 @@ func rewriteValueARM64_OpARM64MOVWUreg(v *Value) bool { if v_0.Op != OpARM64ANDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] v.reset(OpARM64ANDconst) - v.AuxInt = c & (1<<32 - 1) + v.AuxInt = int64ToAuxInt(c & (1<<32 - 1)) v.AddArg(x) return true } @@ -11984,9 +11984,9 @@ func rewriteValueARM64_OpARM64MOVWUreg(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = int64(uint32(c)) + v.AuxInt = int64ToAuxInt(int64(uint32(c))) return true } // match: (MOVWUreg (SLLconst [sc] x)) @@ -12456,9 +12456,9 @@ func rewriteValueARM64_OpARM64MOVWreg(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = int64(int32(c)) + v.AuxInt = int64ToAuxInt(int64(int32(c))) return true } // match: (MOVWreg (SLLconst [lc] x)) @@ -13346,7 +13346,7 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { for { a := v_0 x := v_1 - if v_2.Op != OpARM64MOVDconst || v_2.AuxInt != -1 { + if v_2.Op != OpARM64MOVDconst || auxIntToInt64(v_2.AuxInt) != -1 { break } v.reset(OpARM64ADD) @@ -13357,7 +13357,7 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { // result: a for { a := v_0 - if v_2.Op != OpARM64MOVDconst || v_2.AuxInt != 0 { + if v_2.Op != OpARM64MOVDconst || auxIntToInt64(v_2.AuxInt) != 0 { break } v.copyOf(a) @@ -13368,7 +13368,7 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { for { a := v_0 x := v_1 - if v_2.Op != OpARM64MOVDconst || v_2.AuxInt != 1 { + if v_2.Op != OpARM64MOVDconst || auxIntToInt64(v_2.AuxInt) != 1 { break } v.reset(OpARM64SUB) @@ -13384,12 +13384,12 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(isPowerOfTwo(c)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c) + v.AuxInt = int64ToAuxInt(log2(c)) v.AddArg2(a, x) return true } @@ -13402,13 +13402,13 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(isPowerOfTwo(c-1) && c >= 3) { break } v.reset(OpARM64SUB) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = log2(c - 1) + v0.AuxInt = int64ToAuxInt(log2(c - 1)) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13422,13 +13422,13 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(isPowerOfTwo(c+1) && c >= 7) { break } v.reset(OpARM64ADD) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = log2(c + 1) + v0.AuxInt = int64ToAuxInt(log2(c + 1)) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13442,14 +13442,14 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(c%3 == 0 && isPowerOfTwo(c/3)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c / 3) + v.AuxInt = int64ToAuxInt(log2(c / 3)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13463,14 +13463,14 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(c%5 == 0 && isPowerOfTwo(c/5)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c / 5) + v.AuxInt = int64ToAuxInt(log2(c / 5)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13484,14 +13484,14 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(c%7 == 0 && isPowerOfTwo(c/7)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c / 7) + v.AuxInt = int64ToAuxInt(log2(c / 7)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13505,14 +13505,14 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(c%9 == 0 && isPowerOfTwo(c/9)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c / 9) + v.AuxInt = int64ToAuxInt(log2(c / 9)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13521,7 +13521,7 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { // result: (ADD a x) for { a := v_0 - if v_1.Op != OpARM64MOVDconst || v_1.AuxInt != -1 { + if v_1.Op != OpARM64MOVDconst || auxIntToInt64(v_1.AuxInt) != -1 { break } x := v_2 @@ -13533,7 +13533,7 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { // result: a for { a := v_0 - if v_1.Op != OpARM64MOVDconst || v_1.AuxInt != 0 { + if v_1.Op != OpARM64MOVDconst || auxIntToInt64(v_1.AuxInt) != 0 { break } v.copyOf(a) @@ -13543,7 +13543,7 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { // result: (SUB a x) for { a := v_0 - if v_1.Op != OpARM64MOVDconst || v_1.AuxInt != 1 { + if v_1.Op != OpARM64MOVDconst || auxIntToInt64(v_1.AuxInt) != 1 { break } x := v_2 @@ -13559,13 +13559,13 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(isPowerOfTwo(c)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c) + v.AuxInt = int64ToAuxInt(log2(c)) v.AddArg2(a, x) return true } @@ -13577,14 +13577,14 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(isPowerOfTwo(c-1) && c >= 3) { break } v.reset(OpARM64SUB) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = log2(c - 1) + v0.AuxInt = int64ToAuxInt(log2(c - 1)) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13597,14 +13597,14 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(isPowerOfTwo(c+1) && c >= 7) { break } v.reset(OpARM64ADD) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = log2(c + 1) + v0.AuxInt = int64ToAuxInt(log2(c + 1)) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13617,15 +13617,15 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(c%3 == 0 && isPowerOfTwo(c/3)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c / 3) + v.AuxInt = int64ToAuxInt(log2(c / 3)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13638,15 +13638,15 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(c%5 == 0 && isPowerOfTwo(c/5)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c / 5) + v.AuxInt = int64ToAuxInt(log2(c / 5)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13659,15 +13659,15 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(c%7 == 0 && isPowerOfTwo(c/7)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c / 7) + v.AuxInt = int64ToAuxInt(log2(c / 7)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13680,15 +13680,15 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(c%9 == 0 && isPowerOfTwo(c/9)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c / 9) + v.AuxInt = int64ToAuxInt(log2(c / 9)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13699,11 +13699,11 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) x := v_1 y := v_2 v.reset(OpARM64ADDconst) - v.AuxInt = c + v.AuxInt = int64ToAuxInt(c) v0 := b.NewValue0(v.Pos, OpARM64MNEG, x.Type) v0.AddArg2(x, y) v.AddArg(v0) @@ -13716,13 +13716,13 @@ func rewriteValueARM64_OpARM64MSUB(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if v_2.Op != OpARM64MOVDconst { break } - d := v_2.AuxInt + d := auxIntToInt64(v_2.AuxInt) v.reset(OpARM64SUBconst) - v.AuxInt = c * d + v.AuxInt = int64ToAuxInt(c * d) v.AddArg(a) return true } @@ -13742,7 +13742,7 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(int32(c) == -1) { break } @@ -13758,7 +13758,7 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(int32(c) == 0) { break } @@ -13774,7 +13774,7 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(int32(c) == 1) { break } @@ -13791,12 +13791,12 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(isPowerOfTwo(c)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c) + v.AuxInt = int64ToAuxInt(log2(c)) v.AddArg2(a, x) return true } @@ -13809,13 +13809,13 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(isPowerOfTwo(c-1) && int32(c) >= 3) { break } v.reset(OpARM64SUB) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = log2(c - 1) + v0.AuxInt = int64ToAuxInt(log2(c - 1)) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13829,13 +13829,13 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(isPowerOfTwo(c+1) && int32(c) >= 7) { break } v.reset(OpARM64ADD) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = log2(c + 1) + v0.AuxInt = int64ToAuxInt(log2(c + 1)) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13849,14 +13849,14 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c / 3) + v.AuxInt = int64ToAuxInt(log2(c / 3)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13870,14 +13870,14 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c / 5) + v.AuxInt = int64ToAuxInt(log2(c / 5)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13891,14 +13891,14 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c / 7) + v.AuxInt = int64ToAuxInt(log2(c / 7)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13912,14 +13912,14 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_2.Op != OpARM64MOVDconst { break } - c := v_2.AuxInt + c := auxIntToInt64(v_2.AuxInt) if !(c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c / 9) + v.AuxInt = int64ToAuxInt(log2(c / 9)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -13932,7 +13932,7 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(int32(c) == -1) { break @@ -13949,7 +13949,7 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(int32(c) == 0) { break } @@ -13964,7 +13964,7 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(int32(c) == 1) { break @@ -13981,13 +13981,13 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(isPowerOfTwo(c)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c) + v.AuxInt = int64ToAuxInt(log2(c)) v.AddArg2(a, x) return true } @@ -13999,14 +13999,14 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(isPowerOfTwo(c-1) && int32(c) >= 3) { break } v.reset(OpARM64SUB) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = log2(c - 1) + v0.AuxInt = int64ToAuxInt(log2(c - 1)) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -14019,14 +14019,14 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(isPowerOfTwo(c+1) && int32(c) >= 7) { break } v.reset(OpARM64ADD) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = log2(c + 1) + v0.AuxInt = int64ToAuxInt(log2(c + 1)) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -14039,15 +14039,15 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c / 3) + v.AuxInt = int64ToAuxInt(log2(c / 3)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -14060,15 +14060,15 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c / 5) + v.AuxInt = int64ToAuxInt(log2(c / 5)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -14081,15 +14081,15 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)) { break } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c / 7) + v.AuxInt = int64ToAuxInt(log2(c / 7)) v0 := b.NewValue0(v.Pos, OpARM64SUBshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -14102,15 +14102,15 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) x := v_2 if !(c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)) { break } v.reset(OpARM64SUBshiftLL) - v.AuxInt = log2(c / 9) + v.AuxInt = int64ToAuxInt(log2(c / 9)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg2(a, v0) return true @@ -14121,11 +14121,11 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) x := v_1 y := v_2 v.reset(OpARM64ADDconst) - v.AuxInt = c + v.AuxInt = int64ToAuxInt(c) v0 := b.NewValue0(v.Pos, OpARM64MNEGW, x.Type) v0.AddArg2(x, y) v.AddArg(v0) @@ -14138,13 +14138,13 @@ func rewriteValueARM64_OpARM64MSUBW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if v_2.Op != OpARM64MOVDconst { break } - d := v_2.AuxInt + d := auxIntToInt64(v_2.AuxInt) v.reset(OpARM64SUBconst) - v.AuxInt = int64(int32(c) * int32(d)) + v.AuxInt = int64ToAuxInt(int64(int32(c) * int32(d))) v.AddArg(a) return true } @@ -14174,7 +14174,7 @@ func rewriteValueARM64_OpARM64MUL(v *Value) bool { for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { x := v_0 - if v_1.Op != OpARM64MOVDconst || v_1.AuxInt != -1 { + if v_1.Op != OpARM64MOVDconst || auxIntToInt64(v_1.AuxInt) != -1 { continue } v.reset(OpARM64NEG) @@ -14187,11 +14187,11 @@ func rewriteValueARM64_OpARM64MUL(v *Value) bool { // result: (MOVDconst [0]) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_1.Op != OpARM64MOVDconst || v_1.AuxInt != 0 { + if v_1.Op != OpARM64MOVDconst || auxIntToInt64(v_1.AuxInt) != 0 { continue } v.reset(OpARM64MOVDconst) - v.AuxInt = 0 + v.AuxInt = int64ToAuxInt(0) return true } break @@ -14201,7 +14201,7 @@ func rewriteValueARM64_OpARM64MUL(v *Value) bool { for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { x := v_0 - if v_1.Op != OpARM64MOVDconst || v_1.AuxInt != 1 { + if v_1.Op != OpARM64MOVDconst || auxIntToInt64(v_1.AuxInt) != 1 { continue } v.copyOf(x) @@ -14218,12 +14218,12 @@ func rewriteValueARM64_OpARM64MUL(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(isPowerOfTwo(c)) { continue } v.reset(OpARM64SLLconst) - v.AuxInt = log2(c) + v.AuxInt = int64ToAuxInt(log2(c)) v.AddArg(x) return true } @@ -14238,12 +14238,12 @@ func rewriteValueARM64_OpARM64MUL(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(isPowerOfTwo(c-1) && c >= 3) { continue } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c - 1) + v.AuxInt = int64ToAuxInt(log2(c - 1)) v.AddArg2(x, x) return true } @@ -14258,12 +14258,12 @@ func rewriteValueARM64_OpARM64MUL(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(isPowerOfTwo(c+1) && c >= 7) { continue } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c + 1) + v.AuxInt = int64ToAuxInt(log2(c + 1)) v0 := b.NewValue0(v.Pos, OpARM64NEG, x.Type) v0.AddArg(x) v.AddArg2(v0, x) @@ -14280,14 +14280,14 @@ func rewriteValueARM64_OpARM64MUL(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(c%3 == 0 && isPowerOfTwo(c/3)) { continue } v.reset(OpARM64SLLconst) - v.AuxInt = log2(c / 3) + v.AuxInt = int64ToAuxInt(log2(c / 3)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 1 + v0.AuxInt = int64ToAuxInt(1) v0.AddArg2(x, x) v.AddArg(v0) return true @@ -14303,14 +14303,14 @@ func rewriteValueARM64_OpARM64MUL(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(c%5 == 0 && isPowerOfTwo(c/5)) { continue } v.reset(OpARM64SLLconst) - v.AuxInt = log2(c / 5) + v.AuxInt = int64ToAuxInt(log2(c / 5)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg(v0) return true @@ -14326,14 +14326,14 @@ func rewriteValueARM64_OpARM64MUL(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(c%7 == 0 && isPowerOfTwo(c/7)) { continue } v.reset(OpARM64SLLconst) - v.AuxInt = log2(c / 7) + v.AuxInt = int64ToAuxInt(log2(c / 7)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v1 := b.NewValue0(v.Pos, OpARM64NEG, x.Type) v1.AddArg(x) v0.AddArg2(v1, x) @@ -14351,14 +14351,14 @@ func rewriteValueARM64_OpARM64MUL(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(c%9 == 0 && isPowerOfTwo(c/9)) { continue } v.reset(OpARM64SLLconst) - v.AuxInt = log2(c / 9) + v.AuxInt = int64ToAuxInt(log2(c / 9)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg(v0) return true @@ -14372,13 +14372,13 @@ func rewriteValueARM64_OpARM64MUL(v *Value) bool { if v_0.Op != OpARM64MOVDconst { continue } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) if v_1.Op != OpARM64MOVDconst { continue } - d := v_1.AuxInt + d := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = c * d + v.AuxInt = int64ToAuxInt(c * d) return true } break @@ -14413,7 +14413,7 @@ func rewriteValueARM64_OpARM64MULW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(int32(c) == -1) { continue } @@ -14431,12 +14431,12 @@ func rewriteValueARM64_OpARM64MULW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(int32(c) == 0) { continue } v.reset(OpARM64MOVDconst) - v.AuxInt = 0 + v.AuxInt = int64ToAuxInt(0) return true } break @@ -14450,7 +14450,7 @@ func rewriteValueARM64_OpARM64MULW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(int32(c) == 1) { continue } @@ -14468,12 +14468,12 @@ func rewriteValueARM64_OpARM64MULW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(isPowerOfTwo(c)) { continue } v.reset(OpARM64SLLconst) - v.AuxInt = log2(c) + v.AuxInt = int64ToAuxInt(log2(c)) v.AddArg(x) return true } @@ -14488,12 +14488,12 @@ func rewriteValueARM64_OpARM64MULW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(isPowerOfTwo(c-1) && int32(c) >= 3) { continue } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c - 1) + v.AuxInt = int64ToAuxInt(log2(c - 1)) v.AddArg2(x, x) return true } @@ -14508,12 +14508,12 @@ func rewriteValueARM64_OpARM64MULW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(isPowerOfTwo(c+1) && int32(c) >= 7) { continue } v.reset(OpARM64ADDshiftLL) - v.AuxInt = log2(c + 1) + v.AuxInt = int64ToAuxInt(log2(c + 1)) v0 := b.NewValue0(v.Pos, OpARM64NEG, x.Type) v0.AddArg(x) v.AddArg2(v0, x) @@ -14530,14 +14530,14 @@ func rewriteValueARM64_OpARM64MULW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)) { continue } v.reset(OpARM64SLLconst) - v.AuxInt = log2(c / 3) + v.AuxInt = int64ToAuxInt(log2(c / 3)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 1 + v0.AuxInt = int64ToAuxInt(1) v0.AddArg2(x, x) v.AddArg(v0) return true @@ -14553,14 +14553,14 @@ func rewriteValueARM64_OpARM64MULW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)) { continue } v.reset(OpARM64SLLconst) - v.AuxInt = log2(c / 5) + v.AuxInt = int64ToAuxInt(log2(c / 5)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 2 + v0.AuxInt = int64ToAuxInt(2) v0.AddArg2(x, x) v.AddArg(v0) return true @@ -14576,14 +14576,14 @@ func rewriteValueARM64_OpARM64MULW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)) { continue } v.reset(OpARM64SLLconst) - v.AuxInt = log2(c / 7) + v.AuxInt = int64ToAuxInt(log2(c / 7)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v1 := b.NewValue0(v.Pos, OpARM64NEG, x.Type) v1.AddArg(x) v0.AddArg2(v1, x) @@ -14601,14 +14601,14 @@ func rewriteValueARM64_OpARM64MULW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)) { continue } v.reset(OpARM64SLLconst) - v.AuxInt = log2(c / 9) + v.AuxInt = int64ToAuxInt(log2(c / 9)) v0 := b.NewValue0(v.Pos, OpARM64ADDshiftLL, x.Type) - v0.AuxInt = 3 + v0.AuxInt = int64ToAuxInt(3) v0.AddArg2(x, x) v.AddArg(v0) return true @@ -14622,13 +14622,13 @@ func rewriteValueARM64_OpARM64MULW(v *Value) bool { if v_0.Op != OpARM64MOVDconst { continue } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) if v_1.Op != OpARM64MOVDconst { continue } - d := v_1.AuxInt + d := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = int64(int32(c) * int32(d)) + v.AuxInt = int64ToAuxInt(int64(int32(c) * int32(d))) return true } break @@ -14655,9 +14655,9 @@ func rewriteValueARM64_OpARM64MVN(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = ^c + v.AuxInt = int64ToAuxInt(^c) return true } // match: (MVN x:(SLLconst [c] y)) @@ -14796,9 +14796,9 @@ func rewriteValueARM64_OpARM64NEG(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = -c + v.AuxInt = int64ToAuxInt(-c) return true } // match: (NEG x:(SLLconst [c] y)) @@ -14944,9 +14944,9 @@ func rewriteValueARM64_OpARM64OR(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64ORconst) - v.AuxInt = c + v.AuxInt = int64ToAuxInt(c) v.AddArg(x) return true } @@ -16938,9 +16938,9 @@ func rewriteValueARM64_OpARM64ORN(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64ORconst) - v.AuxInt = ^c + v.AuxInt = int64ToAuxInt(^c) v.AddArg(x) return true } @@ -16952,7 +16952,7 @@ func rewriteValueARM64_OpARM64ORN(v *Value) bool { break } v.reset(OpARM64MOVDconst) - v.AuxInt = -1 + v.AuxInt = int64ToAuxInt(-1) return true } // match: (ORN x0 x1:(SLLconst [c] y)) @@ -17127,7 +17127,7 @@ func rewriteValueARM64_OpARM64ORconst(v *Value) bool { // match: (ORconst [0] x) // result: x for { - if v.AuxInt != 0 { + if auxIntToInt64(v.AuxInt) != 0 { break } x := v_0 @@ -17137,36 +17137,36 @@ func rewriteValueARM64_OpARM64ORconst(v *Value) bool { // match: (ORconst [-1] _) // result: (MOVDconst [-1]) for { - if v.AuxInt != -1 { + if auxIntToInt64(v.AuxInt) != -1 { break } v.reset(OpARM64MOVDconst) - v.AuxInt = -1 + v.AuxInt = int64ToAuxInt(-1) return true } // match: (ORconst [c] (MOVDconst [d])) // result: (MOVDconst [c|d]) for { - c := v.AuxInt + c := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64MOVDconst { break } - d := v_0.AuxInt + d := auxIntToInt64(v_0.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = c | d + v.AuxInt = int64ToAuxInt(c | d) return true } // match: (ORconst [c] (ORconst [d] x)) // result: (ORconst [c|d] x) for { - c := v.AuxInt + c := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64ORconst { break } - d := v_0.AuxInt + d := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] v.reset(OpARM64ORconst) - v.AuxInt = c | d + v.AuxInt = int64ToAuxInt(c | d) v.AddArg(x) return true } @@ -19064,7 +19064,7 @@ func rewriteValueARM64_OpARM64SBCSflags(v *Value) bool { break } v_2_0_0 := v_2_0.Args[0] - if v_2_0_0.Op != OpARM64MOVDconst || v_2_0_0.AuxInt != 0 { + if v_2_0_0.Op != OpARM64MOVDconst || auxIntToInt64(v_2_0_0.AuxInt) != 0 { break } v.reset(OpARM64SUBSflags) @@ -19083,9 +19083,9 @@ func rewriteValueARM64_OpARM64SLL(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64SLLconst) - v.AuxInt = c & 63 + v.AuxInt = int64ToAuxInt(c & 63) v.AddArg(x) return true } @@ -19096,13 +19096,13 @@ func rewriteValueARM64_OpARM64SLLconst(v *Value) bool { // match: (SLLconst [c] (MOVDconst [d])) // result: (MOVDconst [d<>uint64(c)]) for { - c := v.AuxInt + c := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64MOVDconst { break } - d := v_0.AuxInt + d := auxIntToInt64(v_0.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = d >> uint64(c) + v.AuxInt = int64ToAuxInt(d >> uint64(c)) return true } // match: (SRAconst [rc] (SLLconst [lc] x)) @@ -19378,9 +19378,9 @@ func rewriteValueARM64_OpARM64SRL(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64SRLconst) - v.AuxInt = c & 63 + v.AuxInt = int64ToAuxInt(c & 63) v.AddArg(x) return true } @@ -19391,13 +19391,13 @@ func rewriteValueARM64_OpARM64SRLconst(v *Value) bool { // match: (SRLconst [c] (MOVDconst [d])) // result: (MOVDconst [int64(uint64(d)>>uint64(c))]) for { - c := v.AuxInt + c := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64MOVDconst { break } - d := v_0.AuxInt + d := auxIntToInt64(v_0.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = int64(uint64(d) >> uint64(c)) + v.AuxInt = int64ToAuxInt(int64(uint64(d) >> uint64(c))) return true } // match: (SRLconst [c] (SLLconst [c] x)) @@ -19679,9 +19679,9 @@ func rewriteValueARM64_OpARM64SUB(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64SUBconst) - v.AuxInt = c + v.AuxInt = int64ToAuxInt(c) v.AddArg(x) return true } @@ -19765,7 +19765,7 @@ func rewriteValueARM64_OpARM64SUB(v *Value) bool { break } v.reset(OpARM64MOVDconst) - v.AuxInt = 0 + v.AuxInt = int64ToAuxInt(0) return true } // match: (SUB x (SUB y z)) @@ -19862,7 +19862,7 @@ func rewriteValueARM64_OpARM64SUBconst(v *Value) bool { // match: (SUBconst [0] x) // result: x for { - if v.AuxInt != 0 { + if auxIntToInt64(v.AuxInt) != 0 { break } x := v_0 @@ -19872,40 +19872,40 @@ func rewriteValueARM64_OpARM64SUBconst(v *Value) bool { // match: (SUBconst [c] (MOVDconst [d])) // result: (MOVDconst [d-c]) for { - c := v.AuxInt + c := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64MOVDconst { break } - d := v_0.AuxInt + d := auxIntToInt64(v_0.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = d - c + v.AuxInt = int64ToAuxInt(d - c) return true } // match: (SUBconst [c] (SUBconst [d] x)) // result: (ADDconst [-c-d] x) for { - c := v.AuxInt + c := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64SUBconst { break } - d := v_0.AuxInt + d := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] v.reset(OpARM64ADDconst) - v.AuxInt = -c - d + v.AuxInt = int64ToAuxInt(-c - d) v.AddArg(x) return true } // match: (SUBconst [c] (ADDconst [d] x)) // result: (ADDconst [-c+d] x) for { - c := v.AuxInt + c := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64ADDconst { break } - d := v_0.AuxInt + d := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] v.reset(OpARM64ADDconst) - v.AuxInt = -c + d + v.AuxInt = int64ToAuxInt(-c + d) v.AddArg(x) return true } @@ -20030,9 +20030,9 @@ func rewriteValueARM64_OpARM64TST(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64TSTconst) - v.AuxInt = c + v.AuxInt = int64ToAuxInt(c) v.AddArg(x) return true } @@ -20110,16 +20110,16 @@ func rewriteValueARM64_OpARM64TSTW(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (TSTW x (MOVDconst [c])) - // result: (TSTWconst [c] x) + // result: (TSTWconst [int32(c)] x) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { x := v_0 if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64TSTWconst) - v.AuxInt = c + v.AuxInt = int32ToAuxInt(int32(c)) v.AddArg(x) return true } @@ -20375,7 +20375,7 @@ func rewriteValueARM64_OpARM64UDIV(v *Value) bool { // result: x for { x := v_0 - if v_1.Op != OpARM64MOVDconst || v_1.AuxInt != 1 { + if v_1.Op != OpARM64MOVDconst || auxIntToInt64(v_1.AuxInt) != 1 { break } v.copyOf(x) @@ -20389,12 +20389,12 @@ func rewriteValueARM64_OpARM64UDIV(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(isPowerOfTwo(c)) { break } v.reset(OpARM64SRLconst) - v.AuxInt = log2(c) + v.AuxInt = int64ToAuxInt(log2(c)) v.AddArg(x) return true } @@ -20404,13 +20404,13 @@ func rewriteValueARM64_OpARM64UDIV(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) if v_1.Op != OpARM64MOVDconst { break } - d := v_1.AuxInt + d := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = int64(uint64(c) / uint64(d)) + v.AuxInt = int64ToAuxInt(int64(uint64(c) / uint64(d))) return true } return false @@ -20426,7 +20426,7 @@ func rewriteValueARM64_OpARM64UDIVW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(uint32(c) == 1) { break } @@ -20441,12 +20441,12 @@ func rewriteValueARM64_OpARM64UDIVW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(isPowerOfTwo(c) && is32Bit(c)) { break } v.reset(OpARM64SRLconst) - v.AuxInt = log2(c) + v.AuxInt = int64ToAuxInt(log2(c)) v.AddArg(x) return true } @@ -20456,13 +20456,13 @@ func rewriteValueARM64_OpARM64UDIVW(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) if v_1.Op != OpARM64MOVDconst { break } - d := v_1.AuxInt + d := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = int64(uint32(c) / uint32(d)) + v.AuxInt = int64ToAuxInt(int64(uint32(c) / uint32(d))) return true } return false @@ -20490,11 +20490,11 @@ func rewriteValueARM64_OpARM64UMOD(v *Value) bool { // match: (UMOD _ (MOVDconst [1])) // result: (MOVDconst [0]) for { - if v_1.Op != OpARM64MOVDconst || v_1.AuxInt != 1 { + if v_1.Op != OpARM64MOVDconst || auxIntToInt64(v_1.AuxInt) != 1 { break } v.reset(OpARM64MOVDconst) - v.AuxInt = 0 + v.AuxInt = int64ToAuxInt(0) return true } // match: (UMOD x (MOVDconst [c])) @@ -20505,12 +20505,12 @@ func rewriteValueARM64_OpARM64UMOD(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(isPowerOfTwo(c)) { break } v.reset(OpARM64ANDconst) - v.AuxInt = c - 1 + v.AuxInt = int64ToAuxInt(c - 1) v.AddArg(x) return true } @@ -20520,13 +20520,13 @@ func rewriteValueARM64_OpARM64UMOD(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) if v_1.Op != OpARM64MOVDconst { break } - d := v_1.AuxInt + d := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = int64(uint64(c) % uint64(d)) + v.AuxInt = int64ToAuxInt(int64(uint64(c) % uint64(d))) return true } return false @@ -20558,12 +20558,12 @@ func rewriteValueARM64_OpARM64UMODW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(uint32(c) == 1) { break } v.reset(OpARM64MOVDconst) - v.AuxInt = 0 + v.AuxInt = int64ToAuxInt(0) return true } // match: (UMODW x (MOVDconst [c])) @@ -20574,12 +20574,12 @@ func rewriteValueARM64_OpARM64UMODW(v *Value) bool { if v_1.Op != OpARM64MOVDconst { break } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) if !(isPowerOfTwo(c) && is32Bit(c)) { break } v.reset(OpARM64ANDconst) - v.AuxInt = c - 1 + v.AuxInt = int64ToAuxInt(c - 1) v.AddArg(x) return true } @@ -20589,13 +20589,13 @@ func rewriteValueARM64_OpARM64UMODW(v *Value) bool { if v_0.Op != OpARM64MOVDconst { break } - c := v_0.AuxInt + c := auxIntToInt64(v_0.AuxInt) if v_1.Op != OpARM64MOVDconst { break } - d := v_1.AuxInt + d := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = int64(uint32(c) % uint32(d)) + v.AuxInt = int64ToAuxInt(int64(uint32(c) % uint32(d))) return true } return false @@ -20613,9 +20613,9 @@ func rewriteValueARM64_OpARM64XOR(v *Value) bool { if v_1.Op != OpARM64MOVDconst { continue } - c := v_1.AuxInt + c := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64XORconst) - v.AuxInt = c + v.AuxInt = int64ToAuxInt(c) v.AddArg(x) return true } @@ -20629,7 +20629,7 @@ func rewriteValueARM64_OpARM64XOR(v *Value) bool { break } v.reset(OpARM64MOVDconst) - v.AuxInt = 0 + v.AuxInt = int64ToAuxInt(0) return true } // match: (XOR x (MVN y)) @@ -21001,7 +21001,7 @@ func rewriteValueARM64_OpARM64XORconst(v *Value) bool { // match: (XORconst [0] x) // result: x for { - if v.AuxInt != 0 { + if auxIntToInt64(v.AuxInt) != 0 { break } x := v_0 @@ -21011,7 +21011,7 @@ func rewriteValueARM64_OpARM64XORconst(v *Value) bool { // match: (XORconst [-1] x) // result: (MVN x) for { - if v.AuxInt != -1 { + if auxIntToInt64(v.AuxInt) != -1 { break } x := v_0 @@ -21022,26 +21022,26 @@ func rewriteValueARM64_OpARM64XORconst(v *Value) bool { // match: (XORconst [c] (MOVDconst [d])) // result: (MOVDconst [c^d]) for { - c := v.AuxInt + c := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64MOVDconst { break } - d := v_0.AuxInt + d := auxIntToInt64(v_0.AuxInt) v.reset(OpARM64MOVDconst) - v.AuxInt = c ^ d + v.AuxInt = int64ToAuxInt(c ^ d) return true } // match: (XORconst [c] (XORconst [d] x)) // result: (XORconst [c^d] x) for { - c := v.AuxInt + c := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64XORconst { break } - d := v_0.AuxInt + d := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] v.reset(OpARM64XORconst) - v.AuxInt = c ^ d + v.AuxInt = int64ToAuxInt(c ^ d) v.AddArg(x) return true } -- cgit v1.2.3-54-g00ecf From e3063636124d0e5b2d0fad7912a9c6810629f486 Mon Sep 17 00:00:00 2001 From: Jay Conrod Date: Wed, 2 Sep 2020 13:14:36 -0400 Subject: cmd/go: implement 'go install pkg@version' With this change, 'go install' will install executables in module mode without using or modifying the module in the current directory, if there is one. For #40276 Change-Id: I922e71719b3a4e0c779ce7a30429355fc29930bf Reviewed-on: https://go-review.googlesource.com/c/go/+/254365 Run-TryBot: Jay Conrod TryBot-Result: Go Bot Reviewed-by: Bryan C. Mills Reviewed-by: Michael Matloob --- doc/go1.16.html | 12 ++ src/cmd/go/alldocs.go | 27 +++ src/cmd/go/internal/modload/init.go | 46 ++++- src/cmd/go/internal/work/build.go | 192 +++++++++++++++++++++ .../mod/example.com_cmd_v1.0.0-exclude.txt | 28 +++ .../mod/example.com_cmd_v1.0.0-newerself.txt | 28 +++ .../mod/example.com_cmd_v1.0.0-replace.txt | 28 +++ src/cmd/go/testdata/mod/example.com_cmd_v1.0.0.txt | 27 +++ src/cmd/go/testdata/mod/example.com_cmd_v1.9.0.txt | 30 ++++ .../go/testdata/script/mod_install_pkg_version.txt | 187 ++++++++++++++++++++ src/cmd/go/testdata/script/mod_outside.txt | 8 +- 11 files changed, 605 insertions(+), 8 deletions(-) create mode 100644 src/cmd/go/testdata/mod/example.com_cmd_v1.0.0-exclude.txt create mode 100644 src/cmd/go/testdata/mod/example.com_cmd_v1.0.0-newerself.txt create mode 100644 src/cmd/go/testdata/mod/example.com_cmd_v1.0.0-replace.txt create mode 100644 src/cmd/go/testdata/mod/example.com_cmd_v1.0.0.txt create mode 100644 src/cmd/go/testdata/mod/example.com_cmd_v1.9.0.txt create mode 100644 src/cmd/go/testdata/script/mod_install_pkg_version.txt diff --git a/doc/go1.16.html b/doc/go1.16.html index 95e63d0d5a..f177226269 100644 --- a/doc/go1.16.html +++ b/doc/go1.16.html @@ -43,6 +43,18 @@ Do not send CLs removing the interior tags from such phrases.

Go command

+

+ go install now accepts arguments with + version suffixes (for example, go install + example.com/cmd@v1.0.0). This causes go + install to build and install packages in module-aware mode, + ignoring the go.mod file in the current directory or any parent + directory, if there is one. This is useful for installing executables without + affecting the dependencies of the main module.
+ TODO: write and link to section in golang.org/ref/mod
+ TODO: write and link to blog post +

+

retract directives may now be used in a go.mod file to indicate that certain published versions of the module should not be used diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 8ad4f66d09..104aea6c7f 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -715,6 +715,33 @@ // environment variable is not set. Executables in $GOROOT // are installed in $GOROOT/bin or $GOTOOLDIR instead of $GOBIN. // +// If the arguments have version suffixes (like @latest or @v1.0.0), "go install" +// builds packages in module-aware mode, ignoring the go.mod file in the current +// directory or any parent directory, if there is one. This is useful for +// installing executables without affecting the dependencies of the main module. +// To eliminate ambiguity about which module versions are used in the build, the +// arguments must satisfy the following constraints: +// +// - Arguments must be package paths or package patterns (with "..." wildcards). +// They must not be standard packages (like fmt), meta-patterns (std, cmd, +// all), or relative or absolute file paths. +// - All arguments must have the same version suffix. Different queries are not +// allowed, even if they refer to the same version. +// - All arguments must refer to packages in the same module at the same version. +// - No module is considered the "main" module. If the module containing +// packages named on the command line has a go.mod file, it must not contain +// directives (replace and exclude) that would cause it to be interpreted +// differently than if it were the main module. The module must not require +// a higher version of itself. +// - Package path arguments must refer to main packages. Pattern arguments +// will only match main packages. +// +// If the arguments don't have version suffixes, "go install" may run in +// module-aware mode or GOPATH mode, depending on the GO111MODULE environment +// variable and the presence of a go.mod file. See 'go help modules' for details. +// If module-aware mode is enabled, "go install" runs in the context of the main +// module. +// // When module-aware mode is disabled, other packages are installed in the // directory $GOPATH/pkg/$GOOS_$GOARCH. When module-aware mode is enabled, // other packages are built and cached but not installed. diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go index 1f50dcb11c..2f0f60b263 100644 --- a/src/cmd/go/internal/modload/init.go +++ b/src/cmd/go/internal/modload/init.go @@ -35,8 +35,7 @@ import ( ) var ( - mustUseModules = false - initialized bool + initialized bool modRoot string Target module.Version @@ -55,9 +54,33 @@ var ( CmdModInit bool // running 'go mod init' CmdModModule string // module argument for 'go mod init' + // RootMode determines whether a module root is needed. + RootMode Root + + // ForceUseModules may be set to force modules to be enabled when + // GO111MODULE=auto or to report an error when GO111MODULE=off. + ForceUseModules bool + allowMissingModuleImports bool ) +type Root int + +const ( + // AutoRoot is the default for most commands. modload.Init will look for + // a go.mod file in the current directory or any parent. If none is found, + // modules may be disabled (GO111MODULE=on) or commands may run in a + // limited module mode. + AutoRoot Root = iota + + // NoRoot is used for commands that run in module mode and ignore any go.mod + // file the current directory or in parent directories. + NoRoot + + // TODO(jayconrod): add NeedRoot for commands like 'go mod vendor' that + // don't make sense without a main module. +) + // ModFile returns the parsed go.mod file. // // Note that after calling ImportPaths or LoadBuildList, @@ -92,15 +115,19 @@ func Init() { // Keep in sync with WillBeEnabled. We perform extra validation here, and // there are lots of diagnostics and side effects, so we can't use // WillBeEnabled directly. + var mustUseModules bool env := cfg.Getenv("GO111MODULE") switch env { default: base.Fatalf("go: unknown environment setting GO111MODULE=%s", env) case "auto", "": - mustUseModules = false + mustUseModules = ForceUseModules case "on": mustUseModules = true case "off": + if ForceUseModules { + base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'") + } mustUseModules = false return } @@ -135,6 +162,10 @@ func Init() { if CmdModInit { // Running 'go mod init': go.mod will be created in current directory. modRoot = base.Cwd + } else if RootMode == NoRoot { + // TODO(jayconrod): report an error if -mod -modfile is explicitly set on + // the command line. Ignore those flags if they come from GOFLAGS. + modRoot = "" } else { modRoot = findModuleRoot(base.Cwd) if modRoot == "" { @@ -154,6 +185,9 @@ func Init() { // when it happens. See golang.org/issue/26708. modRoot = "" fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir()) + if !mustUseModules { + return + } } } if cfg.ModFile != "" && !strings.HasSuffix(cfg.ModFile, ".mod") { @@ -219,10 +253,12 @@ func init() { // be called until the command is installed and flags are parsed. Instead of // calling Init and Enabled, the main package can call this function. func WillBeEnabled() bool { - if modRoot != "" || mustUseModules { + if modRoot != "" || cfg.ModulesEnabled { + // Already enabled. return true } if initialized { + // Initialized, not enabled. return false } @@ -263,7 +299,7 @@ func WillBeEnabled() bool { // (usually through MustModRoot). func Enabled() bool { Init() - return modRoot != "" || mustUseModules + return modRoot != "" || cfg.ModulesEnabled } // ModRoot returns the root of the main module. diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go index e99982ed36..990e5d9ecd 100644 --- a/src/cmd/go/internal/work/build.go +++ b/src/cmd/go/internal/work/build.go @@ -9,8 +9,10 @@ import ( "errors" "fmt" "go/build" + "internal/goroot" "os" "os/exec" + "path" "path/filepath" "runtime" "strings" @@ -18,8 +20,13 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/load" + "cmd/go/internal/modfetch" + "cmd/go/internal/modload" "cmd/go/internal/search" "cmd/go/internal/trace" + + "golang.org/x/mod/modfile" + "golang.org/x/mod/module" ) var CmdBuild = &base.Command{ @@ -440,6 +447,33 @@ variable, which defaults to $GOPATH/bin or $HOME/go/bin if the GOPATH environment variable is not set. Executables in $GOROOT are installed in $GOROOT/bin or $GOTOOLDIR instead of $GOBIN. +If the arguments have version suffixes (like @latest or @v1.0.0), "go install" +builds packages in module-aware mode, ignoring the go.mod file in the current +directory or any parent directory, if there is one. This is useful for +installing executables without affecting the dependencies of the main module. +To eliminate ambiguity about which module versions are used in the build, the +arguments must satisfy the following constraints: + +- Arguments must be package paths or package patterns (with "..." wildcards). + They must not be standard packages (like fmt), meta-patterns (std, cmd, + all), or relative or absolute file paths. +- All arguments must have the same version suffix. Different queries are not + allowed, even if they refer to the same version. +- All arguments must refer to packages in the same module at the same version. +- No module is considered the "main" module. If the module containing + packages named on the command line has a go.mod file, it must not contain + directives (replace and exclude) that would cause it to be interpreted + differently than if it were the main module. The module must not require + a higher version of itself. +- Package path arguments must refer to main packages. Pattern arguments + will only match main packages. + +If the arguments don't have version suffixes, "go install" may run in +module-aware mode or GOPATH mode, depending on the GO111MODULE environment +variable and the presence of a go.mod file. See 'go help modules' for details. +If module-aware mode is enabled, "go install" runs in the context of the main +module. + When module-aware mode is disabled, other packages are installed in the directory $GOPATH/pkg/$GOOS_$GOARCH. When module-aware mode is enabled, other packages are built and cached but not installed. @@ -510,6 +544,12 @@ func libname(args []string, pkgs []*load.Package) (string, error) { } func runInstall(ctx context.Context, cmd *base.Command, args []string) { + for _, arg := range args { + if strings.Contains(arg, "@") && !build.IsLocalImport(arg) && !filepath.IsAbs(arg) { + installOutsideModule(ctx, args) + return + } + } BuildInit() InstallPackages(ctx, args, load.PackagesForBuild(ctx, args)) } @@ -634,6 +674,158 @@ func InstallPackages(ctx context.Context, patterns []string, pkgs []*load.Packag } } +// installOutsideModule implements 'go install pkg@version'. It builds and +// installs one or more main packages in module mode while ignoring any go.mod +// in the current directory or parent directories. +// +// See golang.org/issue/40276 for details and rationale. +func installOutsideModule(ctx context.Context, args []string) { + modload.ForceUseModules = true + modload.RootMode = modload.NoRoot + modload.AllowMissingModuleImports() + modload.Init() + + // Check that the arguments satisfy syntactic constraints. + var version string + for _, arg := range args { + if i := strings.Index(arg, "@"); i >= 0 { + version = arg[i+1:] + if version == "" { + base.Fatalf("go install %s: version must not be empty", arg) + } + break + } + } + patterns := make([]string, len(args)) + for i, arg := range args { + if !strings.HasSuffix(arg, "@"+version) { + base.Errorf("go install %s: all arguments must have the same version (@%s)", arg, version) + continue + } + p := arg[:len(arg)-len(version)-1] + switch { + case build.IsLocalImport(p): + base.Errorf("go install %s: argument must be a package path, not a relative path", arg) + case filepath.IsAbs(p): + base.Errorf("go install %s: argument must be a package path, not an absolute path", arg) + case search.IsMetaPackage(p): + base.Errorf("go install %s: argument must be a package path, not a meta-package", arg) + case path.Clean(p) != p: + base.Errorf("go install %s: argument must be a clean package path", arg) + case !strings.Contains(p, "...") && search.IsStandardImportPath(p) && goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, p): + base.Errorf("go install %s: argument must not be a package in the standard library", arg) + default: + patterns[i] = p + } + } + base.ExitIfErrors() + BuildInit() + + // Query the module providing the first argument, load its go.mod file, and + // check that it doesn't contain directives that would cause it to be + // interpreted differently if it were the main module. + // + // If multiple modules match the first argument, accept the longest match + // (first result). It's possible this module won't provide packages named by + // later arguments, and other modules would. Let's not try to be too + // magical though. + allowed := modload.CheckAllowed + if modload.IsRevisionQuery(version) { + // Don't check for retractions if a specific revision is requested. + allowed = nil + } + qrs, err := modload.QueryPattern(ctx, patterns[0], version, allowed) + if err != nil { + base.Fatalf("go install %s: %v", args[0], err) + } + installMod := qrs[0].Mod + data, err := modfetch.GoMod(installMod.Path, installMod.Version) + if err != nil { + base.Fatalf("go install %s: %v", args[0], err) + } + f, err := modfile.Parse("go.mod", data, nil) + if err != nil { + base.Fatalf("go install %s: %s: %v", args[0], installMod, err) + } + directiveFmt := "go install %s: %s\n" + + "\tThe go.mod file for the module providing named packages contains one or\n" + + "\tmore %s directives. It must not contain directives that would cause\n" + + "\tit to be interpreted differently than if it were the main module." + if len(f.Replace) > 0 { + base.Fatalf(directiveFmt, args[0], installMod, "replace") + } + if len(f.Exclude) > 0 { + base.Fatalf(directiveFmt, args[0], installMod, "exclude") + } + + // Initialize the build list using a dummy main module that requires the + // module providing the packages on the command line. + target := module.Version{Path: "go-install-target"} + modload.SetBuildList([]module.Version{target, installMod}) + + // Load packages for all arguments. Ignore non-main packages. + // Print a warning if an argument contains "..." and matches no main packages. + // PackagesForBuild already prints warnings for patterns that don't match any + // packages, so be careful not to double print. + matchers := make([]func(string) bool, len(patterns)) + for i, p := range patterns { + if strings.Contains(p, "...") { + matchers[i] = search.MatchPattern(p) + } + } + + // TODO(golang.org/issue/40276): don't report errors loading non-main packages + // matched by a pattern. + pkgs := load.PackagesForBuild(ctx, patterns) + mainPkgs := make([]*load.Package, 0, len(pkgs)) + mainCount := make([]int, len(patterns)) + nonMainCount := make([]int, len(patterns)) + for _, pkg := range pkgs { + if pkg.Name == "main" { + mainPkgs = append(mainPkgs, pkg) + for i := range patterns { + if matchers[i] != nil && matchers[i](pkg.ImportPath) { + mainCount[i]++ + } + } + } else { + for i := range patterns { + if matchers[i] == nil && patterns[i] == pkg.ImportPath { + base.Errorf("go install: package %s is not a main package", pkg.ImportPath) + } else if matchers[i] != nil && matchers[i](pkg.ImportPath) { + nonMainCount[i]++ + } + } + } + } + base.ExitIfErrors() + for i, p := range patterns { + if matchers[i] != nil && mainCount[i] == 0 && nonMainCount[i] > 0 { + fmt.Fprintf(os.Stderr, "go: warning: %q matched no main packages\n", p) + } + } + + // Check that named packages are all provided by the same module. + for _, mod := range modload.LoadedModules() { + if mod.Path == installMod.Path && mod.Version != installMod.Version { + base.Fatalf("go install: %s: module requires a higher version of itself (%s)", installMod, mod.Version) + } + } + for _, pkg := range mainPkgs { + if pkg.Module == nil { + // Packages in std, cmd, and their vendored dependencies + // don't have this field set. + base.Errorf("go install: package %s not provided by module %s", pkg.ImportPath, installMod) + } else if pkg.Module.Path != installMod.Path || pkg.Module.Version != installMod.Version { + base.Errorf("go install: package %s provided by module %s@%s\n\tAll packages must be provided by the same module (%s).", pkg.ImportPath, pkg.Module.Path, pkg.Module.Version, installMod) + } + } + base.ExitIfErrors() + + // Build and install the packages. + InstallPackages(ctx, patterns, mainPkgs) +} + // ExecCmd is the command to use to run user binaries. // Normally it is empty, meaning run the binaries directly. // If cross-compiling and running on a remote system or diff --git a/src/cmd/go/testdata/mod/example.com_cmd_v1.0.0-exclude.txt b/src/cmd/go/testdata/mod/example.com_cmd_v1.0.0-exclude.txt new file mode 100644 index 0000000000..c883d8a774 --- /dev/null +++ b/src/cmd/go/testdata/mod/example.com_cmd_v1.0.0-exclude.txt @@ -0,0 +1,28 @@ +example.com/cmd contains main packages. + +-- .info -- +{"Version":"v1.0.0-exclude"} +-- .mod -- +module example.com/cmd + +go 1.16 + +exclude rsc.io/quote v1.5.2 +-- go.mod -- +module example.com/cmd + +go 1.16 + +exclude rsc.io/quote v1.5.2 +-- a/a.go -- +package main + +func main() {} +-- b/b.go -- +package main + +func main() {} +-- err/err.go -- +package err + +var X = DoesNotCompile diff --git a/src/cmd/go/testdata/mod/example.com_cmd_v1.0.0-newerself.txt b/src/cmd/go/testdata/mod/example.com_cmd_v1.0.0-newerself.txt new file mode 100644 index 0000000000..7670f29ffd --- /dev/null +++ b/src/cmd/go/testdata/mod/example.com_cmd_v1.0.0-newerself.txt @@ -0,0 +1,28 @@ +example.com/cmd contains main packages. + +-- .info -- +{"Version":"v1.0.0-newerself"} +-- .mod -- +module example.com/cmd + +go 1.16 + +require example.com/cmd v1.0.0 +-- go.mod -- +module example.com/cmd + +go 1.16 + +require example.com/cmd v1.0.0 +-- a/a.go -- +package main + +func main() {} +-- b/b.go -- +package main + +func main() {} +-- err/err.go -- +package err + +var X = DoesNotCompile diff --git a/src/cmd/go/testdata/mod/example.com_cmd_v1.0.0-replace.txt b/src/cmd/go/testdata/mod/example.com_cmd_v1.0.0-replace.txt new file mode 100644 index 0000000000..581a496035 --- /dev/null +++ b/src/cmd/go/testdata/mod/example.com_cmd_v1.0.0-replace.txt @@ -0,0 +1,28 @@ +example.com/cmd contains main packages. + +-- .info -- +{"Version":"v1.0.0-replace"} +-- .mod -- +module example.com/cmd + +go 1.16 + +replace rsc.io/quote => rsc.io/quote v1.5.2 +-- go.mod -- +module example.com/cmd + +go 1.16 + +replace rsc.io/quote => rsc.io/quote v1.5.2 +-- a/a.go -- +package main + +func main() {} +-- b/b.go -- +package main + +func main() {} +-- err/err.go -- +package err + +var X = DoesNotCompile diff --git a/src/cmd/go/testdata/mod/example.com_cmd_v1.0.0.txt b/src/cmd/go/testdata/mod/example.com_cmd_v1.0.0.txt new file mode 100644 index 0000000000..ee439384d2 --- /dev/null +++ b/src/cmd/go/testdata/mod/example.com_cmd_v1.0.0.txt @@ -0,0 +1,27 @@ +example.com/cmd contains main packages. + +v1.0.0 is the latest non-retracted version. Other versions contain errors or +detectable problems. + +-- .info -- +{"Version":"v1.0.0"} +-- .mod -- +module example.com/cmd + +go 1.16 +-- go.mod -- +module example.com/cmd + +go 1.16 +-- a/a.go -- +package main + +func main() {} +-- b/b.go -- +package main + +func main() {} +-- err/err.go -- +package err + +var X = DoesNotCompile diff --git a/src/cmd/go/testdata/mod/example.com_cmd_v1.9.0.txt b/src/cmd/go/testdata/mod/example.com_cmd_v1.9.0.txt new file mode 100644 index 0000000000..9298afb1fb --- /dev/null +++ b/src/cmd/go/testdata/mod/example.com_cmd_v1.9.0.txt @@ -0,0 +1,30 @@ +example.com/cmd contains main packages. + +-- .info -- +{"Version":"v1.9.0"} +-- .mod -- +module example.com/cmd + +go 1.16 + +// this is a bad version +retract v1.9.0 +-- go.mod -- +module example.com/cmd + +go 1.16 + +// this is a bad version +retract v1.9.0 +-- a/a.go -- +package main + +func main() {} +-- b/b.go -- +package main + +func main() {} +-- err/err.go -- +package err + +var X = DoesNotCompile diff --git a/src/cmd/go/testdata/script/mod_install_pkg_version.txt b/src/cmd/go/testdata/script/mod_install_pkg_version.txt new file mode 100644 index 0000000000..7e6d4e8e7c --- /dev/null +++ b/src/cmd/go/testdata/script/mod_install_pkg_version.txt @@ -0,0 +1,187 @@ +# 'go install pkg@version' works outside a module. +env GO111MODULE=auto +go install example.com/cmd/a@v1.0.0 +exists $GOPATH/bin/a$GOEXE +rm $GOPATH/bin + + +# 'go install pkg@version' reports an error if modules are disabled. +env GO111MODULE=off +! go install example.com/cmd/a@v1.0.0 +stderr '^go: modules disabled by GO111MODULE=off; see ''go help modules''$' +env GO111MODULE=auto + + +# 'go install pkg@version' ignores go.mod in current directory. +cd m +cp go.mod go.mod.orig +! go list -m all +stderr 'example.com/cmd@v1.1.0-doesnotexist:.*404 Not Found' +go install example.com/cmd/a@latest +cmp go.mod go.mod.orig +exists $GOPATH/bin/a$GOEXE +go version -m $GOPATH/bin/a$GOEXE +stdout '^\tmod\texample.com/cmd\tv1.0.0\t' # "latest", not from go.mod +rm $GOPATH/bin/a +cd .. + + +# Every test case requires linking, so we only cover the most important cases +# when -short is set. +[short] stop + + +# 'go install pkg@version' works on a module that doesn't have a go.mod file +# and with a module whose go.mod file has missing requirements. +# With a proxy, the two cases are indistinguishable. +go install rsc.io/fortune@v1.0.0 +stderr '^go: found rsc.io/quote in rsc.io/quote v1.5.2$' +exists $GOPATH/bin/fortune$GOEXE +! exists $GOPATH/pkg/mod/rsc.io/fortune@v1.0.0/go.mod # no go.mod file +go version -m $GOPATH/bin/fortune$GOEXE +stdout '^\tdep\trsc.io/quote\tv1.5.2\t' # latest version of fortune's dependency +rm $GOPATH/bin + + +# 'go install dir@version' works like a normal 'go install' command if +# dir is a relative or absolute path. +env GO111MODULE=on +go mod download rsc.io/fortune@v1.0.0 +! go install $GOPATH/pkg/mod/rsc.io/fortune@v1.0.0 +stderr '^go: cannot find main module; see ''go help modules''$' +! go install ../pkg/mod/rsc.io/fortune@v1.0.0 +stderr '^go: cannot find main module; see ''go help modules''$' +mkdir tmp +cd tmp +go mod init tmp +go mod edit -require=rsc.io/fortune@v1.0.0 +! go install -mod=readonly $GOPATH/pkg/mod/rsc.io/fortune@v1.0.0 +stderr '^go: updates to go.sum needed, disabled by -mod=readonly$' +! go install -mod=readonly ../../pkg/mod/rsc.io/fortune@v1.0.0 +stderr '^go: updates to go.sum needed, disabled by -mod=readonly$' +go get -d rsc.io/fortune@v1.0.0 +go install -mod=readonly $GOPATH/pkg/mod/rsc.io/fortune@v1.0.0 +exists $GOPATH/bin/fortune$GOEXE +cd .. +rm tmp +rm $GOPATH/bin +env GO111MODULE=auto + +# 'go install pkg@version' reports errors for meta packages, std packages, +# and directories. +! go install std@v1.0.0 +stderr '^go install std@v1.0.0: argument must be a package path, not a meta-package$' +! go install fmt@v1.0.0 +stderr '^go install fmt@v1.0.0: argument must not be a package in the standard library$' +! go install example.com//cmd/a@v1.0.0 +stderr '^go install example.com//cmd/a@v1.0.0: argument must be a clean package path$' +! go install example.com/cmd/a@v1.0.0 ./x@v1.0.0 +stderr '^go install ./x@v1.0.0: argument must be a package path, not a relative path$' +! go install example.com/cmd/a@v1.0.0 $GOPATH/src/x@v1.0.0 +stderr '^go install '$WORK'[/\\]gopath/src/x@v1.0.0: argument must be a package path, not an absolute path$' +! go install example.com/cmd/a@v1.0.0 cmd/...@v1.0.0 +stderr '^go install: package cmd/go not provided by module example.com/cmd@v1.0.0$' + +# 'go install pkg@version' should accept multiple arguments but report an error +# if the version suffixes are different, even if they refer to the same version. +go install example.com/cmd/a@v1.0.0 example.com/cmd/b@v1.0.0 +exists $GOPATH/bin/a$GOEXE +exists $GOPATH/bin/b$GOEXE +rm $GOPATH/bin + +env GO111MODULE=on +go list -m example.com/cmd@latest +stdout '^example.com/cmd v1.0.0$' +env GO111MODULE=auto + +! go install example.com/cmd/a@v1.0.0 example.com/cmd/b@latest +stderr '^go install example.com/cmd/b@latest: all arguments must have the same version \(@v1.0.0\)$' + + +# 'go install pkg@version' should report an error if the arguments are in +# different modules. +! go install example.com/cmd/a@v1.0.0 rsc.io/fortune@v1.0.0 +stderr '^go install: package rsc.io/fortune provided by module rsc.io/fortune@v1.0.0\n\tAll packages must be provided by the same module \(example.com/cmd@v1.0.0\).$' + + +# 'go install pkg@version' should report an error if an argument is not +# a main package. +! go install example.com/cmd/a@v1.0.0 example.com/cmd/err@v1.0.0 +stderr '^go install: package example.com/cmd/err is not a main package$' + +# Wildcards should match only main packages. This module has a non-main package +# with an error, so we'll know if that gets built. +mkdir tmp +cd tmp +go mod init m +go get -d example.com/cmd@v1.0.0 +! go build example.com/cmd/... +stderr 'err[/\\]err.go:3:9: undefined: DoesNotCompile$' +cd .. + +go install example.com/cmd/...@v1.0.0 +exists $GOPATH/bin/a$GOEXE +exists $GOPATH/bin/b$GOEXE +rm $GOPATH/bin + +# If a wildcard matches no packages, we should see a warning. +! go install example.com/cmd/nomatch...@v1.0.0 +stderr '^go install example.com/cmd/nomatch\.\.\.@v1.0.0: module example.com/cmd@v1.0.0 found, but does not contain packages matching example.com/cmd/nomatch\.\.\.$' +go install example.com/cmd/a@v1.0.0 example.com/cmd/nomatch...@v1.0.0 +stderr '^go: warning: "example.com/cmd/nomatch\.\.\." matched no packages$' + +# If a wildcard matches only non-main packges, we should see a different warning. +go install example.com/cmd/err...@v1.0.0 +stderr '^go: warning: "example.com/cmd/err\.\.\." matched no main packages$' + + +# 'go install pkg@version' should report errors if the module contains +# replace or exclude directives. +go mod download example.com/cmd@v1.0.0-replace +! go install example.com/cmd/a@v1.0.0-replace +cmp stderr replace-err + +go mod download example.com/cmd@v1.0.0-exclude +! go install example.com/cmd/a@v1.0.0-exclude +cmp stderr exclude-err + +# 'go install pkg@version' should report an error if the module requires a +# higher version of itself. +! go install example.com/cmd/a@v1.0.0-newerself +stderr '^go install: example.com/cmd@v1.0.0-newerself: module requires a higher version of itself \(v1.0.0\)$' + + +# 'go install pkg@version' will only match a retracted version if it's +# explicitly requested. +env GO111MODULE=on +go list -m -versions example.com/cmd +! stdout v1.9.0 +go list -m -versions -retracted example.com/cmd +stdout v1.9.0 +go install example.com/cmd/a@latest +go version -m $GOPATH/bin/a$GOEXE +stdout '^\tmod\texample.com/cmd\tv1.0.0\t' +go install example.com/cmd/a@v1.9.0 +go version -m $GOPATH/bin/a$GOEXE +stdout '^\tmod\texample.com/cmd\tv1.9.0\t' + +-- m/go.mod -- +module m + +go 1.16 + +require example.com/cmd v1.1.0-doesnotexist +-- x/x.go -- +package main + +func main() {} +-- replace-err -- +go install example.com/cmd/a@v1.0.0-replace: example.com/cmd@v1.0.0-replace + The go.mod file for the module providing named packages contains one or + more replace directives. It must not contain directives that would cause + it to be interpreted differently than if it were the main module. +-- exclude-err -- +go install example.com/cmd/a@v1.0.0-exclude: example.com/cmd@v1.0.0-exclude + The go.mod file for the module providing named packages contains one or + more exclude directives. It must not contain directives that would cause + it to be interpreted differently than if it were the main module. diff --git a/src/cmd/go/testdata/script/mod_outside.txt b/src/cmd/go/testdata/script/mod_outside.txt index 03ef576168..2001c45c3c 100644 --- a/src/cmd/go/testdata/script/mod_outside.txt +++ b/src/cmd/go/testdata/script/mod_outside.txt @@ -177,9 +177,11 @@ go doc fmt ! go doc example.com/version stderr 'doc: cannot find module providing package example.com/version: working directory is not part of a module' -# 'go install' with a version should fail due to syntax. -! go install example.com/printversion@v1.0.0 -stderr 'can only use path@version syntax with' +# 'go install' with a version should succeed if all constraints are met. +# See mod_install_pkg_version. +rm $GOPATH/bin +go install example.com/printversion@v0.1.0 +exists $GOPATH/bin/printversion$GOEXE # 'go install' should fail if a package argument must be resolved to a module. ! go install example.com/printversion -- cgit v1.2.3-54-g00ecf From 03875bd9bc112d25a4496f7ff22888f23a26baea Mon Sep 17 00:00:00 2001 From: Jay Conrod Date: Fri, 11 Sep 2020 13:30:43 -0400 Subject: cmd/go: add modload.NeedRoot mode for commands that need module root This makes error reporting a bit more consistent for 'go mod' subcommands. Most of these commands only work in module mode when a go.mod file is present. Setting modload.ForceUseModules reports an error when GO111MODULE=off. Setting modload.RootMode to modload.NeedRoot reports an error when no go.mod file is present. Change-Id: I1daa8d2971cb8658e0c804765839d903734a412e Reviewed-on: https://go-review.googlesource.com/c/go/+/254369 Reviewed-by: Bryan C. Mills Reviewed-by: Michael Matloob --- src/cmd/go/internal/modcmd/download.go | 4 +--- src/cmd/go/internal/modcmd/graph.go | 11 ++--------- src/cmd/go/internal/modcmd/init.go | 4 +--- src/cmd/go/internal/modcmd/tidy.go | 2 ++ src/cmd/go/internal/modcmd/vendor.go | 2 ++ src/cmd/go/internal/modcmd/verify.go | 11 ++--------- src/cmd/go/internal/modcmd/why.go | 2 ++ src/cmd/go/internal/modload/init.go | 8 ++++++-- src/cmd/go/testdata/script/mod_off.txt | 4 ++-- src/cmd/go/testdata/script/mod_off_init.txt | 2 +- 10 files changed, 21 insertions(+), 29 deletions(-) diff --git a/src/cmd/go/internal/modcmd/download.go b/src/cmd/go/internal/modcmd/download.go index 6227fd9f33..050a2e0e12 100644 --- a/src/cmd/go/internal/modcmd/download.go +++ b/src/cmd/go/internal/modcmd/download.go @@ -80,9 +80,7 @@ type moduleJSON struct { func runDownload(ctx context.Context, cmd *base.Command, args []string) { // Check whether modules are enabled and whether we're in a module. - if cfg.Getenv("GO111MODULE") == "off" { - base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'") - } + modload.ForceUseModules = true if !modload.HasModRoot() && len(args) == 0 { base.Fatalf("go mod download: no modules specified (see 'go help mod download')") } diff --git a/src/cmd/go/internal/modcmd/graph.go b/src/cmd/go/internal/modcmd/graph.go index a149b65605..3277548c23 100644 --- a/src/cmd/go/internal/modcmd/graph.go +++ b/src/cmd/go/internal/modcmd/graph.go @@ -13,7 +13,6 @@ import ( "sort" "cmd/go/internal/base" - "cmd/go/internal/cfg" "cmd/go/internal/modload" "golang.org/x/mod/module" @@ -39,14 +38,8 @@ func runGraph(ctx context.Context, cmd *base.Command, args []string) { if len(args) > 0 { base.Fatalf("go mod graph: graph takes no arguments") } - // Checks go mod expected behavior - if !modload.Enabled() { - if cfg.Getenv("GO111MODULE") == "off" { - base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'") - } else { - base.Fatalf("go: cannot find main module; see 'go help modules'") - } - } + modload.ForceUseModules = true + modload.RootMode = modload.NeedRoot modload.LoadAllModules(ctx) reqs := modload.MinReqs() diff --git a/src/cmd/go/internal/modcmd/init.go b/src/cmd/go/internal/modcmd/init.go index 21b235653e..7cfc0e6f5b 100644 --- a/src/cmd/go/internal/modcmd/init.go +++ b/src/cmd/go/internal/modcmd/init.go @@ -40,9 +40,7 @@ func runInit(ctx context.Context, cmd *base.Command, args []string) { if len(args) == 1 { modload.CmdModModule = args[0] } - if os.Getenv("GO111MODULE") == "off" { - base.Fatalf("go mod init: modules disabled by GO111MODULE=off; see 'go help modules'") - } + modload.ForceUseModules = true modFilePath := modload.ModFilePath() if _, err := os.Stat(modFilePath); err == nil { base.Fatalf("go mod init: go.mod already exists") diff --git a/src/cmd/go/internal/modcmd/tidy.go b/src/cmd/go/internal/modcmd/tidy.go index 30df674ef6..cbe3ded5f8 100644 --- a/src/cmd/go/internal/modcmd/tidy.go +++ b/src/cmd/go/internal/modcmd/tidy.go @@ -50,6 +50,8 @@ func runTidy(ctx context.Context, cmd *base.Command, args []string) { // that are in 'all' but outside of the main module, we must explicitly // request that their test dependencies be included. modload.LoadTests = true + modload.ForceUseModules = true + modload.RootMode = modload.NeedRoot modload.LoadALL(ctx) modload.TidyBuildList() diff --git a/src/cmd/go/internal/modcmd/vendor.go b/src/cmd/go/internal/modcmd/vendor.go index 91d2509452..44094b7252 100644 --- a/src/cmd/go/internal/modcmd/vendor.go +++ b/src/cmd/go/internal/modcmd/vendor.go @@ -47,6 +47,8 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) { if len(args) != 0 { base.Fatalf("go mod vendor: vendor takes no arguments") } + modload.ForceUseModules = true + modload.RootMode = modload.NeedRoot pkgs := modload.LoadVendor(ctx) vdir := filepath.Join(modload.ModRoot(), "vendor") diff --git a/src/cmd/go/internal/modcmd/verify.go b/src/cmd/go/internal/modcmd/verify.go index 7700588bde..bd591d3f32 100644 --- a/src/cmd/go/internal/modcmd/verify.go +++ b/src/cmd/go/internal/modcmd/verify.go @@ -14,7 +14,6 @@ import ( "runtime" "cmd/go/internal/base" - "cmd/go/internal/cfg" "cmd/go/internal/modfetch" "cmd/go/internal/modload" @@ -45,14 +44,8 @@ func runVerify(ctx context.Context, cmd *base.Command, args []string) { // NOTE(rsc): Could take a module pattern. base.Fatalf("go mod verify: verify takes no arguments") } - // Checks go mod expected behavior - if !modload.Enabled() || !modload.HasModRoot() { - if cfg.Getenv("GO111MODULE") == "off" { - base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'") - } else { - base.Fatalf("go: cannot find main module; see 'go help modules'") - } - } + modload.ForceUseModules = true + modload.RootMode = modload.NeedRoot // Only verify up to GOMAXPROCS zips at once. type token struct{} diff --git a/src/cmd/go/internal/modcmd/why.go b/src/cmd/go/internal/modcmd/why.go index 8454fdfec6..ea7c28e0b8 100644 --- a/src/cmd/go/internal/modcmd/why.go +++ b/src/cmd/go/internal/modcmd/why.go @@ -61,6 +61,8 @@ func init() { } func runWhy(ctx context.Context, cmd *base.Command, args []string) { + modload.ForceUseModules = true + modload.RootMode = modload.NeedRoot loadALL := modload.LoadALL if *whyVendor { loadALL = modload.LoadVendor diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go index 2f0f60b263..f93abee96d 100644 --- a/src/cmd/go/internal/modload/init.go +++ b/src/cmd/go/internal/modload/init.go @@ -77,8 +77,9 @@ const ( // file the current directory or in parent directories. NoRoot - // TODO(jayconrod): add NeedRoot for commands like 'go mod vendor' that - // don't make sense without a main module. + // NeedRoot is used for commands that must run in module mode and don't + // make sense without a main module. + NeedRoot ) // ModFile returns the parsed go.mod file. @@ -172,6 +173,9 @@ func Init() { if cfg.ModFile != "" { base.Fatalf("go: cannot find main module, but -modfile was set.\n\t-modfile cannot be used to set the module root directory.") } + if RootMode == NeedRoot { + base.Fatalf("go: cannot find main module; see 'go help modules'") + } if !mustUseModules { // GO111MODULE is 'auto', and we can't find a module root. // Stay in GOPATH mode. diff --git a/src/cmd/go/testdata/script/mod_off.txt b/src/cmd/go/testdata/script/mod_off.txt index cada6deb1d..a73a58d4d0 100644 --- a/src/cmd/go/testdata/script/mod_off.txt +++ b/src/cmd/go/testdata/script/mod_off.txt @@ -4,7 +4,7 @@ env GO111MODULE=off # GO111MODULE=off when outside of GOPATH will fatal # with an error message, even with some source code in the directory and a go.mod. ! go mod init -stderr 'go mod init: modules disabled by GO111MODULE=off; see ''go help modules''' +stderr 'go: modules disabled by GO111MODULE=off; see ''go help modules''' ! go mod graph stderr 'go: modules disabled by GO111MODULE=off; see ''go help modules''' ! go mod verify @@ -16,7 +16,7 @@ stderr 'go: modules disabled by GO111MODULE=off; see ''go help modules''' mkdir z cd z ! go mod init -stderr 'go mod init: modules disabled by GO111MODULE=off; see ''go help modules''' +stderr 'go: modules disabled by GO111MODULE=off; see ''go help modules''' ! go mod graph stderr 'go: modules disabled by GO111MODULE=off; see ''go help modules''' ! go mod verify diff --git a/src/cmd/go/testdata/script/mod_off_init.txt b/src/cmd/go/testdata/script/mod_off_init.txt index 1339c8aef9..2aec0b3ed5 100644 --- a/src/cmd/go/testdata/script/mod_off_init.txt +++ b/src/cmd/go/testdata/script/mod_off_init.txt @@ -2,4 +2,4 @@ # ignored anyway due to GO111MODULE=off. env GO111MODULE=off ! go mod init -stderr 'go mod init: modules disabled by GO111MODULE=off; see ''go help modules''' +stderr 'go: modules disabled by GO111MODULE=off; see ''go help modules''' -- cgit v1.2.3-54-g00ecf From f1c400a06393aad55cd4758fc78ccd7aec379ec0 Mon Sep 17 00:00:00 2001 From: Jay Conrod Date: Tue, 15 Sep 2020 10:14:15 -0400 Subject: cmd/go: fix broken mod_outside test Since CL 254369, 'go mod graph' now reports an error when invoked outside a module. This broke the mod_outside test, which expected 'go mod graph' to succeed with no output. Change-Id: Ic30ee68f1f4c4d33795bdf7df70a7631fb9395e5 Reviewed-on: https://go-review.googlesource.com/c/go/+/255017 Trust: Jay Conrod Run-TryBot: Jay Conrod Reviewed-by: Bryan C. Mills TryBot-Result: Go Bot --- src/cmd/go/testdata/script/mod_outside.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/cmd/go/testdata/script/mod_outside.txt b/src/cmd/go/testdata/script/mod_outside.txt index 2001c45c3c..e398f7bc40 100644 --- a/src/cmd/go/testdata/script/mod_outside.txt +++ b/src/cmd/go/testdata/script/mod_outside.txt @@ -69,10 +69,9 @@ go clean -n ! stdout . ! stderr . -# 'go mod graph' should not display anything, since there are no active modules. -go mod graph -! stdout . -! stderr . +# 'go mod graph' should fail, since there's no module graph. +! go mod graph +stderr 'cannot find main module' # 'go mod why' should fail, since there is no main module to depend on anything. ! go mod why -m example.com/version -- cgit v1.2.3-54-g00ecf From de0957dc081e1ec49c99a0f37403ceadbaaedf85 Mon Sep 17 00:00:00 2001 From: Daniel Martí Date: Thu, 10 Sep 2020 22:53:59 +0100 Subject: cmd/go: relax version's error on unexpected flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In https://golang.org/cl/221397 we made commands like "go version -v" error, since both of the command's flags only make sense when arguments follow them. Without arguments, the command only reports Go's own version, and the flags are most likely a mistake. However, the script below is entirely reasonable: export GOFLAGS=-v # make all Go commands verbose go version go build After the previous CL, "go version" would error. Instead, only error if the flag was passed explicitly, and not via GOFLAGS. The patch does mean that we won't error on "GOFLAGS=-v go version -v", but that very unlikely false negative is okay. The error is only meant to help the user not misuse the flags, anyway - it's not a critical error of any sort. To reuse inGOFLAGS, we move it to the base package and export it there, since it's where the rest of the GOFLAGS funcs are. Fixes #41264. Change-Id: I74003dd25d94bacf9ac507b5cad778fd65233321 Reviewed-on: https://go-review.googlesource.com/c/go/+/254157 Trust: Daniel Martí Run-TryBot: Daniel Martí TryBot-Result: Go Bot Reviewed-by: Bryan C. Mills --- src/cmd/go/internal/base/goflags.go | 17 +++++++++++++++++ src/cmd/go/internal/version/version.go | 9 ++++++++- src/cmd/go/internal/work/init.go | 22 +++------------------- src/cmd/go/testdata/script/version.txt | 6 ++++++ 4 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/cmd/go/internal/base/goflags.go b/src/cmd/go/internal/base/goflags.go index f11f9a5d33..4da27550fd 100644 --- a/src/cmd/go/internal/base/goflags.go +++ b/src/cmd/go/internal/base/goflags.go @@ -130,3 +130,20 @@ func SetFromGOFLAGS(flags *flag.FlagSet) { } } } + +// InGOFLAGS returns whether GOFLAGS contains the given flag, such as "-mod". +func InGOFLAGS(flag string) bool { + for _, goflag := range GOFLAGS() { + name := goflag + if strings.HasPrefix(name, "--") { + name = name[1:] + } + if i := strings.Index(name, "="); i >= 0 { + name = name[:i] + } + if name == flag { + return true + } + } + return false +} diff --git a/src/cmd/go/internal/version/version.go b/src/cmd/go/internal/version/version.go index c2de8d326d..5aa0f8e7ed 100644 --- a/src/cmd/go/internal/version/version.go +++ b/src/cmd/go/internal/version/version.go @@ -54,7 +54,14 @@ var ( func runVersion(ctx context.Context, cmd *base.Command, args []string) { if len(args) == 0 { - if *versionM || *versionV { + // If any of this command's flags were passed explicitly, error + // out, because they only make sense with arguments. + // + // Don't error if the flags came from GOFLAGS, since that can be + // a reasonable use case. For example, imagine GOFLAGS=-v to + // turn "verbose mode" on for all Go commands, which should not + // break "go version". + if (!base.InGOFLAGS("-m") && *versionM) || (!base.InGOFLAGS("-v") && *versionV) { fmt.Fprintf(os.Stderr, "go version: flags can only be used with arguments\n") base.SetExitStatus(2) return diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go index f78020032c..d71387d323 100644 --- a/src/cmd/go/internal/work/init.go +++ b/src/cmd/go/internal/work/init.go @@ -254,34 +254,18 @@ func buildModeInit() { case "": // Behavior will be determined automatically, as if no flag were passed. case "readonly", "vendor", "mod": - if !cfg.ModulesEnabled && !inGOFLAGS("-mod") { + if !cfg.ModulesEnabled && !base.InGOFLAGS("-mod") { base.Fatalf("build flag -mod=%s only valid when using modules", cfg.BuildMod) } default: base.Fatalf("-mod=%s not supported (can be '', 'mod', 'readonly', or 'vendor')", cfg.BuildMod) } if !cfg.ModulesEnabled { - if cfg.ModCacheRW && !inGOFLAGS("-modcacherw") { + if cfg.ModCacheRW && !base.InGOFLAGS("-modcacherw") { base.Fatalf("build flag -modcacherw only valid when using modules") } - if cfg.ModFile != "" && !inGOFLAGS("-mod") { + if cfg.ModFile != "" && !base.InGOFLAGS("-mod") { base.Fatalf("build flag -modfile only valid when using modules") } } } - -func inGOFLAGS(flag string) bool { - for _, goflag := range base.GOFLAGS() { - name := goflag - if strings.HasPrefix(name, "--") { - name = name[1:] - } - if i := strings.Index(name, "="); i >= 0 { - name = name[:i] - } - if name == flag { - return true - } - } - return false -} diff --git a/src/cmd/go/testdata/script/version.txt b/src/cmd/go/testdata/script/version.txt index 81ca698620..8615a4aac5 100644 --- a/src/cmd/go/testdata/script/version.txt +++ b/src/cmd/go/testdata/script/version.txt @@ -9,6 +9,12 @@ stderr 'with arguments' ! go version -v stderr 'with arguments' +# Neither of the two flags above should be an issue via GOFLAGS. +env GOFLAGS='-m -v' +go version +stdout '^go version' +env GOFLAGS= + env GO111MODULE=on # Skip the builds below if we are running in short mode. [short] skip -- cgit v1.2.3-54-g00ecf From 3ab825de9d50b24e2b97267a5d66dadb66399180 Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Thu, 10 Sep 2020 09:34:15 -0400 Subject: cmd/go/internal/modget: warn about unmatched packages exactly once Due to an inverted condition, we were emitting a "matched no packages" warning twice in some cases and not at all in others. For #41315 Change-Id: I472cd2d4f75811c8734852f2bdd7346f4c612816 Reviewed-on: https://go-review.googlesource.com/c/go/+/254819 Trust: Bryan C. Mills Trust: Michael Matloob Run-TryBot: Bryan C. Mills TryBot-Result: Go Bot Reviewed-by: Jay Conrod Reviewed-by: Michael Matloob --- src/cmd/go/internal/modget/get.go | 9 ++++-- src/cmd/go/testdata/script/mod_get_nopkgs.txt | 40 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 src/cmd/go/testdata/script/mod_get_nopkgs.txt diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go index 829cfe055a..1b5cf68840 100644 --- a/src/cmd/go/internal/modget/get.go +++ b/src/cmd/go/internal/modget/get.go @@ -609,9 +609,12 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { } prevBuildList = buildList } - if !*getD { - // Only print warnings after the last iteration, - // and only if we aren't going to build. + if *getD { + // Only print warnings after the last iteration, and only if we aren't going + // to build (to avoid doubled warnings). + // + // Only local patterns in the main module, such as './...', can be unmatched. + // (See the mod_get_nopkgs test for more detail.) search.WarnUnmatched(matches) } diff --git a/src/cmd/go/testdata/script/mod_get_nopkgs.txt b/src/cmd/go/testdata/script/mod_get_nopkgs.txt new file mode 100644 index 0000000000..078e71a041 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_get_nopkgs.txt @@ -0,0 +1,40 @@ +cd subdir + +# 'go get' on empty patterns that are necessarily local to the module +# should warn that the patterns are empty, exactly once. + +go get ./... +stderr -count=1 'matched no packages' + +go get -d ./... +stderr -count=1 'matched no packages' + +# 'go get' on patterns that could conceivably match nested modules +# should report a module resolution error. + +go get -d example.net/emptysubdir/... # control case + +! go get -d example.net/emptysubdir/subdir/... +! stderr 'matched no packages' +stderr '^go get example\.net/emptysubdir/subdir/\.\.\.: module example\.net/emptysubdir/subdir: reading http://.*: 404 Not Found\n\tserver response: 404 page not found\n\z' + +# It doesn't make sense to 'go get' a path in the standard library, +# since the standard library necessarily can't have unresolved imports. +# +# TODO(#30241): Maybe that won't always be the case? +# +# For that case, we emit a "malformed module path" error message, +# which isn't ideal either. + +! go get -d builtin/... # in GOROOT/src, but contains no packages +stderr '^go get builtin/...: malformed module path "builtin": missing dot in first path element$' + +-- go.mod -- +module example.net/emptysubdir + +go 1.16 +-- emptysubdir.go -- +// Package emptysubdir has a subdirectory containing no packages. +package emptysubdir +-- subdir/README.txt -- +This module intentionally does not contain any p -- cgit v1.2.3-54-g00ecf From dbde566219336e84360b4a38da10b5f63b19021e Mon Sep 17 00:00:00 2001 From: Jay Conrod Date: Wed, 9 Sep 2020 16:53:08 -0400 Subject: cmd/go: default to -mod=readonly in most commands For #40728 Change-Id: I6618f1b5a632e8b353a483a83bb0cdf4ef6df72c Reviewed-on: https://go-review.googlesource.com/c/go/+/251881 Trust: Jay Conrod Run-TryBot: Jay Conrod TryBot-Result: Go Bot Reviewed-by: Michael Matloob Reviewed-by: Bryan C. Mills --- src/cmd/go/alldocs.go | 85 ++++++++++------------- src/cmd/go/internal/modload/help.go | 85 ++++++++++------------- src/cmd/go/internal/modload/init.go | 18 ++--- src/cmd/go/testdata/script/mod_readonly.txt | 15 ++-- src/cmd/go/testdata/script/mod_replace_import.txt | 5 +- 5 files changed, 96 insertions(+), 112 deletions(-) diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 104aea6c7f..b7e5bbed2d 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -2613,72 +2613,63 @@ // // Maintaining module requirements // -// The go.mod file is meant to be readable and editable by both -// programmers and tools. The go command itself automatically updates the go.mod file -// to maintain a standard formatting and the accuracy of require statements. -// -// Any go command that finds an unfamiliar import will look up the module -// containing that import and add the latest version of that module -// to go.mod automatically. In most cases, therefore, it suffices to -// add an import to source code and run 'go build', 'go test', or even 'go list': -// as part of analyzing the package, the go command will discover -// and resolve the import and update the go.mod file. -// -// Any go command can determine that a module requirement is -// missing and must be added, even when considering only a single -// package from the module. On the other hand, determining that a module requirement -// is no longer necessary and can be deleted requires a full view of -// all packages in the module, across all possible build configurations -// (architectures, operating systems, build tags, and so on). -// The 'go mod tidy' command builds that view and then -// adds any missing module requirements and removes unnecessary ones. +// The go.mod file is meant to be readable and editable by both programmers and +// tools. Most updates to dependencies can be performed using "go get" and +// "go mod tidy". Other module-aware build commands may be invoked using the +// -mod=mod flag to automatically add missing requirements and fix inconsistencies. +// +// The "go get" command updates go.mod to change the module versions used in a +// build. An upgrade of one module may imply upgrading others, and similarly a +// downgrade of one module may imply downgrading others. The "go get" command +// makes these implied changes as well. See "go help module-get". +// +// The "go mod" command provides other functionality for use in maintaining +// and understanding modules and go.mod files. See "go help mod", particularly +// "go help mod tidy" and "go help mod edit". // // As part of maintaining the require statements in go.mod, the go command // tracks which ones provide packages imported directly by the current module // and which ones provide packages only used indirectly by other module // dependencies. Requirements needed only for indirect uses are marked with a -// "// indirect" comment in the go.mod file. Indirect requirements are +// "// indirect" comment in the go.mod file. Indirect requirements may be // automatically removed from the go.mod file once they are implied by other // direct requirements. Indirect requirements only arise when using modules // that fail to state some of their own dependencies or when explicitly // upgrading a module's dependencies ahead of its own stated requirements. // -// Because of this automatic maintenance, the information in go.mod is an -// up-to-date, readable description of the build. -// -// The 'go get' command updates go.mod to change the module versions used in a -// build. An upgrade of one module may imply upgrading others, and similarly a -// downgrade of one module may imply downgrading others. The 'go get' command -// makes these implied changes as well. If go.mod is edited directly, commands -// like 'go build' or 'go list' will assume that an upgrade is intended and -// automatically make any implied upgrades and update go.mod to reflect them. -// -// The 'go mod' command provides other functionality for use in maintaining -// and understanding modules and go.mod files. See 'go help mod'. -// -// The -mod build flag provides additional control over updating and use of go.mod. -// -// If invoked with -mod=readonly, the go command is disallowed from the implicit -// automatic updating of go.mod described above. Instead, it fails when any changes -// to go.mod are needed. This setting is most useful to check that go.mod does -// not need updates, such as in a continuous integration and testing system. -// The "go get" command remains permitted to update go.mod even with -mod=readonly, -// and the "go mod" commands do not take the -mod flag (or any other build flags). +// The -mod build flag provides additional control over the updating and use of +// go.mod for commands that build packages like "go build" and "go test". +// +// If invoked with -mod=readonly (the default in most situations), the go command +// reports an error if a package named on the command line or an imported package +// is not provided by any module in the build list computed from the main module's +// requirements. The go command also reports an error if a module's checksum is +// missing from go.sum (see Module downloading and verification). Either go.mod or +// go.sum must be updated in these situations. +// +// If invoked with -mod=mod, the go command automatically updates go.mod and +// go.sum, fixing inconsistencies and adding missing requirements and checksums +// as needed. If the go command finds an unfamiliar import, it looks up the +// module containing that import and adds a requirement for the latest version +// of that module to go.mod. In most cases, therefore, one may add an import to +// source code and run "go build", "go test", or even "go list" with -mod=mod: +// as part of analyzing the package, the go command will resolve the import and +// update the go.mod file. // // If invoked with -mod=vendor, the go command loads packages from the main // module's vendor directory instead of downloading modules to and loading packages // from the module cache. The go command assumes the vendor directory holds // correct copies of dependencies, and it does not compute the set of required // module versions from go.mod files. However, the go command does check that -// vendor/modules.txt (generated by 'go mod vendor') contains metadata consistent +// vendor/modules.txt (generated by "go mod vendor") contains metadata consistent // with go.mod. // -// If invoked with -mod=mod, the go command loads modules from the module cache -// even if there is a vendor directory present. +// If the go command is not invoked with a -mod flag, and the vendor directory +// is present, and the "go" version in go.mod is 1.14 or higher, the go command +// will act as if it were invoked with -mod=vendor. Otherwise, the -mod flag +// defaults to -mod=readonly. // -// If the go command is not invoked with a -mod flag and the vendor directory -// is present and the "go" version in go.mod is 1.14 or higher, the go command -// will act as if it were invoked with -mod=vendor. +// Note that neither "go get" nor the "go mod" subcommands accept the -mod flag. // // Pseudo-versions // diff --git a/src/cmd/go/internal/modload/help.go b/src/cmd/go/internal/modload/help.go index 37f23d967f..56920c28b9 100644 --- a/src/cmd/go/internal/modload/help.go +++ b/src/cmd/go/internal/modload/help.go @@ -124,72 +124,63 @@ and the build list. For example: Maintaining module requirements -The go.mod file is meant to be readable and editable by both -programmers and tools. The go command itself automatically updates the go.mod file -to maintain a standard formatting and the accuracy of require statements. - -Any go command that finds an unfamiliar import will look up the module -containing that import and add the latest version of that module -to go.mod automatically. In most cases, therefore, it suffices to -add an import to source code and run 'go build', 'go test', or even 'go list': -as part of analyzing the package, the go command will discover -and resolve the import and update the go.mod file. - -Any go command can determine that a module requirement is -missing and must be added, even when considering only a single -package from the module. On the other hand, determining that a module requirement -is no longer necessary and can be deleted requires a full view of -all packages in the module, across all possible build configurations -(architectures, operating systems, build tags, and so on). -The 'go mod tidy' command builds that view and then -adds any missing module requirements and removes unnecessary ones. +The go.mod file is meant to be readable and editable by both programmers and +tools. Most updates to dependencies can be performed using "go get" and +"go mod tidy". Other module-aware build commands may be invoked using the +-mod=mod flag to automatically add missing requirements and fix inconsistencies. + +The "go get" command updates go.mod to change the module versions used in a +build. An upgrade of one module may imply upgrading others, and similarly a +downgrade of one module may imply downgrading others. The "go get" command +makes these implied changes as well. See "go help module-get". + +The "go mod" command provides other functionality for use in maintaining +and understanding modules and go.mod files. See "go help mod", particularly +"go help mod tidy" and "go help mod edit". As part of maintaining the require statements in go.mod, the go command tracks which ones provide packages imported directly by the current module and which ones provide packages only used indirectly by other module dependencies. Requirements needed only for indirect uses are marked with a -"// indirect" comment in the go.mod file. Indirect requirements are +"// indirect" comment in the go.mod file. Indirect requirements may be automatically removed from the go.mod file once they are implied by other direct requirements. Indirect requirements only arise when using modules that fail to state some of their own dependencies or when explicitly upgrading a module's dependencies ahead of its own stated requirements. -Because of this automatic maintenance, the information in go.mod is an -up-to-date, readable description of the build. - -The 'go get' command updates go.mod to change the module versions used in a -build. An upgrade of one module may imply upgrading others, and similarly a -downgrade of one module may imply downgrading others. The 'go get' command -makes these implied changes as well. If go.mod is edited directly, commands -like 'go build' or 'go list' will assume that an upgrade is intended and -automatically make any implied upgrades and update go.mod to reflect them. - -The 'go mod' command provides other functionality for use in maintaining -and understanding modules and go.mod files. See 'go help mod'. - -The -mod build flag provides additional control over updating and use of go.mod. - -If invoked with -mod=readonly, the go command is disallowed from the implicit -automatic updating of go.mod described above. Instead, it fails when any changes -to go.mod are needed. This setting is most useful to check that go.mod does -not need updates, such as in a continuous integration and testing system. -The "go get" command remains permitted to update go.mod even with -mod=readonly, -and the "go mod" commands do not take the -mod flag (or any other build flags). +The -mod build flag provides additional control over the updating and use of +go.mod for commands that build packages like "go build" and "go test". + +If invoked with -mod=readonly (the default in most situations), the go command +reports an error if a package named on the command line or an imported package +is not provided by any module in the build list computed from the main module's +requirements. The go command also reports an error if a module's checksum is +missing from go.sum (see Module downloading and verification). Either go.mod or +go.sum must be updated in these situations. + +If invoked with -mod=mod, the go command automatically updates go.mod and +go.sum, fixing inconsistencies and adding missing requirements and checksums +as needed. If the go command finds an unfamiliar import, it looks up the +module containing that import and adds a requirement for the latest version +of that module to go.mod. In most cases, therefore, one may add an import to +source code and run "go build", "go test", or even "go list" with -mod=mod: +as part of analyzing the package, the go command will resolve the import and +update the go.mod file. If invoked with -mod=vendor, the go command loads packages from the main module's vendor directory instead of downloading modules to and loading packages from the module cache. The go command assumes the vendor directory holds correct copies of dependencies, and it does not compute the set of required module versions from go.mod files. However, the go command does check that -vendor/modules.txt (generated by 'go mod vendor') contains metadata consistent +vendor/modules.txt (generated by "go mod vendor") contains metadata consistent with go.mod. -If invoked with -mod=mod, the go command loads modules from the module cache -even if there is a vendor directory present. +If the go command is not invoked with a -mod flag, and the vendor directory +is present, and the "go" version in go.mod is 1.14 or higher, the go command +will act as if it were invoked with -mod=vendor. Otherwise, the -mod flag +defaults to -mod=readonly. -If the go command is not invoked with a -mod flag and the vendor directory -is present and the "go" version in go.mod is 1.14 or higher, the go command -will act as if it were invoked with -mod=vendor. +Note that neither "go get" nor the "go mod" subcommands accept the -mod flag. Pseudo-versions diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go index f93abee96d..60aadf23ea 100644 --- a/src/cmd/go/internal/modload/init.go +++ b/src/cmd/go/internal/modload/init.go @@ -571,7 +571,7 @@ func setDefaultBuildMod() { return } if modRoot == "" { - cfg.BuildMod = "mod" + cfg.BuildMod = "readonly" return } @@ -594,13 +594,7 @@ func setDefaultBuildMod() { cfg.BuildModReason = fmt.Sprintf("Go version in go.mod is %s, so vendor directory was not used.", modGo) } - p := ModFilePath() - if fi, err := os.Stat(p); err == nil && !hasWritePerm(p, fi) { - cfg.BuildMod = "readonly" - cfg.BuildModReason = "go.mod file is read-only." - return - } - cfg.BuildMod = "mod" + cfg.BuildMod = "readonly" } func legacyModInit() { @@ -898,10 +892,12 @@ func WriteGoMod() { if dirty && cfg.BuildMod == "readonly" { // If we're about to fail due to -mod=readonly, // prefer to report a dirty go.mod over a dirty go.sum - if cfg.BuildModReason != "" { - base.Fatalf("go: updates to go.mod needed, disabled by -mod=readonly\n\t(%s)", cfg.BuildModReason) - } else if cfg.BuildModExplicit { + if cfg.BuildModExplicit { base.Fatalf("go: updates to go.mod needed, disabled by -mod=readonly") + } else if cfg.BuildModReason != "" { + base.Fatalf("go: updates to go.mod needed, disabled by -mod=readonly\n\t(%s)", cfg.BuildModReason) + } else { + base.Fatalf("go: updates to go.mod needed; try 'go mod tidy' first") } } diff --git a/src/cmd/go/testdata/script/mod_readonly.txt b/src/cmd/go/testdata/script/mod_readonly.txt index ac581264f1..a8458fdea3 100644 --- a/src/cmd/go/testdata/script/mod_readonly.txt +++ b/src/cmd/go/testdata/script/mod_readonly.txt @@ -10,13 +10,12 @@ stderr '^x.go:2:8: cannot find module providing package rsc\.io/quote: import lo ! stderr '\(\)' # If we don't have a reason for -mod=readonly, don't log an empty one. cmp go.mod go.mod.empty -# -mod=readonly should be set implicitly if the go.mod file is read-only -chmod 0400 go.mod +# -mod=readonly should be set by default. env GOFLAGS= ! go list all -stderr '^x.go:2:8: cannot find module providing package rsc\.io/quote: import lookup disabled by -mod=readonly\n\t\(go.mod file is read-only\.\)$' +stderr '^x.go:2:8: cannot find module providing package rsc\.io/quote$' +cmp go.mod go.mod.empty -chmod 0600 go.mod env GOFLAGS=-mod=readonly # update go.mod - go get allowed @@ -48,18 +47,26 @@ cp go.mod go.mod.inconsistent stderr 'go: updates to go.mod needed, disabled by -mod=readonly' cmp go.mod go.mod.inconsistent +# We get a different message when -mod=readonly is used by default. +env GOFLAGS= +! go list +stderr '^go: updates to go.mod needed; try ''go mod tidy'' first$' + # However, it should not reject files missing a 'go' directive, # since that was not always required. cp go.mod.nogo go.mod go list all +cmp go.mod go.mod.nogo # Nor should it reject files with redundant (not incorrect) # requirements. cp go.mod.redundant go.mod go list all +cmp go.mod go.mod.redundant cp go.mod.indirect go.mod go list all +cmp go.mod go.mod.indirect -- go.mod -- module m diff --git a/src/cmd/go/testdata/script/mod_replace_import.txt b/src/cmd/go/testdata/script/mod_replace_import.txt index b4de5c50f7..407a6cef7d 100644 --- a/src/cmd/go/testdata/script/mod_replace_import.txt +++ b/src/cmd/go/testdata/script/mod_replace_import.txt @@ -1,9 +1,8 @@ env GO111MODULE=on -# 'go list -mod=readonly' should not add requirements even if they can be -# resolved locally. +# 'go list' should not add requirements even if they can be resolved locally. cp go.mod go.mod.orig -! go list -mod=readonly all +! go list all cmp go.mod go.mod.orig # 'go list' should resolve imports using replacements. -- cgit v1.2.3-54-g00ecf From 8248b5791cd825f80c55e972c1e96c6fadf5885e Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Mon, 14 Sep 2020 15:56:59 -0400 Subject: cmd/go/internal/modget: factor out functions for argument resolution For #37438 For #41315 For #36460 Change-Id: I17041c35ec91ff6ffb547e0f32572673d191b1ed Reviewed-on: https://go-review.googlesource.com/c/go/+/254820 Trust: Bryan C. Mills Trust: Jay Conrod Run-TryBot: Bryan C. Mills TryBot-Result: Go Bot Reviewed-by: Michael Matloob Reviewed-by: Jay Conrod --- src/cmd/go/internal/modget/get.go | 340 +++++++++++++++++++++----------------- 1 file changed, 184 insertions(+), 156 deletions(-) diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go index 1b5cf68840..0c501e3885 100644 --- a/src/cmd/go/internal/modget/get.go +++ b/src/cmd/go/internal/modget/get.go @@ -225,6 +225,8 @@ type getArg struct { vers string } +func (arg getArg) String() string { return arg.raw } + // querySpec describes a query for a specific module. path may be a // module path, package path, or package pattern. vers is a version // query string from a command line argument. @@ -278,13 +280,6 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { } modload.LoadTests = *getT - buildList := modload.LoadAllModules(ctx) - buildList = buildList[:len(buildList):len(buildList)] // copy on append - versionByPath := make(map[string]string) - for _, m := range buildList { - versionByPath[m.Path] = m.Version - } - // Do not allow any updating of go.mod until we've applied // all the requested changes and checked that the result matches // what was requested. @@ -294,150 +289,15 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { // 'go get' is expected to do this, unlike other commands. modload.AllowMissingModuleImports() - // Parse command-line arguments and report errors. The command-line - // arguments are of the form path@version or simply path, with implicit - // @upgrade. path@none is "downgrade away". - var gets []getArg - var queries []*query - for _, arg := range search.CleanPatterns(args) { - // Argument is path or path@vers. - path := arg - vers := "" - if i := strings.Index(arg, "@"); i >= 0 { - path, vers = arg[:i], arg[i+1:] - } - if strings.Contains(vers, "@") || arg != path && vers == "" { - base.Errorf("go get %s: invalid module version syntax", arg) - continue - } - - // Guard against 'go get x.go', a common mistake. - // Note that package and module paths may end with '.go', so only print an error - // if the argument has no version and either has no slash or refers to an existing file. - if strings.HasSuffix(arg, ".go") && vers == "" { - if !strings.Contains(arg, "/") { - base.Errorf("go get %s: arguments must be package or module paths", arg) - continue - } - if fi, err := os.Stat(arg); err == nil && !fi.IsDir() { - base.Errorf("go get: %s exists as a file, but 'go get' requires package arguments", arg) - continue - } - } - - // If no version suffix is specified, assume @upgrade. - // If -u=patch was specified, assume @patch instead. - if vers == "" { - if getU != "" { - vers = string(getU) - } else { - vers = "upgrade" - } - } - - gets = append(gets, getArg{raw: arg, path: path, vers: vers}) - - // Determine the modules that path refers to, and create queries - // to lookup modules at target versions before loading packages. - // This is an imprecise process, but it helps reduce unnecessary - // queries and package loading. It's also necessary for handling - // patterns like golang.org/x/tools/..., which can't be expanded - // during package loading until they're in the build list. - switch { - case filepath.IsAbs(path) || search.IsRelativePath(path): - // Absolute paths like C:\foo and relative paths like ../foo... - // are restricted to matching packages in the main module. If the path - // is explicit and contains no wildcards (...), check that it is a - // package in the main module. If the path contains wildcards but - // matches no packages, we'll warn after package loading. - if !strings.Contains(path, "...") { - m := search.NewMatch(path) - if pkgPath := modload.DirImportPath(path); pkgPath != "." { - m = modload.TargetPackages(ctx, pkgPath) - } - if len(m.Pkgs) == 0 { - for _, err := range m.Errs { - base.Errorf("go get %s: %v", arg, err) - } - - abs, err := filepath.Abs(path) - if err != nil { - abs = path - } - base.Errorf("go get %s: path %s is not a package in module rooted at %s", arg, abs, modload.ModRoot()) - continue - } - } - - if path != arg { - base.Errorf("go get %s: can't request explicit version of path in main module", arg) - continue - } + getArgs := parseArgs(args) - case strings.Contains(path, "..."): - // Wait until we load packages to look up modules. - // We don't know yet whether any modules in the build list provide - // packages matching the pattern. For example, suppose - // golang.org/x/tools and golang.org/x/tools/playground are separate - // modules, and only golang.org/x/tools is in the build list. If the - // user runs 'go get golang.org/x/tools/playground/...', we should - // add a requirement for golang.org/x/tools/playground. We should not - // upgrade golang.org/x/tools. - - case path == "all": - // If there is no main module, "all" is not meaningful. - if !modload.HasModRoot() { - base.Errorf(`go get %s: cannot match "all": working directory is not part of a module`, arg) - } - // Don't query modules until we load packages. We'll automatically - // look up any missing modules. - - case search.IsMetaPackage(path): - base.Errorf("go get %s: explicit requirement on standard-library module %s not allowed", path, path) - continue - - default: - // The argument is a package or module path. - if modload.HasModRoot() { - if m := modload.TargetPackages(ctx, path); len(m.Pkgs) != 0 { - // The path is in the main module. Nothing to query. - if vers != "upgrade" && vers != "patch" { - base.Errorf("go get %s: can't request explicit version of path in main module", arg) - } - continue - } - } - - first := path - if i := strings.IndexByte(first, '/'); i >= 0 { - first = path - } - if !strings.Contains(first, ".") { - // The path doesn't have a dot in the first component and cannot be - // queried as a module. It may be a package in the standard library, - // which is fine, so don't report an error unless we encounter - // a problem loading packages below. - continue - } - - // If we're querying "upgrade" or "patch", we need to know the current - // version of the module. For "upgrade", we want to avoid accidentally - // downgrading from a newer prerelease. For "patch", we need to query - // the correct minor version. - // Here, we check if "path" is the name of a module in the build list - // (other than the main module) and set prevM if so. If "path" isn't - // a module in the build list, the current version doesn't matter - // since it's either an unknown module or a package within a module - // that we'll discover later. - q := &query{querySpec: querySpec{path: path, vers: vers}, arg: arg} - if v, ok := versionByPath[path]; ok && path != modload.Target.Path { - q.prevM = module.Version{Path: path, Version: v} - q.forceModulePath = true - } - queries = append(queries, q) - } + buildList := modload.LoadAllModules(ctx) + buildList = buildList[:len(buildList):len(buildList)] // copy on append + selectedVersion := make(map[string]string) + for _, m := range buildList { + selectedVersion[m.Path] = m.Version } - base.ExitIfErrors() + queries := classifyArgs(ctx, selectedVersion, getArgs) // Query modules referenced by command line arguments at requested versions. // We need to do this before loading packages since patterns that refer to @@ -450,11 +310,11 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { // We call SetBuildList here and elsewhere, since newUpgrader, // ImportPathsQuiet, and other functions read the global build list. for _, q := range queries { - if _, ok := versionByPath[q.m.Path]; !ok && q.m.Version != "none" { + if _, ok := selectedVersion[q.m.Path]; !ok && q.m.Version != "none" { buildList = append(buildList, q.m) } } - versionByPath = nil // out of date now; rebuilt later when needed + selectedVersion = nil // out of date now; rebuilt later when needed modload.SetBuildList(buildList) // Upgrade modules specifically named on the command line. This is our only @@ -508,7 +368,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { // Build a list of arguments that may refer to packages. var pkgPatterns []string var pkgGets []getArg - for _, arg := range gets { + for _, arg := range getArgs { if modOnly[arg.path] == nil && arg.vers != "none" { pkgPatterns = append(pkgPatterns, arg.path) pkgGets = append(pkgGets, arg) @@ -643,12 +503,12 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { // Scan for any upgrades lost by the downgrades. var lostUpgrades []*query if len(down) > 0 { - versionByPath = make(map[string]string) + selectedVersion = make(map[string]string) for _, m := range modload.LoadedModules() { - versionByPath[m.Path] = m.Version + selectedVersion[m.Path] = m.Version } for _, q := range byPath { - if v, ok := versionByPath[q.m.Path]; q.m.Version != "none" && (!ok || semver.Compare(v, q.m.Version) != 0) { + if v, ok := selectedVersion[q.m.Path]; q.m.Version != "none" && (!ok || semver.Compare(v, q.m.Version) != 0) { lostUpgrades = append(lostUpgrades, q) } } @@ -695,7 +555,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { if sep != "," { // We have no idea why this happened. // At least report the problem. - if v := versionByPath[q.m.Path]; v == "" { + if v := selectedVersion[q.m.Path]; v == "" { fmt.Fprintf(&buf, " removed unexpectedly") } else { fmt.Fprintf(&buf, " ended up at %s unexpectedly", v) @@ -735,6 +595,174 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { work.InstallPackages(ctx, pkgPatterns, pkgs) } +// parseArgs parses command-line arguments and reports errors. +// +// The command-line arguments are of the form path@version or simply path, with +// implicit @upgrade. path@none is "downgrade away". +func parseArgs(rawArgs []string) []getArg { + defer base.ExitIfErrors() + + var gets []getArg + for _, raw := range search.CleanPatterns(rawArgs) { + // Argument is path or path@vers. + path := raw + vers := "" + if i := strings.Index(raw, "@"); i >= 0 { + path, vers = raw[:i], raw[i+1:] + } + if strings.Contains(vers, "@") || raw != path && vers == "" { + base.Errorf("go get %s: invalid module version syntax", raw) + continue + } + + // Guard against 'go get x.go', a common mistake. + // Note that package and module paths may end with '.go', so only print an error + // if the argument has no version and either has no slash or refers to an existing file. + if strings.HasSuffix(raw, ".go") && vers == "" { + if !strings.Contains(raw, "/") { + base.Errorf("go get %s: arguments must be package or module paths", raw) + continue + } + if fi, err := os.Stat(raw); err == nil && !fi.IsDir() { + base.Errorf("go get: %s exists as a file, but 'go get' requires package arguments", raw) + continue + } + } + + // If no version suffix is specified, assume @upgrade. + // If -u=patch was specified, assume @patch instead. + if vers == "" { + if getU != "" { + vers = string(getU) + } else { + vers = "upgrade" + } + } + + gets = append(gets, getArg{raw: raw, path: path, vers: vers}) + } + + return gets +} + +// classifyArgs determines which arguments refer to packages and which refer to +// modules, and creates queries to look up modules at target versions before +// loading packages. +// +// This is an imprecise process, but it helps reduce unnecessary +// queries and package loading. It's also necessary for handling +// patterns like golang.org/x/tools/..., which can't be expanded +// during package loading until they're in the build list. +func classifyArgs(ctx context.Context, selectedVersion map[string]string, args []getArg) []*query { + defer base.ExitIfErrors() + + queries := make([]*query, 0, len(args)) + + for _, arg := range args { + path := arg.path + switch { + case filepath.IsAbs(path) || search.IsRelativePath(path): + // Absolute paths like C:\foo and relative paths like ../foo... + // are restricted to matching packages in the main module. If the path + // is explicit and contains no wildcards (...), check that it is a + // package in the main module. If the path contains wildcards but + // matches no packages, we'll warn after package loading. + if !strings.Contains(path, "...") { + m := search.NewMatch(path) + if pkgPath := modload.DirImportPath(path); pkgPath != "." { + m = modload.TargetPackages(ctx, pkgPath) + } + if len(m.Pkgs) == 0 { + for _, err := range m.Errs { + base.Errorf("go get %s: %v", arg, err) + } + + abs, err := filepath.Abs(path) + if err != nil { + abs = path + } + base.Errorf("go get %s: path %s is not a package in module rooted at %s", arg, abs, modload.ModRoot()) + continue + } + } + + if arg.path != arg.raw { + base.Errorf("go get %s: can't request explicit version of path in main module", arg) + continue + } + + case strings.Contains(path, "..."): + // Wait until we load packages to look up modules. + // We don't know yet whether any modules in the build list provide + // packages matching the pattern. For example, suppose + // golang.org/x/tools and golang.org/x/tools/playground are separate + // modules, and only golang.org/x/tools is in the build list. If the + // user runs 'go get golang.org/x/tools/playground/...', we should + // add a requirement for golang.org/x/tools/playground. We should not + // upgrade golang.org/x/tools. + + case path == "all": + // If there is no main module, "all" is not meaningful. + if !modload.HasModRoot() { + base.Errorf(`go get %s: cannot match "all": working directory is not part of a module`, arg) + } + // Don't query modules until we load packages. We'll automatically + // look up any missing modules. + + case search.IsMetaPackage(path): + base.Errorf("go get %s: explicit requirement on standard-library module %s not allowed", path, path) + continue + + default: + // The argument is a package or module path. + if modload.HasModRoot() { + if m := modload.TargetPackages(ctx, path); len(m.Pkgs) != 0 { + // The path is in the main module. Nothing to query. + if arg.vers != "upgrade" && arg.vers != "patch" { + base.Errorf("go get %s: can't request explicit version of path in main module", arg) + } + continue + } + } + + first := path + if i := strings.IndexByte(first, '/'); i >= 0 { + first = path + } + if !strings.Contains(first, ".") { + // The path doesn't have a dot in the first component and cannot be + // queried as a module. It may be a package in the standard library, + // which is fine, so don't report an error unless we encounter + // a problem loading packages. + continue + } + + // If we're querying "upgrade" or "patch", we need to know the current + // version of the module. For "upgrade", we want to avoid accidentally + // downgrading from a newer prerelease. For "patch", we need to query + // the correct minor version. + // Here, we check if "path" is the name of a module in the build list + // (other than the main module) and set prevM if so. If "path" isn't + // a module in the build list, the current version doesn't matter + // since it's either an unknown module or a package within a module + // that we'll discover later. + q := &query{querySpec: querySpec{path: arg.path, vers: arg.vers}, arg: arg.raw} + if v, ok := selectedVersion[path]; ok { + if path == modload.Target.Path { + // TODO(bcmills): This is held over from a previous version of the get + // implementation. Why was it a special case? + } else { + q.prevM = module.Version{Path: path, Version: v} + q.forceModulePath = true + } + } + queries = append(queries, q) + } + } + + return queries +} + // runQueries looks up modules at target versions in parallel. Results will be // cached. If the same module is referenced by multiple queries at different // versions (including earlier queries in the modOnly map), an error will be -- cgit v1.2.3-54-g00ecf From b6dbaef68fdbb3f14387e4c32a8890144220f54e Mon Sep 17 00:00:00 2001 From: Henrique Vicente Date: Mon, 17 Feb 2020 02:22:47 +0100 Subject: os/signal: add NotifyContext to cancel context using system signals Fixes #37255 Change-Id: Ic0fde3498afefed6e4447f8476e4da7c1faa7145 Reviewed-on: https://go-review.googlesource.com/c/go/+/219640 Run-TryBot: Ian Lance Taylor TryBot-Result: Go Bot Trust: Giovanni Bajo Reviewed-by: Ian Lance Taylor --- src/os/signal/example_unix_test.go | 47 +++++++++++ src/os/signal/signal.go | 75 +++++++++++++++++ src/os/signal/signal_test.go | 162 +++++++++++++++++++++++++++++++++++++ 3 files changed, 284 insertions(+) create mode 100644 src/os/signal/example_unix_test.go diff --git a/src/os/signal/example_unix_test.go b/src/os/signal/example_unix_test.go new file mode 100644 index 0000000000..a0af37a5bb --- /dev/null +++ b/src/os/signal/example_unix_test.go @@ -0,0 +1,47 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris + +package signal_test + +import ( + "context" + "fmt" + "log" + "os" + "os/signal" + "time" +) + +// This example passes a context with a signal to tell a blocking function that +// it should abandon its work after a signal is received. +func ExampleNotifyContext() { + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) + defer stop() + + p, err := os.FindProcess(os.Getpid()) + if err != nil { + log.Fatal(err) + } + + // On a Unix-like system, pressing Ctrl+C on a keyboard sends a + // SIGINT signal to the process of the program in execution. + // + // This example simulates that by sending a SIGINT signal to itself. + if err := p.Signal(os.Interrupt); err != nil { + log.Fatal(err) + } + + select { + case <-time.After(time.Second): + fmt.Println("missed signal") + case <-ctx.Done(): + fmt.Println(ctx.Err()) // prints "context canceled" + stop() // stop receiving signal notifications as soon as possible. + } + + // Output: + // context canceled +} diff --git a/src/os/signal/signal.go b/src/os/signal/signal.go index 8e31aa2627..4250a7e0de 100644 --- a/src/os/signal/signal.go +++ b/src/os/signal/signal.go @@ -5,6 +5,7 @@ package signal import ( + "context" "os" "sync" ) @@ -257,3 +258,77 @@ func process(sig os.Signal) { } } } + +// NotifyContext returns a copy of the parent context that is marked done +// (its Done channel is closed) when one of the listed signals arrives, +// when the returned stop function is called, or when the parent context's +// Done channel is closed, whichever happens first. +// +// The stop function unregisters the signal behavior, which, like signal.Reset, +// may restore the default behavior for a given signal. For example, the default +// behavior of a Go program receiving os.Interrupt is to exit. Calling +// NotifyContext(parent, os.Interrupt) will change the behavior to cancel +// the returned context. Future interrupts received will not trigger the default +// (exit) behavior until the returned stop function is called. +// +// The stop function releases resources associated with it, so code should +// call stop as soon as the operations running in this Context complete and +// signals no longer need to be diverted to the context. +func NotifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) { + ctx, cancel := context.WithCancel(parent) + c := &signalCtx{ + Context: ctx, + cancel: cancel, + signals: signals, + } + c.ch = make(chan os.Signal, 1) + Notify(c.ch, c.signals...) + if ctx.Err() == nil { + go func() { + select { + case <-c.ch: + c.cancel() + case <-c.Done(): + } + }() + } + return c, c.stop +} + +type signalCtx struct { + context.Context + + cancel context.CancelFunc + signals []os.Signal + ch chan os.Signal +} + +func (c *signalCtx) stop() { + c.cancel() + Stop(c.ch) +} + +type stringer interface { + String() string +} + +func (c *signalCtx) String() string { + var buf []byte + // We know that the type of c.Context is context.cancelCtx, and we know that the + // String method of cancelCtx returns a string that ends with ".WithCancel". + name := c.Context.(stringer).String() + name = name[:len(name)-len(".WithCancel")] + buf = append(buf, "signal.NotifyContext("+name...) + if len(c.signals) != 0 { + buf = append(buf, ", ["...) + for i, s := range c.signals { + buf = append(buf, s.String()...) + if i != len(c.signals)-1 { + buf = append(buf, ' ') + } + } + buf = append(buf, ']') + } + buf = append(buf, ')') + return string(buf) +} diff --git a/src/os/signal/signal_test.go b/src/os/signal/signal_test.go index f0e06b8795..23e33fe82b 100644 --- a/src/os/signal/signal_test.go +++ b/src/os/signal/signal_test.go @@ -8,6 +8,7 @@ package signal import ( "bytes" + "context" "flag" "fmt" "internal/testenv" @@ -674,3 +675,164 @@ func TestTime(t *testing.T) { close(stop) <-done } + +func TestNotifyContext(t *testing.T) { + c, stop := NotifyContext(context.Background(), syscall.SIGINT) + defer stop() + + if want, got := "signal.NotifyContext(context.Background, [interrupt])", fmt.Sprint(c); want != got { + t.Errorf("c.String() = %q, want %q", got, want) + } + + syscall.Kill(syscall.Getpid(), syscall.SIGINT) + select { + case <-c.Done(): + if got := c.Err(); got != context.Canceled { + t.Errorf("c.Err() = %q, want %q", got, context.Canceled) + } + case <-time.After(time.Second): + t.Errorf("timed out waiting for context to be done after SIGINT") + } +} + +func TestNotifyContextStop(t *testing.T) { + Ignore(syscall.SIGHUP) + if !Ignored(syscall.SIGHUP) { + t.Errorf("expected SIGHUP to be ignored when explicitly ignoring it.") + } + + parent, cancelParent := context.WithCancel(context.Background()) + defer cancelParent() + c, stop := NotifyContext(parent, syscall.SIGHUP) + defer stop() + + // If we're being notified, then the signal should not be ignored. + if Ignored(syscall.SIGHUP) { + t.Errorf("expected SIGHUP to not be ignored.") + } + + if want, got := "signal.NotifyContext(context.Background.WithCancel, [hangup])", fmt.Sprint(c); want != got { + t.Errorf("c.String() = %q, wanted %q", got, want) + } + + stop() + select { + case <-c.Done(): + if got := c.Err(); got != context.Canceled { + t.Errorf("c.Err() = %q, want %q", got, context.Canceled) + } + case <-time.After(time.Second): + t.Errorf("timed out waiting for context to be done after calling stop") + } +} + +func TestNotifyContextCancelParent(t *testing.T) { + parent, cancelParent := context.WithCancel(context.Background()) + defer cancelParent() + c, stop := NotifyContext(parent, syscall.SIGINT) + defer stop() + + if want, got := "signal.NotifyContext(context.Background.WithCancel, [interrupt])", fmt.Sprint(c); want != got { + t.Errorf("c.String() = %q, want %q", got, want) + } + + cancelParent() + select { + case <-c.Done(): + if got := c.Err(); got != context.Canceled { + t.Errorf("c.Err() = %q, want %q", got, context.Canceled) + } + case <-time.After(time.Second): + t.Errorf("timed out waiting for parent context to be canceled") + } +} + +func TestNotifyContextPrematureCancelParent(t *testing.T) { + parent, cancelParent := context.WithCancel(context.Background()) + defer cancelParent() + + cancelParent() // Prematurely cancel context before calling NotifyContext. + c, stop := NotifyContext(parent, syscall.SIGINT) + defer stop() + + if want, got := "signal.NotifyContext(context.Background.WithCancel, [interrupt])", fmt.Sprint(c); want != got { + t.Errorf("c.String() = %q, want %q", got, want) + } + + select { + case <-c.Done(): + if got := c.Err(); got != context.Canceled { + t.Errorf("c.Err() = %q, want %q", got, context.Canceled) + } + case <-time.After(time.Second): + t.Errorf("timed out waiting for parent context to be canceled") + } +} + +func TestNotifyContextSimultaneousNotifications(t *testing.T) { + c, stop := NotifyContext(context.Background(), syscall.SIGINT) + defer stop() + + if want, got := "signal.NotifyContext(context.Background, [interrupt])", fmt.Sprint(c); want != got { + t.Errorf("c.String() = %q, want %q", got, want) + } + + var wg sync.WaitGroup + n := 10 + wg.Add(n) + for i := 0; i < n; i++ { + go func() { + syscall.Kill(syscall.Getpid(), syscall.SIGINT) + wg.Done() + }() + } + wg.Wait() + select { + case <-c.Done(): + if got := c.Err(); got != context.Canceled { + t.Errorf("c.Err() = %q, want %q", got, context.Canceled) + } + case <-time.After(time.Second): + t.Errorf("expected context to be canceled") + } +} + +func TestNotifyContextSimultaneousStop(t *testing.T) { + c, stop := NotifyContext(context.Background(), syscall.SIGINT) + defer stop() + + if want, got := "signal.NotifyContext(context.Background, [interrupt])", fmt.Sprint(c); want != got { + t.Errorf("c.String() = %q, want %q", got, want) + } + + var wg sync.WaitGroup + n := 10 + wg.Add(n) + for i := 0; i < n; i++ { + go func() { + stop() + wg.Done() + }() + } + wg.Wait() + select { + case <-c.Done(): + if got := c.Err(); got != context.Canceled { + t.Errorf("c.Err() = %q, want %q", got, context.Canceled) + } + case <-time.After(time.Second): + t.Errorf("expected context to be canceled") + } +} + +func TestNotifyContextStringer(t *testing.T) { + parent, cancelParent := context.WithCancel(context.Background()) + defer cancelParent() + c, stop := NotifyContext(parent, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) + defer stop() + + want := `signal.NotifyContext(context.Background.WithCancel, [hangup interrupt terminated])` + if got := fmt.Sprint(c); got != want { + t.Errorf("c.String() = %q, want %q", got, want) + } +} -- cgit v1.2.3-54-g00ecf From eaa97fbf20baffac713ed1b780f864a6fee54ab6 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 1 Sep 2020 17:01:00 -0700 Subject: cmd/cgo: don't translate bitfields into Go fields The cgo tool would sometimes emit a bitfield at an offset that did not correspond to the C offset, such as for the example in the new test. Change-Id: I61b2ca10ee44a42f81c13ed12865f2060168fed5 Reviewed-on: https://go-review.googlesource.com/c/go/+/252378 Trust: Ian Lance Taylor Run-TryBot: Ian Lance Taylor Reviewed-by: Matthew Dempsky TryBot-Result: Go Bot --- doc/go1.16.html | 10 ++++++++++ misc/cgo/testgodefs/testdata/bitfields.go | 31 +++++++++++++++++++++++++++++++ misc/cgo/testgodefs/testdata/main.go | 28 ++++++++++++++++++++++++++++ misc/cgo/testgodefs/testgodefs_test.go | 1 + src/cmd/cgo/gcc.go | 20 +++++--------------- 5 files changed, 75 insertions(+), 15 deletions(-) create mode 100644 misc/cgo/testgodefs/testdata/bitfields.go diff --git a/doc/go1.16.html b/doc/go1.16.html index f177226269..0167030ef8 100644 --- a/doc/go1.16.html +++ b/doc/go1.16.html @@ -86,6 +86,16 @@ Do not send CLs removing the interior tags from such phrases. by go mod vendor since Go 1.11.

+

Cgo

+ +

+ The cgo tool will no longer try to translate + C struct bitfields into Go struct fields, even if their size can be + represented in Go. The order in which C bitfields appear in memory + is implementation dependent, so in some cases the cgo tool produced + results that were silently incorrect. +

+

TODO

diff --git a/misc/cgo/testgodefs/testdata/bitfields.go b/misc/cgo/testgodefs/testdata/bitfields.go new file mode 100644 index 0000000000..6a9724dcd1 --- /dev/null +++ b/misc/cgo/testgodefs/testdata/bitfields.go @@ -0,0 +1,31 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// +build ignore + +package main + +// This file tests that we don't generate an incorrect field location +// for a bitfield that appears aligned. + +/* +struct bitfields { + unsigned int B1 : 5; + unsigned int B2 : 1; + unsigned int B3 : 1; + unsigned int B4 : 1; + unsigned int Short1 : 16; // misaligned on 8 bit boundary + unsigned int B5 : 1; + unsigned int B6 : 1; + unsigned int B7 : 1; + unsigned int B8 : 1; + unsigned int B9 : 1; + unsigned int B10 : 3; + unsigned int Short2 : 16; // alignment is OK + unsigned int Short3 : 16; // alignment is OK +}; +*/ +import "C" + +type bitfields C.struct_bitfields diff --git a/misc/cgo/testgodefs/testdata/main.go b/misc/cgo/testgodefs/testdata/main.go index 2e1ad3376a..4a3f6a701c 100644 --- a/misc/cgo/testgodefs/testdata/main.go +++ b/misc/cgo/testgodefs/testdata/main.go @@ -4,6 +4,12 @@ package main +import ( + "fmt" + "os" + "reflect" +) + // Test that the struct field in anonunion.go was promoted. var v1 T var v2 = v1.L @@ -23,4 +29,26 @@ var v7 = S{} var _ = issue38649{X: 0} func main() { + pass := true + + // The Go translation of bitfields should not have any of the + // bitfield types. The order in which bitfields are laid out + // in memory is implementation defined, so we can't easily + // know how a bitfield should correspond to a Go type, even if + // it appears to be aligned correctly. + bitfieldType := reflect.TypeOf(bitfields{}) + check := func(name string) { + _, ok := bitfieldType.FieldByName(name) + if ok { + fmt.Fprintf(os.Stderr, "found unexpected bitfields field %s\n", name) + pass = false + } + } + check("Short1") + check("Short2") + check("Short3") + + if !pass { + os.Exit(1) + } } diff --git a/misc/cgo/testgodefs/testgodefs_test.go b/misc/cgo/testgodefs/testgodefs_test.go index e4085f9ca8..4c2312c1c8 100644 --- a/misc/cgo/testgodefs/testgodefs_test.go +++ b/misc/cgo/testgodefs/testgodefs_test.go @@ -19,6 +19,7 @@ import ( // import "C" block. Add more tests here. var filePrefixes = []string{ "anonunion", + "bitfields", "issue8478", "fieldtypedef", "issue37479", diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 9179b5490e..eb6c1a5c89 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -2831,21 +2831,11 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct tgo := t.Go size := t.Size talign := t.Align - if f.BitSize > 0 { - switch f.BitSize { - case 8, 16, 32, 64: - default: - continue - } - size = f.BitSize / 8 - name := tgo.(*ast.Ident).String() - if strings.HasPrefix(name, "int") { - name = "int" - } else { - name = "uint" - } - tgo = ast.NewIdent(name + fmt.Sprint(f.BitSize)) - talign = size + if f.BitOffset > 0 || f.BitSize > 0 { + // The layout of bitfields is implementation defined, + // so we don't know how they correspond to Go fields + // even if they are aligned at byte boundaries. + continue } if talign > 0 && f.ByteOffset%talign != 0 { -- cgit v1.2.3-54-g00ecf From 790fa1c546a05936406f6bbf24f6a6ddeb6ec6ad Mon Sep 17 00:00:00 2001 From: Martin Möhrmann Date: Mon, 14 Sep 2020 16:30:43 +0200 Subject: cmd/compile: unify reflect, string and slice copy runtime functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a common runtime slicecopy function to copy strings or slices into slices. This deduplicates similar code previously used in reflect.slicecopy and runtime.stringslicecopy. Change-Id: I09572ff0647a9e12bb5c6989689ce1c43f16b7f1 Reviewed-on: https://go-review.googlesource.com/c/go/+/254658 Run-TryBot: Martin Möhrmann TryBot-Result: Go Bot Trust: Martin Möhrmann Reviewed-by: Keith Randall --- src/cmd/compile/internal/gc/builtin.go | 408 ++++++++++++------------- src/cmd/compile/internal/gc/builtin/runtime.go | 3 +- src/cmd/compile/internal/gc/subr.go | 12 +- src/cmd/compile/internal/gc/walk.go | 60 ++-- src/runtime/mbarrier.go | 23 +- src/runtime/slice.go | 44 +-- 6 files changed, 247 insertions(+), 303 deletions(-) diff --git a/src/cmd/compile/internal/gc/builtin.go b/src/cmd/compile/internal/gc/builtin.go index 861ffaaa5b..da7b107bfe 100644 --- a/src/cmd/compile/internal/gc/builtin.go +++ b/src/cmd/compile/internal/gc/builtin.go @@ -64,136 +64,135 @@ var runtimeDecls = [...]struct { {"stringtoslicebyte", funcTag, 49}, {"stringtoslicerune", funcTag, 52}, {"slicecopy", funcTag, 53}, - {"slicestringcopy", funcTag, 54}, - {"decoderune", funcTag, 55}, - {"countrunes", funcTag, 56}, - {"convI2I", funcTag, 57}, - {"convT16", funcTag, 58}, - {"convT32", funcTag, 58}, - {"convT64", funcTag, 58}, - {"convTstring", funcTag, 58}, - {"convTslice", funcTag, 58}, - {"convT2E", funcTag, 59}, - {"convT2Enoptr", funcTag, 59}, - {"convT2I", funcTag, 59}, - {"convT2Inoptr", funcTag, 59}, - {"assertE2I", funcTag, 57}, - {"assertE2I2", funcTag, 60}, - {"assertI2I", funcTag, 57}, - {"assertI2I2", funcTag, 60}, - {"panicdottypeE", funcTag, 61}, - {"panicdottypeI", funcTag, 61}, - {"panicnildottype", funcTag, 62}, - {"ifaceeq", funcTag, 64}, - {"efaceeq", funcTag, 64}, - {"fastrand", funcTag, 66}, - {"makemap64", funcTag, 68}, - {"makemap", funcTag, 69}, - {"makemap_small", funcTag, 70}, - {"mapaccess1", funcTag, 71}, - {"mapaccess1_fast32", funcTag, 72}, - {"mapaccess1_fast64", funcTag, 72}, - {"mapaccess1_faststr", funcTag, 72}, - {"mapaccess1_fat", funcTag, 73}, - {"mapaccess2", funcTag, 74}, - {"mapaccess2_fast32", funcTag, 75}, - {"mapaccess2_fast64", funcTag, 75}, - {"mapaccess2_faststr", funcTag, 75}, - {"mapaccess2_fat", funcTag, 76}, - {"mapassign", funcTag, 71}, - {"mapassign_fast32", funcTag, 72}, - {"mapassign_fast32ptr", funcTag, 72}, - {"mapassign_fast64", funcTag, 72}, - {"mapassign_fast64ptr", funcTag, 72}, - {"mapassign_faststr", funcTag, 72}, - {"mapiterinit", funcTag, 77}, - {"mapdelete", funcTag, 77}, - {"mapdelete_fast32", funcTag, 78}, - {"mapdelete_fast64", funcTag, 78}, - {"mapdelete_faststr", funcTag, 78}, - {"mapiternext", funcTag, 79}, - {"mapclear", funcTag, 80}, - {"makechan64", funcTag, 82}, - {"makechan", funcTag, 83}, - {"chanrecv1", funcTag, 85}, - {"chanrecv2", funcTag, 86}, - {"chansend1", funcTag, 88}, + {"decoderune", funcTag, 54}, + {"countrunes", funcTag, 55}, + {"convI2I", funcTag, 56}, + {"convT16", funcTag, 57}, + {"convT32", funcTag, 57}, + {"convT64", funcTag, 57}, + {"convTstring", funcTag, 57}, + {"convTslice", funcTag, 57}, + {"convT2E", funcTag, 58}, + {"convT2Enoptr", funcTag, 58}, + {"convT2I", funcTag, 58}, + {"convT2Inoptr", funcTag, 58}, + {"assertE2I", funcTag, 56}, + {"assertE2I2", funcTag, 59}, + {"assertI2I", funcTag, 56}, + {"assertI2I2", funcTag, 59}, + {"panicdottypeE", funcTag, 60}, + {"panicdottypeI", funcTag, 60}, + {"panicnildottype", funcTag, 61}, + {"ifaceeq", funcTag, 63}, + {"efaceeq", funcTag, 63}, + {"fastrand", funcTag, 65}, + {"makemap64", funcTag, 67}, + {"makemap", funcTag, 68}, + {"makemap_small", funcTag, 69}, + {"mapaccess1", funcTag, 70}, + {"mapaccess1_fast32", funcTag, 71}, + {"mapaccess1_fast64", funcTag, 71}, + {"mapaccess1_faststr", funcTag, 71}, + {"mapaccess1_fat", funcTag, 72}, + {"mapaccess2", funcTag, 73}, + {"mapaccess2_fast32", funcTag, 74}, + {"mapaccess2_fast64", funcTag, 74}, + {"mapaccess2_faststr", funcTag, 74}, + {"mapaccess2_fat", funcTag, 75}, + {"mapassign", funcTag, 70}, + {"mapassign_fast32", funcTag, 71}, + {"mapassign_fast32ptr", funcTag, 71}, + {"mapassign_fast64", funcTag, 71}, + {"mapassign_fast64ptr", funcTag, 71}, + {"mapassign_faststr", funcTag, 71}, + {"mapiterinit", funcTag, 76}, + {"mapdelete", funcTag, 76}, + {"mapdelete_fast32", funcTag, 77}, + {"mapdelete_fast64", funcTag, 77}, + {"mapdelete_faststr", funcTag, 77}, + {"mapiternext", funcTag, 78}, + {"mapclear", funcTag, 79}, + {"makechan64", funcTag, 81}, + {"makechan", funcTag, 82}, + {"chanrecv1", funcTag, 84}, + {"chanrecv2", funcTag, 85}, + {"chansend1", funcTag, 87}, {"closechan", funcTag, 30}, - {"writeBarrier", varTag, 90}, - {"typedmemmove", funcTag, 91}, - {"typedmemclr", funcTag, 92}, - {"typedslicecopy", funcTag, 93}, - {"selectnbsend", funcTag, 94}, - {"selectnbrecv", funcTag, 95}, - {"selectnbrecv2", funcTag, 97}, - {"selectsetpc", funcTag, 98}, - {"selectgo", funcTag, 99}, + {"writeBarrier", varTag, 89}, + {"typedmemmove", funcTag, 90}, + {"typedmemclr", funcTag, 91}, + {"typedslicecopy", funcTag, 92}, + {"selectnbsend", funcTag, 93}, + {"selectnbrecv", funcTag, 94}, + {"selectnbrecv2", funcTag, 96}, + {"selectsetpc", funcTag, 97}, + {"selectgo", funcTag, 98}, {"block", funcTag, 9}, - {"makeslice", funcTag, 100}, - {"makeslice64", funcTag, 101}, - {"makeslicecopy", funcTag, 102}, - {"growslice", funcTag, 104}, - {"memmove", funcTag, 105}, - {"memclrNoHeapPointers", funcTag, 106}, - {"memclrHasPointers", funcTag, 106}, - {"memequal", funcTag, 107}, - {"memequal0", funcTag, 108}, - {"memequal8", funcTag, 108}, - {"memequal16", funcTag, 108}, - {"memequal32", funcTag, 108}, - {"memequal64", funcTag, 108}, - {"memequal128", funcTag, 108}, - {"f32equal", funcTag, 109}, - {"f64equal", funcTag, 109}, - {"c64equal", funcTag, 109}, - {"c128equal", funcTag, 109}, - {"strequal", funcTag, 109}, - {"interequal", funcTag, 109}, - {"nilinterequal", funcTag, 109}, - {"memhash", funcTag, 110}, - {"memhash0", funcTag, 111}, - {"memhash8", funcTag, 111}, - {"memhash16", funcTag, 111}, - {"memhash32", funcTag, 111}, - {"memhash64", funcTag, 111}, - {"memhash128", funcTag, 111}, - {"f32hash", funcTag, 111}, - {"f64hash", funcTag, 111}, - {"c64hash", funcTag, 111}, - {"c128hash", funcTag, 111}, - {"strhash", funcTag, 111}, - {"interhash", funcTag, 111}, - {"nilinterhash", funcTag, 111}, - {"int64div", funcTag, 112}, - {"uint64div", funcTag, 113}, - {"int64mod", funcTag, 112}, - {"uint64mod", funcTag, 113}, - {"float64toint64", funcTag, 114}, - {"float64touint64", funcTag, 115}, - {"float64touint32", funcTag, 116}, - {"int64tofloat64", funcTag, 117}, - {"uint64tofloat64", funcTag, 118}, - {"uint32tofloat64", funcTag, 119}, - {"complex128div", funcTag, 120}, - {"racefuncenter", funcTag, 121}, + {"makeslice", funcTag, 99}, + {"makeslice64", funcTag, 100}, + {"makeslicecopy", funcTag, 101}, + {"growslice", funcTag, 103}, + {"memmove", funcTag, 104}, + {"memclrNoHeapPointers", funcTag, 105}, + {"memclrHasPointers", funcTag, 105}, + {"memequal", funcTag, 106}, + {"memequal0", funcTag, 107}, + {"memequal8", funcTag, 107}, + {"memequal16", funcTag, 107}, + {"memequal32", funcTag, 107}, + {"memequal64", funcTag, 107}, + {"memequal128", funcTag, 107}, + {"f32equal", funcTag, 108}, + {"f64equal", funcTag, 108}, + {"c64equal", funcTag, 108}, + {"c128equal", funcTag, 108}, + {"strequal", funcTag, 108}, + {"interequal", funcTag, 108}, + {"nilinterequal", funcTag, 108}, + {"memhash", funcTag, 109}, + {"memhash0", funcTag, 110}, + {"memhash8", funcTag, 110}, + {"memhash16", funcTag, 110}, + {"memhash32", funcTag, 110}, + {"memhash64", funcTag, 110}, + {"memhash128", funcTag, 110}, + {"f32hash", funcTag, 110}, + {"f64hash", funcTag, 110}, + {"c64hash", funcTag, 110}, + {"c128hash", funcTag, 110}, + {"strhash", funcTag, 110}, + {"interhash", funcTag, 110}, + {"nilinterhash", funcTag, 110}, + {"int64div", funcTag, 111}, + {"uint64div", funcTag, 112}, + {"int64mod", funcTag, 111}, + {"uint64mod", funcTag, 112}, + {"float64toint64", funcTag, 113}, + {"float64touint64", funcTag, 114}, + {"float64touint32", funcTag, 115}, + {"int64tofloat64", funcTag, 116}, + {"uint64tofloat64", funcTag, 117}, + {"uint32tofloat64", funcTag, 118}, + {"complex128div", funcTag, 119}, + {"racefuncenter", funcTag, 120}, {"racefuncenterfp", funcTag, 9}, {"racefuncexit", funcTag, 9}, - {"raceread", funcTag, 121}, - {"racewrite", funcTag, 121}, - {"racereadrange", funcTag, 122}, - {"racewriterange", funcTag, 122}, - {"msanread", funcTag, 122}, - {"msanwrite", funcTag, 122}, - {"checkptrAlignment", funcTag, 123}, - {"checkptrArithmetic", funcTag, 125}, - {"libfuzzerTraceCmp1", funcTag, 127}, - {"libfuzzerTraceCmp2", funcTag, 129}, - {"libfuzzerTraceCmp4", funcTag, 130}, - {"libfuzzerTraceCmp8", funcTag, 131}, - {"libfuzzerTraceConstCmp1", funcTag, 127}, - {"libfuzzerTraceConstCmp2", funcTag, 129}, - {"libfuzzerTraceConstCmp4", funcTag, 130}, - {"libfuzzerTraceConstCmp8", funcTag, 131}, + {"raceread", funcTag, 120}, + {"racewrite", funcTag, 120}, + {"racereadrange", funcTag, 121}, + {"racewriterange", funcTag, 121}, + {"msanread", funcTag, 121}, + {"msanwrite", funcTag, 121}, + {"checkptrAlignment", funcTag, 122}, + {"checkptrArithmetic", funcTag, 124}, + {"libfuzzerTraceCmp1", funcTag, 126}, + {"libfuzzerTraceCmp2", funcTag, 128}, + {"libfuzzerTraceCmp4", funcTag, 129}, + {"libfuzzerTraceCmp8", funcTag, 130}, + {"libfuzzerTraceConstCmp1", funcTag, 126}, + {"libfuzzerTraceConstCmp2", funcTag, 128}, + {"libfuzzerTraceConstCmp4", funcTag, 129}, + {"libfuzzerTraceConstCmp8", funcTag, 130}, {"x86HasPOPCNT", varTag, 6}, {"x86HasSSE41", varTag, 6}, {"x86HasFMA", varTag, 6}, @@ -202,7 +201,7 @@ var runtimeDecls = [...]struct { } func runtimeTypes() []*types.Type { - var typs [132]*types.Type + var typs [131]*types.Type typs[0] = types.Bytetype typs[1] = types.NewPtr(typs[0]) typs[2] = types.Types[TANY] @@ -257,83 +256,82 @@ func runtimeTypes() []*types.Type { typs[51] = types.NewPtr(typs[50]) typs[52] = functype(nil, []*Node{anonfield(typs[51]), anonfield(typs[28])}, []*Node{anonfield(typs[46])}) typs[53] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[5])}, []*Node{anonfield(typs[15])}) - typs[54] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[28])}, []*Node{anonfield(typs[15])}) - typs[55] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[15])}, []*Node{anonfield(typs[45]), anonfield(typs[15])}) - typs[56] = functype(nil, []*Node{anonfield(typs[28])}, []*Node{anonfield(typs[15])}) - typs[57] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2])}) - typs[58] = functype(nil, []*Node{anonfield(typs[2])}, []*Node{anonfield(typs[7])}) - typs[59] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, []*Node{anonfield(typs[2])}) - typs[60] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2]), anonfield(typs[6])}) - typs[61] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[1])}, nil) - typs[62] = functype(nil, []*Node{anonfield(typs[1])}, nil) - typs[63] = types.NewPtr(typs[5]) - typs[64] = functype(nil, []*Node{anonfield(typs[63]), anonfield(typs[7]), anonfield(typs[7])}, []*Node{anonfield(typs[6])}) - typs[65] = types.Types[TUINT32] - typs[66] = functype(nil, nil, []*Node{anonfield(typs[65])}) - typs[67] = types.NewMap(typs[2], typs[2]) - typs[68] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[3])}, []*Node{anonfield(typs[67])}) - typs[69] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[3])}, []*Node{anonfield(typs[67])}) - typs[70] = functype(nil, nil, []*Node{anonfield(typs[67])}) - typs[71] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3])}, []*Node{anonfield(typs[3])}) - typs[72] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[2])}, []*Node{anonfield(typs[3])}) - typs[73] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3])}) - typs[74] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) - typs[75] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[2])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) - typs[76] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) - typs[77] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3])}, nil) - typs[78] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[2])}, nil) - typs[79] = functype(nil, []*Node{anonfield(typs[3])}, nil) - typs[80] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67])}, nil) - typs[81] = types.NewChan(typs[2], types.Cboth) - typs[82] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22])}, []*Node{anonfield(typs[81])}) - typs[83] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[81])}) - typs[84] = types.NewChan(typs[2], types.Crecv) - typs[85] = functype(nil, []*Node{anonfield(typs[84]), anonfield(typs[3])}, nil) - typs[86] = functype(nil, []*Node{anonfield(typs[84]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) - typs[87] = types.NewChan(typs[2], types.Csend) - typs[88] = functype(nil, []*Node{anonfield(typs[87]), anonfield(typs[3])}, nil) - typs[89] = types.NewArray(typs[0], 3) - typs[90] = tostruct([]*Node{namedfield("enabled", typs[6]), namedfield("pad", typs[89]), namedfield("needed", typs[6]), namedfield("cgo", typs[6]), namedfield("alignme", typs[24])}) - typs[91] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil) - typs[92] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil) - typs[93] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15])}, []*Node{anonfield(typs[15])}) - typs[94] = functype(nil, []*Node{anonfield(typs[87]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) - typs[95] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[84])}, []*Node{anonfield(typs[6])}) - typs[96] = types.NewPtr(typs[6]) - typs[97] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[96]), anonfield(typs[84])}, []*Node{anonfield(typs[6])}) - typs[98] = functype(nil, []*Node{anonfield(typs[63])}, nil) - typs[99] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[63]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[6])}, []*Node{anonfield(typs[15]), anonfield(typs[6])}) - typs[100] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[7])}) - typs[101] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[22])}, []*Node{anonfield(typs[7])}) - typs[102] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[7])}, []*Node{anonfield(typs[7])}) - typs[103] = types.NewSlice(typs[2]) - typs[104] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[103]), anonfield(typs[15])}, []*Node{anonfield(typs[103])}) - typs[105] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[5])}, nil) - typs[106] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5])}, nil) - typs[107] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[5])}, []*Node{anonfield(typs[6])}) - typs[108] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) - typs[109] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[7])}, []*Node{anonfield(typs[6])}) - typs[110] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5]), anonfield(typs[5])}, []*Node{anonfield(typs[5])}) - typs[111] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5])}, []*Node{anonfield(typs[5])}) - typs[112] = functype(nil, []*Node{anonfield(typs[22]), anonfield(typs[22])}, []*Node{anonfield(typs[22])}) - typs[113] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, []*Node{anonfield(typs[24])}) - typs[114] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[22])}) - typs[115] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[24])}) - typs[116] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[65])}) - typs[117] = functype(nil, []*Node{anonfield(typs[22])}, []*Node{anonfield(typs[20])}) - typs[118] = functype(nil, []*Node{anonfield(typs[24])}, []*Node{anonfield(typs[20])}) - typs[119] = functype(nil, []*Node{anonfield(typs[65])}, []*Node{anonfield(typs[20])}) - typs[120] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[26])}, []*Node{anonfield(typs[26])}) - typs[121] = functype(nil, []*Node{anonfield(typs[5])}, nil) - typs[122] = functype(nil, []*Node{anonfield(typs[5]), anonfield(typs[5])}, nil) - typs[123] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[1]), anonfield(typs[5])}, nil) - typs[124] = types.NewSlice(typs[7]) - typs[125] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[124])}, nil) - typs[126] = types.Types[TUINT8] - typs[127] = functype(nil, []*Node{anonfield(typs[126]), anonfield(typs[126])}, nil) - typs[128] = types.Types[TUINT16] - typs[129] = functype(nil, []*Node{anonfield(typs[128]), anonfield(typs[128])}, nil) - typs[130] = functype(nil, []*Node{anonfield(typs[65]), anonfield(typs[65])}, nil) - typs[131] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, nil) + typs[54] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[15])}, []*Node{anonfield(typs[45]), anonfield(typs[15])}) + typs[55] = functype(nil, []*Node{anonfield(typs[28])}, []*Node{anonfield(typs[15])}) + typs[56] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2])}) + typs[57] = functype(nil, []*Node{anonfield(typs[2])}, []*Node{anonfield(typs[7])}) + typs[58] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, []*Node{anonfield(typs[2])}) + typs[59] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2]), anonfield(typs[6])}) + typs[60] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[1])}, nil) + typs[61] = functype(nil, []*Node{anonfield(typs[1])}, nil) + typs[62] = types.NewPtr(typs[5]) + typs[63] = functype(nil, []*Node{anonfield(typs[62]), anonfield(typs[7]), anonfield(typs[7])}, []*Node{anonfield(typs[6])}) + typs[64] = types.Types[TUINT32] + typs[65] = functype(nil, nil, []*Node{anonfield(typs[64])}) + typs[66] = types.NewMap(typs[2], typs[2]) + typs[67] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[3])}, []*Node{anonfield(typs[66])}) + typs[68] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[3])}, []*Node{anonfield(typs[66])}) + typs[69] = functype(nil, nil, []*Node{anonfield(typs[66])}) + typs[70] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3])}, []*Node{anonfield(typs[3])}) + typs[71] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[2])}, []*Node{anonfield(typs[3])}) + typs[72] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3])}) + typs[73] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) + typs[74] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[2])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) + typs[75] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) + typs[76] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3])}, nil) + typs[77] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[2])}, nil) + typs[78] = functype(nil, []*Node{anonfield(typs[3])}, nil) + typs[79] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66])}, nil) + typs[80] = types.NewChan(typs[2], types.Cboth) + typs[81] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22])}, []*Node{anonfield(typs[80])}) + typs[82] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[80])}) + typs[83] = types.NewChan(typs[2], types.Crecv) + typs[84] = functype(nil, []*Node{anonfield(typs[83]), anonfield(typs[3])}, nil) + typs[85] = functype(nil, []*Node{anonfield(typs[83]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) + typs[86] = types.NewChan(typs[2], types.Csend) + typs[87] = functype(nil, []*Node{anonfield(typs[86]), anonfield(typs[3])}, nil) + typs[88] = types.NewArray(typs[0], 3) + typs[89] = tostruct([]*Node{namedfield("enabled", typs[6]), namedfield("pad", typs[88]), namedfield("needed", typs[6]), namedfield("cgo", typs[6]), namedfield("alignme", typs[24])}) + typs[90] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil) + typs[91] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil) + typs[92] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15])}, []*Node{anonfield(typs[15])}) + typs[93] = functype(nil, []*Node{anonfield(typs[86]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) + typs[94] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[83])}, []*Node{anonfield(typs[6])}) + typs[95] = types.NewPtr(typs[6]) + typs[96] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[95]), anonfield(typs[83])}, []*Node{anonfield(typs[6])}) + typs[97] = functype(nil, []*Node{anonfield(typs[62])}, nil) + typs[98] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[6])}, []*Node{anonfield(typs[15]), anonfield(typs[6])}) + typs[99] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[7])}) + typs[100] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[22])}, []*Node{anonfield(typs[7])}) + typs[101] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[7])}, []*Node{anonfield(typs[7])}) + typs[102] = types.NewSlice(typs[2]) + typs[103] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[102]), anonfield(typs[15])}, []*Node{anonfield(typs[102])}) + typs[104] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[5])}, nil) + typs[105] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5])}, nil) + typs[106] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[5])}, []*Node{anonfield(typs[6])}) + typs[107] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) + typs[108] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[7])}, []*Node{anonfield(typs[6])}) + typs[109] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5]), anonfield(typs[5])}, []*Node{anonfield(typs[5])}) + typs[110] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5])}, []*Node{anonfield(typs[5])}) + typs[111] = functype(nil, []*Node{anonfield(typs[22]), anonfield(typs[22])}, []*Node{anonfield(typs[22])}) + typs[112] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, []*Node{anonfield(typs[24])}) + typs[113] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[22])}) + typs[114] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[24])}) + typs[115] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[64])}) + typs[116] = functype(nil, []*Node{anonfield(typs[22])}, []*Node{anonfield(typs[20])}) + typs[117] = functype(nil, []*Node{anonfield(typs[24])}, []*Node{anonfield(typs[20])}) + typs[118] = functype(nil, []*Node{anonfield(typs[64])}, []*Node{anonfield(typs[20])}) + typs[119] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[26])}, []*Node{anonfield(typs[26])}) + typs[120] = functype(nil, []*Node{anonfield(typs[5])}, nil) + typs[121] = functype(nil, []*Node{anonfield(typs[5]), anonfield(typs[5])}, nil) + typs[122] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[1]), anonfield(typs[5])}, nil) + typs[123] = types.NewSlice(typs[7]) + typs[124] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[123])}, nil) + typs[125] = types.Types[TUINT8] + typs[126] = functype(nil, []*Node{anonfield(typs[125]), anonfield(typs[125])}, nil) + typs[127] = types.Types[TUINT16] + typs[128] = functype(nil, []*Node{anonfield(typs[127]), anonfield(typs[127])}, nil) + typs[129] = functype(nil, []*Node{anonfield(typs[64]), anonfield(typs[64])}, nil) + typs[130] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, nil) return typs[:] } diff --git a/src/cmd/compile/internal/gc/builtin/runtime.go b/src/cmd/compile/internal/gc/builtin/runtime.go index 635da80f7c..02d6c7b7f5 100644 --- a/src/cmd/compile/internal/gc/builtin/runtime.go +++ b/src/cmd/compile/internal/gc/builtin/runtime.go @@ -75,8 +75,7 @@ func slicebytetostringtmp(ptr *byte, n int) string func slicerunetostring(*[32]byte, []rune) string func stringtoslicebyte(*[32]byte, string) []byte func stringtoslicerune(*[32]rune, string) []rune -func slicecopy(toPtr *any, toLen int, frPtr *any, frLen int, wid uintptr) int -func slicestringcopy(toPtr *byte, toLen int, fr string) int +func slicecopy(toPtr *any, toLen int, fromPtr *any, fromLen int, wid uintptr) int func decoderune(string, int) (retv rune, retk int) func countrunes(string) int diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index 5a5833d19f..8883e75c49 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -928,16 +928,20 @@ func (o Op) IsSlice3() bool { return false } -// slicePtrLen extracts the pointer and length from a slice. +// backingArrayPtrLen extracts the pointer and length from a slice or string. // This constructs two nodes referring to n, so n must be a cheapexpr. -func (n *Node) slicePtrLen() (ptr, len *Node) { +func (n *Node) backingArrayPtrLen() (ptr, len *Node) { var init Nodes c := cheapexpr(n, &init) if c != n || init.Len() != 0 { - Fatalf("slicePtrLen not cheap: %v", n) + Fatalf("backingArrayPtrLen not cheap: %v", n) } ptr = nod(OSPTR, n, nil) - ptr.Type = n.Type.Elem().PtrTo() + if n.Type.IsString() { + ptr.Type = types.Types[TUINT8].PtrTo() + } else { + ptr.Type = n.Type.Elem().PtrTo() + } len = nod(OLEN, n, nil) len.Type = types.Types[TINT] return ptr, len diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 2d29366880..c3a740d4cc 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -1484,7 +1484,7 @@ opswitch: } else { // slicebytetostring(*[32]byte, ptr *byte, n int) string n.Left = cheapexpr(n.Left, init) - ptr, len := n.Left.slicePtrLen() + ptr, len := n.Left.backingArrayPtrLen() n = mkcall("slicebytetostring", n.Type, init, a, ptr, len) } @@ -1497,7 +1497,7 @@ opswitch: } // slicebytetostringtmp(ptr *byte, n int) string n.Left = cheapexpr(n.Left, init) - ptr, len := n.Left.slicePtrLen() + ptr, len := n.Left.backingArrayPtrLen() n = mkcall("slicebytetostringtmp", n.Type, init, ptr, len) case OSTR2BYTES: @@ -2764,36 +2764,25 @@ func appendslice(n *Node, init *Nodes) *Node { // instantiate typedslicecopy(typ *type, dstPtr *any, dstLen int, srcPtr *any, srcLen int) int fn := syslook("typedslicecopy") fn = substArgTypes(fn, l1.Type.Elem(), l2.Type.Elem()) - ptr1, len1 := nptr1.slicePtrLen() - ptr2, len2 := nptr2.slicePtrLen() + ptr1, len1 := nptr1.backingArrayPtrLen() + ptr2, len2 := nptr2.backingArrayPtrLen() ncopy = mkcall1(fn, types.Types[TINT], &nodes, typename(elemtype), ptr1, len1, ptr2, len2) - } else if instrumenting && !compiling_runtime { - // rely on runtime to instrument copy. - // copy(s[len(l1):], l2) + // rely on runtime to instrument: + // copy(s[len(l1):], l2) + // l2 can be a slice or string. nptr1 := nod(OSLICE, s, nil) nptr1.Type = s.Type nptr1.SetSliceBounds(nod(OLEN, l1, nil), nil, nil) nptr1 = cheapexpr(nptr1, &nodes) - nptr2 := l2 - if l2.Type.IsString() { - // instantiate func slicestringcopy(toPtr *byte, toLen int, fr string) int - fn := syslook("slicestringcopy") - ptr, len := nptr1.slicePtrLen() - str := nod(OCONVNOP, nptr2, nil) - str.Type = types.Types[TSTRING] - ncopy = mkcall1(fn, types.Types[TINT], &nodes, ptr, len, str) - } else { - // instantiate func slicecopy(to any, fr any, wid uintptr) int - fn := syslook("slicecopy") - fn = substArgTypes(fn, l1.Type.Elem(), l2.Type.Elem()) - ptr1, len1 := nptr1.slicePtrLen() - ptr2, len2 := nptr2.slicePtrLen() - ncopy = mkcall1(fn, types.Types[TINT], &nodes, ptr1, len1, ptr2, len2, nodintconst(elemtype.Width)) - } + ptr1, len1 := nptr1.backingArrayPtrLen() + ptr2, len2 := nptr2.backingArrayPtrLen() + fn := syslook("slicecopy") + fn = substArgTypes(fn, ptr1.Type.Elem(), ptr2.Type.Elem()) + ncopy = mkcall1(fn, types.Types[TINT], &nodes, ptr1, len1, ptr2, len2, nodintconst(elemtype.Width)) } else { // memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) nptr1 := nod(OINDEX, s, nod(OLEN, l1, nil)) @@ -3092,28 +3081,25 @@ func copyany(n *Node, init *Nodes, runtimecall bool) *Node { Curfn.Func.setWBPos(n.Pos) fn := writebarrierfn("typedslicecopy", n.Left.Type.Elem(), n.Right.Type.Elem()) n.Left = cheapexpr(n.Left, init) - ptrL, lenL := n.Left.slicePtrLen() + ptrL, lenL := n.Left.backingArrayPtrLen() n.Right = cheapexpr(n.Right, init) - ptrR, lenR := n.Right.slicePtrLen() + ptrR, lenR := n.Right.backingArrayPtrLen() return mkcall1(fn, n.Type, init, typename(n.Left.Type.Elem()), ptrL, lenL, ptrR, lenR) } if runtimecall { - if n.Right.Type.IsString() { - fn := syslook("slicestringcopy") - n.Left = cheapexpr(n.Left, init) - ptr, len := n.Left.slicePtrLen() - str := nod(OCONVNOP, n.Right, nil) - str.Type = types.Types[TSTRING] - return mkcall1(fn, n.Type, init, ptr, len, str) - } + // rely on runtime to instrument: + // copy(n.Left, n.Right) + // n.Right can be a slice or string. - fn := syslook("slicecopy") - fn = substArgTypes(fn, n.Left.Type.Elem(), n.Right.Type.Elem()) n.Left = cheapexpr(n.Left, init) - ptrL, lenL := n.Left.slicePtrLen() + ptrL, lenL := n.Left.backingArrayPtrLen() n.Right = cheapexpr(n.Right, init) - ptrR, lenR := n.Right.slicePtrLen() + ptrR, lenR := n.Right.backingArrayPtrLen() + + fn := syslook("slicecopy") + fn = substArgTypes(fn, ptrL.Type.Elem(), ptrR.Type.Elem()) + return mkcall1(fn, n.Type, init, ptrL, lenL, ptrR, lenR, nodintconst(n.Left.Type.Elem().Width)) } diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go index f7875d327a..2b5affce52 100644 --- a/src/runtime/mbarrier.go +++ b/src/runtime/mbarrier.go @@ -281,28 +281,7 @@ func typedslicecopy(typ *_type, dstPtr unsafe.Pointer, dstLen int, srcPtr unsafe //go:linkname reflect_typedslicecopy reflect.typedslicecopy func reflect_typedslicecopy(elemType *_type, dst, src slice) int { if elemType.ptrdata == 0 { - n := dst.len - if n > src.len { - n = src.len - } - if n == 0 { - return 0 - } - - size := uintptr(n) * elemType.size - if raceenabled { - callerpc := getcallerpc() - pc := funcPC(reflect_typedslicecopy) - racewriterangepc(dst.array, size, callerpc, pc) - racereadrangepc(src.array, size, callerpc, pc) - } - if msanenabled { - msanwrite(dst.array, size) - msanread(src.array, size) - } - - memmove(dst.array, src.array, size) - return n + return slicecopy(dst.array, dst.len, src.array, src.len, elemType.size) } return typedslicecopy(elemType, dst.array, dst.len, src.array, src.len) } diff --git a/src/runtime/slice.go b/src/runtime/slice.go index 0418ace25a..82a45c78a9 100644 --- a/src/runtime/slice.go +++ b/src/runtime/slice.go @@ -243,12 +243,13 @@ func isPowerOfTwo(x uintptr) bool { return x&(x-1) == 0 } -func slicecopy(toPtr unsafe.Pointer, toLen int, fmPtr unsafe.Pointer, fmLen int, width uintptr) int { - if fmLen == 0 || toLen == 0 { +// slicecopy is used to copy from a string or slice of pointerless elements into a slice. +func slicecopy(toPtr unsafe.Pointer, toLen int, fromPtr unsafe.Pointer, fromLen int, width uintptr) int { + if fromLen == 0 || toLen == 0 { return 0 } - n := fmLen + n := fromLen if toLen < n { n = toLen } @@ -257,46 +258,23 @@ func slicecopy(toPtr unsafe.Pointer, toLen int, fmPtr unsafe.Pointer, fmLen int, return n } + size := uintptr(n) * width if raceenabled { callerpc := getcallerpc() pc := funcPC(slicecopy) - racereadrangepc(fmPtr, uintptr(n*int(width)), callerpc, pc) - racewriterangepc(toPtr, uintptr(n*int(width)), callerpc, pc) + racereadrangepc(fromPtr, size, callerpc, pc) + racewriterangepc(toPtr, size, callerpc, pc) } if msanenabled { - msanread(fmPtr, uintptr(n*int(width))) - msanwrite(toPtr, uintptr(n*int(width))) + msanread(fromPtr, size) + msanwrite(toPtr, size) } - size := uintptr(n) * width if size == 1 { // common case worth about 2x to do here // TODO: is this still worth it with new memmove impl? - *(*byte)(toPtr) = *(*byte)(fmPtr) // known to be a byte pointer + *(*byte)(toPtr) = *(*byte)(fromPtr) // known to be a byte pointer } else { - memmove(toPtr, fmPtr, size) - } - return n -} - -func slicestringcopy(toPtr *byte, toLen int, fm string) int { - if len(fm) == 0 || toLen == 0 { - return 0 - } - - n := len(fm) - if toLen < n { - n = toLen + memmove(toPtr, fromPtr, size) } - - if raceenabled { - callerpc := getcallerpc() - pc := funcPC(slicestringcopy) - racewriterangepc(unsafe.Pointer(toPtr), uintptr(n), callerpc, pc) - } - if msanenabled { - msanwrite(unsafe.Pointer(toPtr), uintptr(n)) - } - - memmove(unsafe.Pointer(toPtr), stringStructOf(&fm).str, uintptr(n)) return n } -- cgit v1.2.3-54-g00ecf From e82c9bd81654dab14f786c26af2dd8ea3a7a1737 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 15 Sep 2020 13:49:45 +0200 Subject: os, internal/syscall/unix: use pipe2 instead of pipe on illumos Illumos provides the pipe2 syscall. Add a wrapper to internal/syscall/unix and use it to implement os.Pipe. Change-Id: I26ecdbcae1e8d51f80e2bc8a86fb129826387b1f Reviewed-on: https://go-review.googlesource.com/c/go/+/254981 Trust: Tobias Klauser Run-TryBot: Tobias Klauser TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor --- src/internal/syscall/unix/pipe2_illumos.go | 34 ++++++++++++++++++++++++++++++ src/os/pipe2_illumos.go | 25 ++++++++++++++++++++++ src/os/pipe_bsd.go | 2 +- 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/internal/syscall/unix/pipe2_illumos.go create mode 100644 src/os/pipe2_illumos.go diff --git a/src/internal/syscall/unix/pipe2_illumos.go b/src/internal/syscall/unix/pipe2_illumos.go new file mode 100644 index 0000000000..f3ac8d29df --- /dev/null +++ b/src/internal/syscall/unix/pipe2_illumos.go @@ -0,0 +1,34 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build illumos + +package unix + +import ( + "syscall" + "unsafe" +) + +//go:cgo_import_dynamic libc_pipe2 pipe2 "libc.so" + +//go:linkname procpipe2 libc_pipe2 + +var procpipe2 uintptr + +type _C_int int32 + +func Pipe2(p []int, flags int) error { + if len(p) != 2 { + return syscall.EINVAL + } + var pp [2]_C_int + _, _, errno := syscall6(uintptr(unsafe.Pointer(&procpipe2)), 2, uintptr(unsafe.Pointer(&pp)), uintptr(flags), 0, 0, 0, 0) + if errno != 0 { + return errno + } + p[0] = int(pp[0]) + p[1] = int(pp[1]) + return nil +} diff --git a/src/os/pipe2_illumos.go b/src/os/pipe2_illumos.go new file mode 100644 index 0000000000..026ce62b9a --- /dev/null +++ b/src/os/pipe2_illumos.go @@ -0,0 +1,25 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build illumos + +package os + +import ( + "internal/syscall/unix" + "syscall" +) + +// Pipe returns a connected pair of Files; reads from r return bytes written to w. +// It returns the files and an error, if any. +func Pipe() (r *File, w *File, err error) { + var p [2]int + + e := unix.Pipe2(p[0:], syscall.O_CLOEXEC) + if e != nil { + return nil, nil, NewSyscallError("pipe", e) + } + + return newFile(uintptr(p[0]), "|0", kindPipe), newFile(uintptr(p[1]), "|1", kindPipe), nil +} diff --git a/src/os/pipe_bsd.go b/src/os/pipe_bsd.go index 0d2d82feb9..115d6baa19 100644 --- a/src/os/pipe_bsd.go +++ b/src/os/pipe_bsd.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly js,wasm solaris +// +build aix darwin dragonfly js,wasm solaris,!illumos package os -- cgit v1.2.3-54-g00ecf From a86b6f23f08fd42154bc5bbfa417da6ee5ef48fb Mon Sep 17 00:00:00 2001 From: diaxu01 Date: Thu, 2 Apr 2020 02:39:28 +0000 Subject: cmd/internal/obj/arm64: optimize the instruction of moving long effective stack address Currently, when the offset of "MOVD $offset(Rn), Rd" is a large positive constant or a negative constant, the assembler will load this offset from the constant pool.This patch gets rid of the constant pool by encoding the offset into two ADD instructions if it's a large positive constant or one SUB instruction if negative. For very large negative offset, it is rarely used, here we don't optimize this case. Optimized case 1: MOVD $-0x100000(R7), R0 Before: LDR 0x67670(constant pool), R27; ADD R27.UXTX, R0, R7 After: SUB $0x100000, R7, R0 Optimized case 2: MOVD $0x123468(R7), R0 Before: LDR 0x67670(constant pool), R27; ADD R27.UXTX, R0, R7 After: ADD $0x123000, R7, R27; ADD $0x000468, R27, R0 1. Binary size before/after. binary size change pkg/linux_arm64 +4KB pkg/tool/linux_arm64 no change go no change gofmt no change 2. go1 benckmark. name old time/op new time/op delta pkg:test/bench/go1 goos:linux goarch:arm64 BinaryTree17-64 7335721401.800000ns +-40% 6264542009.800000ns +-14% ~ (p=0.421 n=5+5) Fannkuch11-64 3886551822.600000ns +- 0% 3875870590.200000ns +- 0% ~ (p=0.151 n=5+5) FmtFprintfEmpty-64 82.960000ns +- 1% 83.900000ns +- 2% +1.13% (p=0.048 n=5+5) FmtFprintfString-64 149.200000ns +- 1% 148.000000ns +- 0% -0.80% (p=0.016 n=5+4) FmtFprintfInt-64 177.000000ns +- 0% 178.400000ns +- 2% ~ (p=0.794 n=4+5) FmtFprintfIntInt-64 240.200000ns +- 2% 239.400000ns +- 4% ~ (p=0.302 n=5+5) FmtFprintfPrefixedInt-64 300.400000ns +- 0% 299.200000ns +- 1% ~ (p=0.119 n=5+5) FmtFprintfFloat-64 360.000000ns +- 0% 361.600000ns +- 3% ~ (p=0.349 n=4+5) FmtManyArgs-64 1064.400000ns +- 1% 1061.400000ns +- 0% ~ (p=0.087 n=5+5) GobDecode-64 12080404.400000ns +- 2% 11637601.000000ns +- 1% -3.67% (p=0.008 n=5+5) GobEncode-64 8474973.800000ns +- 2% 7977801.600000ns +- 2% -5.87% (p=0.008 n=5+5) Gzip-64 416501238.400000ns +- 0% 410463405.400000ns +- 0% -1.45% (p=0.008 n=5+5) Gunzip-64 58088415.200000ns +- 0% 58826209.600000ns +- 0% +1.27% (p=0.008 n=5+5) HTTPClientServer-64 128660.200000ns +-23% 117840.800000ns +- 8% ~ (p=0.222 n=5+5) JSONEncode-64 17547746.800000ns +- 4% 17216180.000000ns +- 1% ~ (p=0.222 n=5+5) JSONDecode-64 80879896.000000ns +- 1% 80063737.200000ns +- 0% -1.01% (p=0.008 n=5+5) Mandelbrot200-64 5484901.600000ns +- 0% 5483614.400000ns +- 0% ~ (p=0.310 n=5+5) GoParse-64 6201166.800000ns +- 6% 6150920.600000ns +- 1% ~ (p=0.548 n=5+5) RegexpMatchEasy0_32-64 135.000000ns +- 0% 139.200000ns +- 7% ~ (p=0.643 n=5+5) RegexpMatchEasy0_1K-64 484.600000ns +- 2% 483.800000ns +- 2% ~ (p=0.984 n=5+5) RegexpMatchEasy1_32-64 128.000000ns +- 1% 124.600000ns +- 1% -2.66% (p=0.008 n=5+5) RegexpMatchEasy1_1K-64 769.400000ns +- 2% 761.400000ns +- 1% ~ (p=0.460 n=5+5) RegexpMatchMedium_32-64 12.900000ns +- 0% 12.500000ns +- 0% -3.10% (p=0.008 n=5+5) RegexpMatchMedium_1K-64 57879.200000ns +- 1% 56512.200000ns +- 0% -2.36% (p=0.008 n=5+5) RegexpMatchHard_32-64 3091.600000ns +- 1% 3071.000000ns +- 0% -0.67% (p=0.048 n=5+5) RegexpMatchHard_1K-64 92941.200000ns +- 1% 92794.000000ns +- 0% ~ (p=1.000 n=5+5) Revcomp-64 1695605187.000000ns +-54% 1821697637.400000ns +-47% ~ (p=1.000 n=5+5) Template-64 112839686.800000ns +- 1% 109964069.200000ns +- 3% ~ (p=0.095 n=5+5) TimeParse-64 587.000000ns +- 0% 587.000000ns +- 0% ~ (all equal) TimeFormat-64 586.000000ns +- 1% 584.200000ns +- 1% ~ (p=0.659 n=5+5) [Geo mean] 81804.262218ns 80694.712973ns -1.36% name old speed new speed delta pkg:test/bench/go1 goos:linux goarch:arm64 GobDecode-64 63.6MB/s +- 2% 66.0MB/s +- 1% +3.78% (p=0.008 n=5+5) GobEncode-64 90.6MB/s +- 2% 96.2MB/s +- 2% +6.23% (p=0.008 n=5+5) Gzip-64 46.6MB/s +- 0% 47.3MB/s +- 0% +1.47% (p=0.008 n=5+5) Gunzip-64 334MB/s +- 0% 330MB/s +- 0% -1.25% (p=0.008 n=5+5) JSONEncode-64 111MB/s +- 4% 113MB/s +- 1% ~ (p=0.222 n=5+5) JSONDecode-64 24.0MB/s +- 1% 24.2MB/s +- 0% +1.02% (p=0.008 n=5+5) GoParse-64 9.35MB/s +- 6% 9.42MB/s +- 1% ~ (p=0.571 n=5+5) RegexpMatchEasy0_32-64 237MB/s +- 0% 231MB/s +- 7% ~ (p=0.690 n=5+5) RegexpMatchEasy0_1K-64 2.11GB/s +- 2% 2.12GB/s +- 2% ~ (p=1.000 n=5+5) RegexpMatchEasy1_32-64 250MB/s +- 1% 257MB/s +- 1% +2.63% (p=0.008 n=5+5) RegexpMatchEasy1_1K-64 1.33GB/s +- 2% 1.35GB/s +- 1% ~ (p=0.548 n=5+5) RegexpMatchMedium_32-64 77.6MB/s +- 0% 79.8MB/s +- 0% +2.80% (p=0.008 n=5+5) RegexpMatchMedium_1K-64 17.7MB/s +- 1% 18.1MB/s +- 0% +2.41% (p=0.008 n=5+5) RegexpMatchHard_32-64 10.4MB/s +- 1% 10.4MB/s +- 0% ~ (p=0.056 n=5+5) RegexpMatchHard_1K-64 11.0MB/s +- 1% 11.0MB/s +- 0% ~ (p=0.984 n=5+5) Revcomp-64 188MB/s +-71% 155MB/s +-71% ~ (p=1.000 n=5+5) Template-64 17.2MB/s +- 1% 17.7MB/s +- 3% ~ (p=0.095 n=5+5) [Geo mean] 79.2MB/s 79.3MB/s +0.24% Change-Id: I593ac3e7037afafc3605ad4b0cfb51d5dd88015d Reviewed-on: https://go-review.googlesource.com/c/go/+/232438 Trust: Alberto Donizetti Run-TryBot: Alberto Donizetti TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/asm/internal/asm/testdata/arm64.s | 15 ++++++++-- src/cmd/internal/obj/arm64/a.out.go | 7 +++-- src/cmd/internal/obj/arm64/anames7.go | 1 + src/cmd/internal/obj/arm64/asm7.go | 46 +++++++++++++++++++++---------- 4 files changed, 50 insertions(+), 19 deletions(-) diff --git a/src/cmd/asm/internal/asm/testdata/arm64.s b/src/cmd/asm/internal/asm/testdata/arm64.s index e106ff2ae1..acfb16b096 100644 --- a/src/cmd/asm/internal/asm/testdata/arm64.s +++ b/src/cmd/asm/internal/asm/testdata/arm64.s @@ -340,8 +340,19 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8 MOVD $0x1111ffff1111aaaa, R1 // MOVD $1230045644216969898, R1 // a1aa8a922122a2f22122e2f2 MOVD $0, R1 // 010080d2 MOVD $-1, R1 // 01008092 - MOVD $0x210000, R0 // MOVD $2162688, R0 // 2004a0d2 - MOVD $0xffffffffffffaaaa, R1 // MOVD $-21846, R1 // a1aa8a92 + MOVD $0x210000, R0 // MOVD $2162688, R0 // 2004a0d2 + MOVD $0xffffffffffffaaaa, R1 // MOVD $-21846, R1 // a1aa8a92 + + MOVD $0x1002(RSP), R1 // MOVD $4098(RSP), R1 // fb074091610b0091 + MOVD $0x1708(RSP), RSP // MOVD $5896(RSP), RSP // fb0740917f231c91 + MOVD $0x2001(R7), R1 // MOVD $8193(R7), R1 // fb08409161070091 + MOVD $0xffffff(R7), R1 // MOVD $16777215(R7), R1 // fbfc7f9161ff3f91 + + MOVD $-0x1(R7), R1 // MOVD $-1(R7), R1 // e10400d1 + MOVD $-0x30(R7), R1 // MOVD $-48(R7), R1 // e1c000d1 + MOVD $-0x708(R7), R1 // MOVD $-1800(R7), R1 // e1201cd1 + MOVD $-0x2000(RSP), R1 // MOVD $-8192(RSP), R1 // e10b40d1 + MOVD $-0x10000(RSP), RSP // MOVD $-65536(RSP), RSP // ff4340d1 // // CLS diff --git a/src/cmd/internal/obj/arm64/a.out.go b/src/cmd/internal/obj/arm64/a.out.go index 2839da1437..b3c9e9a18e 100644 --- a/src/cmd/internal/obj/arm64/a.out.go +++ b/src/cmd/internal/obj/arm64/a.out.go @@ -410,9 +410,10 @@ const ( C_FCON // floating-point constant C_VCONADDR // 64-bit memory address - C_AACON // ADDCON offset in auto constant $a(FP) - C_LACON // 32-bit offset in auto constant $a(FP) - C_AECON // ADDCON offset in extern constant $e(SB) + C_AACON // ADDCON offset in auto constant $a(FP) + C_AACON2 // 24-bit offset in auto constant $a(FP) + C_LACON // 32-bit offset in auto constant $a(FP) + C_AECON // ADDCON offset in extern constant $e(SB) // TODO(aram): only one branch class should be enough C_SBRA // for TYPE_BRANCH diff --git a/src/cmd/internal/obj/arm64/anames7.go b/src/cmd/internal/obj/arm64/anames7.go index e1703fc4ab..96c9f788d9 100644 --- a/src/cmd/internal/obj/arm64/anames7.go +++ b/src/cmd/internal/obj/arm64/anames7.go @@ -36,6 +36,7 @@ var cnames7 = []string{ "FCON", "VCONADDR", "AACON", + "AACON2", "LACON", "AECON", "SBRA", diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go index df4bbbbd35..fc2033d689 100644 --- a/src/cmd/internal/obj/arm64/asm7.go +++ b/src/cmd/internal/obj/arm64/asm7.go @@ -391,7 +391,11 @@ var optab = []Optab{ {AMOVD, C_VCON, C_NONE, C_NONE, C_REG, 12, 16, 0, NOTUSETMP, 0}, {AMOVK, C_VCON, C_NONE, C_NONE, C_REG, 33, 4, 0, 0, 0}, - {AMOVD, C_AACON, C_NONE, C_NONE, C_REG, 4, 4, REGFROM, 0, 0}, + {AMOVD, C_AACON, C_NONE, C_NONE, C_RSP, 4, 4, REGFROM, 0, 0}, + {AMOVD, C_AACON2, C_NONE, C_NONE, C_RSP, 4, 8, REGFROM, 0, 0}, + + /* load long effective stack address (load int32 offset and add) */ + {AMOVD, C_LACON, C_NONE, C_NONE, C_RSP, 34, 8, REGSP, LFROM, 0}, // Move a large constant to a Vn. {AFMOVQ, C_VCON, C_NONE, C_NONE, C_VREG, 101, 4, 0, LFROM, 0}, @@ -594,9 +598,6 @@ var optab = []Optab{ {AFMOVD, C_LAUTO, C_NONE, C_NONE, C_FREG, 31, 8, REGSP, LFROM, 0}, {AFMOVD, C_LOREG, C_NONE, C_NONE, C_FREG, 31, 8, 0, LFROM, 0}, - /* load long effective stack address (load int32 offset and add) */ - {AMOVD, C_LACON, C_NONE, C_NONE, C_REG, 34, 8, REGSP, LFROM, 0}, - /* pre/post-indexed load (unscaled, signed 9-bit offset) */ {AMOVD, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPOST}, {AMOVW, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPOST}, @@ -1361,6 +1362,10 @@ func isaddcon(v int64) bool { return v <= 0xFFF } +func isaddcon2(v int64) bool { + return 0 <= v && v <= 0xFFFFFF +} + // isbitcon reports whether a constant can be encoded into a logical instruction. // bitcon has a binary form of repetition of a bit sequence of length 2, 4, 8, 16, 32, or 64, // which itself is a rotate (w.r.t. the length of the unit) of a sequence of ones. @@ -1889,10 +1894,14 @@ func (c *ctxt7) aclass(a *obj.Addr) int { default: return C_GOK } - - if isaddcon(c.instoffset) { + cf := c.instoffset + if isaddcon(cf) || isaddcon(-cf) { return C_AACON } + if isaddcon2(cf) { + return C_AACON2 + } + return C_LACON case obj.TYPE_BRANCH: @@ -2046,7 +2055,7 @@ func cmp(a int, b int) bool { return cmp(C_LCON, b) case C_LACON: - if b == C_AACON { + if b == C_AACON || b == C_AACON2 { return true } @@ -3062,11 +3071,10 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { } o1 |= (uint32(r&31) << 5) | uint32(rt&31) - case 4: /* mov $addcon, R; mov $recon, R; mov $racon, R */ - o1 = c.opirr(p, p.As) - + case 4: /* mov $addcon, R; mov $recon, R; mov $racon, R; mov $addcon2, R */ rt := int(p.To.Reg) r := int(o.param) + if r == 0 { r = REGZERO } else if r == REGFROM { @@ -3075,13 +3083,23 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { if r == 0 { r = REGSP } + v := int32(c.regoff(&p.From)) - if (v & 0xFFF000) != 0 { - v >>= 12 - o1 |= 1 << 22 /* shift, by 12 */ + var op int32 + if v < 0 { + v = -v + op = int32(c.opirr(p, ASUB)) + } else { + op = int32(c.opirr(p, AADD)) + } + + if int(o.size) == 8 { + o1 = c.oaddi(p, op, v&0xfff000, r, REGTMP) + o2 = c.oaddi(p, op, v&0x000fff, REGTMP, rt) + break } - o1 |= ((uint32(v) & 0xFFF) << 10) | (uint32(r&31) << 5) | uint32(rt&31) + o1 = c.oaddi(p, op, v, r, rt) case 5: /* b s; bl s */ o1 = c.opbra(p, p.As) -- cgit v1.2.3-54-g00ecf From 37aa65357007411d121fbdbaa5a340aba21ab40a Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Fri, 24 Jul 2020 03:38:38 +1000 Subject: cmd/link: make it easier to debug an elfrelocsect size mismatch Change-Id: I54976b004b4db006509f5e0781b1c2e46cfa09ab Reviewed-on: https://go-review.googlesource.com/c/go/+/244577 Run-TryBot: Than McIntosh TryBot-Result: Go Bot Reviewed-by: Cherry Zhang Trust: Joel Sing --- src/cmd/link/internal/ld/elf.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index 2862f65f9f..f5a2f899fc 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -12,6 +12,7 @@ import ( "crypto/sha1" "encoding/binary" "encoding/hex" + "fmt" "path/filepath" "sort" "strings" @@ -1400,7 +1401,7 @@ func elfrelocsect(ctxt *Link, out *OutBuf, sect *sym.Section, syms []loader.Sym) // sanity check if uint64(out.Offset()) != sect.Reloff+sect.Rellen { - panic("elfrelocsect: size mismatch") + panic(fmt.Sprintf("elfrelocsect: size mismatch %d != %d + %d", out.Offset(), sect.Reloff, sect.Rellen)) } } -- cgit v1.2.3-54-g00ecf From 2ae2a94857cb17a98a86a8332d6f76863982bf59 Mon Sep 17 00:00:00 2001 From: Michael Anthony Knyszek Date: Wed, 16 Sep 2020 16:22:28 +0000 Subject: runtime: fix leak and locking in BenchmarkMSpanCountAlloc CL 249917 made the mspan in MSpanCountAlloc no longer stack-allocated (for good reason), but then allocated an mspan on each call and did not free it, resulting in a leak. That allocation was also not protected by the heap lock, which could lead to data corruption of mheap fields and the spanalloc. To fix this, export some functions to allocate/free dummy mspans from spanalloc (with proper locking) and allocate just one up-front for the benchmark, freeing it at the end. Then, update MSpanCountAlloc to accept a dummy mspan. Note that we need to allocate the dummy mspan up-front otherwise we measure things like heap locking and fixalloc performance instead of what we actually want to measure: how fast we can do a popcount on the mark bits. Fixes #41391. Change-Id: If6629a6ec1ece639c7fb78532045837a8c872c04 Reviewed-on: https://go-review.googlesource.com/c/go/+/255297 Run-TryBot: Michael Knyszek Reviewed-by: Keith Randall TryBot-Result: Go Bot Trust: Michael Knyszek --- src/runtime/export_test.go | 28 +++++++++++++++++++++++++--- src/runtime/gc_test.go | 6 +++++- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index 929bb35db6..e3d6441c18 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -983,9 +983,31 @@ func MapHashCheck(m interface{}, k interface{}) (uintptr, uintptr) { return x, y } -func MSpanCountAlloc(bits []byte) int { - s := (*mspan)(mheap_.spanalloc.alloc()) +// mspan wrapper for testing. +//go:notinheap +type MSpan mspan + +// Allocate an mspan for testing. +func AllocMSpan() *MSpan { + var s *mspan + systemstack(func() { + s = (*mspan)(mheap_.spanalloc.alloc()) + }) + return (*MSpan)(s) +} + +// Free an allocated mspan. +func FreeMSpan(s *MSpan) { + systemstack(func() { + mheap_.spanalloc.free(unsafe.Pointer(s)) + }) +} + +func MSpanCountAlloc(ms *MSpan, bits []byte) int { + s := (*mspan)(ms) s.nelems = uintptr(len(bits) * 8) s.gcmarkBits = (*gcBits)(unsafe.Pointer(&bits[0])) - return s.countAlloc() + result := s.countAlloc() + s.gcmarkBits = nil + return result } diff --git a/src/runtime/gc_test.go b/src/runtime/gc_test.go index c5c8a4cecf..9edebdada6 100644 --- a/src/runtime/gc_test.go +++ b/src/runtime/gc_test.go @@ -763,6 +763,10 @@ func BenchmarkScanStackNoLocals(b *testing.B) { } func BenchmarkMSpanCountAlloc(b *testing.B) { + // Allocate one dummy mspan for the whole benchmark. + s := runtime.AllocMSpan() + defer runtime.FreeMSpan(s) + // n is the number of bytes to benchmark against. // n must always be a multiple of 8, since gcBits is // always rounded up 8 bytes. @@ -774,7 +778,7 @@ func BenchmarkMSpanCountAlloc(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - runtime.MSpanCountAlloc(bits) + runtime.MSpanCountAlloc(s, bits) } }) } -- cgit v1.2.3-54-g00ecf From 16328513bfb12d96e8f33fc37f816e1441027135 Mon Sep 17 00:00:00 2001 From: Carl Johnson Date: Thu, 27 Aug 2020 13:08:44 +0000 Subject: flag: add Func Fixes #39557 Change-Id: Ida578f7484335e8c6bf927255f75377eda63b563 GitHub-Last-Rev: b97294f7669c24011e5b093179d65636512a84cd GitHub-Pull-Request: golang/go#39880 Reviewed-on: https://go-review.googlesource.com/c/go/+/240014 Reviewed-by: Russ Cox Trust: Ian Lance Taylor --- src/flag/example_func_test.go | 41 +++++++++++++++++++++++++++++++++++ src/flag/flag.go | 22 ++++++++++++++++++- src/flag/flag_test.go | 50 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 src/flag/example_func_test.go diff --git a/src/flag/example_func_test.go b/src/flag/example_func_test.go new file mode 100644 index 0000000000..7c30c5e713 --- /dev/null +++ b/src/flag/example_func_test.go @@ -0,0 +1,41 @@ +// 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 flag_test + +import ( + "errors" + "flag" + "fmt" + "net" + "os" +) + +func ExampleFunc() { + fs := flag.NewFlagSet("ExampleFunc", flag.ContinueOnError) + fs.SetOutput(os.Stdout) + var ip net.IP + fs.Func("ip", "`IP address` to parse", func(s string) error { + ip = net.ParseIP(s) + if ip == nil { + return errors.New("could not parse IP") + } + return nil + }) + fs.Parse([]string{"-ip", "127.0.0.1"}) + fmt.Printf("{ip: %v, loopback: %t}\n\n", ip, ip.IsLoopback()) + + // 256 is not a valid IPv4 component + fs.Parse([]string{"-ip", "256.0.0.1"}) + fmt.Printf("{ip: %v, loopback: %t}\n\n", ip, ip.IsLoopback()) + + // Output: + // {ip: 127.0.0.1, loopback: true} + // + // invalid value "256.0.0.1" for flag -ip: could not parse IP + // Usage of ExampleFunc: + // -ip IP address + // IP address to parse + // {ip: , loopback: false} +} diff --git a/src/flag/flag.go b/src/flag/flag.go index 286bba6873..a8485f034f 100644 --- a/src/flag/flag.go +++ b/src/flag/flag.go @@ -278,6 +278,12 @@ func (d *durationValue) Get() interface{} { return time.Duration(*d) } func (d *durationValue) String() string { return (*time.Duration)(d).String() } +type funcValue func(string) error + +func (f funcValue) Set(s string) error { return f(s) } + +func (f funcValue) String() string { return "" } + // Value is the interface to the dynamic value stored in a flag. // (The default value is represented as a string.) // @@ -296,7 +302,7 @@ type Value interface { // Getter is an interface that allows the contents of a Value to be retrieved. // It wraps the Value interface, rather than being part of it, because it // appeared after Go 1 and its compatibility rules. All Value types provided -// by this package satisfy the Getter interface. +// by this package satisfy the Getter interface, except the type used by Func. type Getter interface { Value Get() interface{} @@ -830,6 +836,20 @@ func Duration(name string, value time.Duration, usage string) *time.Duration { return CommandLine.Duration(name, value, usage) } +// Func defines a flag with the specified name and usage string. +// Each time the flag is seen, fn is called with the value of the flag. +// If fn returns a non-nil error, it will be treated as a flag value parsing error. +func (f *FlagSet) Func(name, usage string, fn func(string) error) { + f.Var(funcValue(fn), name, usage) +} + +// Func defines a flag with the specified name and usage string. +// Each time the flag is seen, fn is called with the value of the flag. +// If fn returns a non-nil error, it will be treated as a flag value parsing error. +func Func(name, usage string, fn func(string) error) { + CommandLine.Func(name, usage, fn) +} + // Var defines a flag with the specified name and usage string. The type and // value of the flag are represented by the first argument, of type Value, which // typically holds a user-defined implementation of Value. For instance, the diff --git a/src/flag/flag_test.go b/src/flag/flag_test.go index a01a5e4cea..2793064511 100644 --- a/src/flag/flag_test.go +++ b/src/flag/flag_test.go @@ -38,6 +38,7 @@ func TestEverything(t *testing.T) { String("test_string", "0", "string value") Float64("test_float64", 0, "float64 value") Duration("test_duration", 0, "time.Duration value") + Func("test_func", "func value", func(string) error { return nil }) m := make(map[string]*Flag) desired := "0" @@ -52,6 +53,8 @@ func TestEverything(t *testing.T) { ok = true case f.Name == "test_duration" && f.Value.String() == desired+"s": ok = true + case f.Name == "test_func" && f.Value.String() == "": + ok = true } if !ok { t.Error("Visit: bad value", f.Value.String(), "for", f.Name) @@ -59,7 +62,7 @@ func TestEverything(t *testing.T) { } } VisitAll(visitor) - if len(m) != 8 { + if len(m) != 9 { t.Error("VisitAll misses some flags") for k, v := range m { t.Log(k, *v) @@ -82,9 +85,10 @@ func TestEverything(t *testing.T) { Set("test_string", "1") Set("test_float64", "1") Set("test_duration", "1s") + Set("test_func", "1") desired = "1" Visit(visitor) - if len(m) != 8 { + if len(m) != 9 { t.Error("Visit fails after set") for k, v := range m { t.Log(k, *v) @@ -257,6 +261,48 @@ func TestUserDefined(t *testing.T) { } } +func TestUserDefinedFunc(t *testing.T) { + var flags FlagSet + flags.Init("test", ContinueOnError) + var ss []string + flags.Func("v", "usage", func(s string) error { + ss = append(ss, s) + return nil + }) + if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil { + t.Error(err) + } + if len(ss) != 3 { + t.Fatal("expected 3 args; got ", len(ss)) + } + expect := "[1 2 3]" + if got := fmt.Sprint(ss); got != expect { + t.Errorf("expected value %q got %q", expect, got) + } + // test usage + var buf strings.Builder + flags.SetOutput(&buf) + flags.Parse([]string{"-h"}) + if usage := buf.String(); !strings.Contains(usage, "usage") { + t.Errorf("usage string not included: %q", usage) + } + // test Func error + flags = *NewFlagSet("test", ContinueOnError) + flags.Func("v", "usage", func(s string) error { + return fmt.Errorf("test error") + }) + // flag not set, so no error + if err := flags.Parse(nil); err != nil { + t.Error(err) + } + // flag set, expect error + if err := flags.Parse([]string{"-v", "1"}); err == nil { + t.Error("expected error; got none") + } else if errMsg := err.Error(); !strings.Contains(errMsg, "test error") { + t.Errorf(`error should contain "test error"; got %q`, errMsg) + } +} + func TestUserDefinedForCommandLine(t *testing.T) { const help = "HELP" var result string -- cgit v1.2.3-54-g00ecf From 4f915911e84819b69329a224d5b646983ac9fed7 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Wed, 26 Aug 2020 14:07:35 -0700 Subject: cmd/compile: allow aliases to go:notinheap types The alias doesn't need to be marked go:notinheap. It gets its notinheap-ness from the target type. Without this change, the type alias test in the notinheap.go file generates these two errors: notinheap.go:62: misplaced compiler directive notinheap.go:63: type nih must be go:notinheap The first is a result of go:notinheap pragmas not applying to type alias declarations. The second is the result of then trying to match the notinheap-ness of the alias and the target type. Add a few more go:notinheap tests while we are here. Update #40954 Change-Id: I067ec47698df6e9e593e080d67796fd05a1d480f Reviewed-on: https://go-review.googlesource.com/c/go/+/250939 Run-TryBot: Keith Randall TryBot-Result: Go Bot Reviewed-by: Emmanuel Odeke Trust: Keith Randall --- src/cmd/compile/internal/gc/typecheck.go | 2 +- test/notinheap.go | 8 ++++++++ test/notinheap2.go | 10 +++++++--- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index fb169cfec8..9bb3c69cd0 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -2068,7 +2068,7 @@ func typecheck1(n *Node, top int) (res *Node) { ok |= ctxStmt n.Left = typecheck(n.Left, ctxType) checkwidth(n.Left.Type) - if n.Left.Type != nil && n.Left.Type.NotInHeap() && n.Left.Name.Param.Pragma&NotInHeap == 0 { + if n.Left.Type != nil && n.Left.Type.NotInHeap() && !n.Left.Name.Param.Alias && n.Left.Name.Param.Pragma&NotInHeap == 0 { // The type contains go:notinheap types, so it // must be marked as such (alternatively, we // could silently propagate go:notinheap). diff --git a/test/notinheap.go b/test/notinheap.go index 16c3f8faf0..a2284a5068 100644 --- a/test/notinheap.go +++ b/test/notinheap.go @@ -52,6 +52,14 @@ type t3 byte //go:notinheap type t4 rune +// Type aliases inherit the go:notinheap-ness of the type they alias. +type nihAlias = nih + +type embedAlias1 struct { // ERROR "must be go:notinheap" + x nihAlias +} +type embedAlias2 [1]nihAlias // ERROR "must be go:notinheap" + var sink interface{} func i() { diff --git a/test/notinheap2.go b/test/notinheap2.go index de1e6db1d3..09d0fc0b7b 100644 --- a/test/notinheap2.go +++ b/test/notinheap2.go @@ -27,14 +27,18 @@ func f() { // Heap allocation is not okay. var y *nih +var y2 *struct{ x nih } +var y3 *[1]nih var z []nih var w []nih var n int func g() { - y = new(nih) // ERROR "heap allocation disallowed" - z = make([]nih, 1) // ERROR "heap allocation disallowed" - z = append(z, x) // ERROR "heap allocation disallowed" + y = new(nih) // ERROR "heap allocation disallowed" + y2 = new(struct{ x nih }) // ERROR "heap allocation disallowed" + y3 = new([1]nih) // ERROR "heap allocation disallowed" + z = make([]nih, 1) // ERROR "heap allocation disallowed" + z = append(z, x) // ERROR "heap allocation disallowed" // Test for special case of OMAKESLICECOPY x := make([]nih, n) // ERROR "heap allocation disallowed" copy(x, z) -- cgit v1.2.3-54-g00ecf From 42b023d7b9cb8229e3035fa3d36bce41a1ef0c43 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Wed, 26 Aug 2020 14:17:35 -0700 Subject: cmd/cgo: use go:notinheap for anonymous structs They can't reasonably be allocated on the heap. Not a huge deal, but it has an interesting and useful side effect. After CL 249917, the compiler and runtime treat pointers to go:notinheap types as uintptrs instead of real pointers (no write barrier, not processed during stack scanning, ...). That feature is exactly what we want for cgo to fix #40954. All the cases we have of pointers declared in C, but which might actually be filled with non-pointer data, are of this form (JNI's jobject heirarch, Darwin's CFType heirarchy, ...). Fixes #40954 Change-Id: I44a3b9bc2513d4287107e39d0cbbd0efd46a3aae Reviewed-on: https://go-review.googlesource.com/c/go/+/250940 Run-TryBot: Emmanuel Odeke TryBot-Result: Go Bot Trust: Keith Randall Reviewed-by: Ian Lance Taylor --- src/cmd/cgo/gcc.go | 15 +++++++++++++++ src/cmd/cgo/main.go | 3 ++- src/cmd/cgo/out.go | 3 +++ test/fixedbugs/issue40954.go | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 test/fixedbugs/issue40954.go diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index eb6c1a5c89..730db44990 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -2448,6 +2448,18 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ tt := *t tt.C = &TypeRepr{"%s %s", []interface{}{dt.Kind, tag}} tt.Go = c.Ident("struct{}") + if dt.Kind == "struct" { + // We don't know what the representation of this struct is, so don't let + // anyone allocate one on the Go side. As a side effect of this annotation, + // pointers to this type will not be considered pointers in Go. They won't + // get writebarrier-ed or adjusted during a stack copy. This should handle + // all the cases badPointerTypedef used to handle, but hopefully will + // continue to work going forward without any more need for cgo changes. + tt.NotInHeap = true + // TODO: we should probably do the same for unions. Unions can't live + // on the Go heap, right? It currently doesn't work for unions because + // they are defined as a type alias for struct{}, not a defined type. + } typedef[name.Name] = &tt break } @@ -2518,6 +2530,7 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ } t.Go = name t.BadPointer = sub.BadPointer + t.NotInHeap = sub.NotInHeap if unionWithPointer[sub.Go] { unionWithPointer[t.Go] = true } @@ -2528,6 +2541,7 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ tt := *t tt.Go = sub.Go tt.BadPointer = sub.BadPointer + tt.NotInHeap = sub.NotInHeap typedef[name.Name] = &tt } @@ -3026,6 +3040,7 @@ func (c *typeConv) anonymousStructTypedef(dt *dwarf.TypedefType) bool { // non-pointers in this type. // TODO: Currently our best solution is to find these manually and list them as // they come up. A better solution is desired. +// Note: DEPRECATED. There is now a better solution. Search for NotInHeap in this file. func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool { if c.badCFType(dt) { return true diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index 5a7bb3f87b..ef3ed968e4 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -151,7 +151,8 @@ type Type struct { Go ast.Expr EnumValues map[string]int64 Typedef string - BadPointer bool + BadPointer bool // this pointer type should be represented as a uintptr (deprecated) + NotInHeap bool // this type should have a go:notinheap annotation } // A FuncType collects information about a function type in both the C and Go worlds. diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 50d2811f1b..03b8333b10 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -108,6 +108,9 @@ func (p *Package) writeDefs() { sort.Strings(typedefNames) for _, name := range typedefNames { def := typedef[name] + if def.NotInHeap { + fmt.Fprintf(fgo2, "//go:notinheap\n") + } fmt.Fprintf(fgo2, "type %s ", name) // We don't have source info for these types, so write them out without source info. // Otherwise types would look like: diff --git a/test/fixedbugs/issue40954.go b/test/fixedbugs/issue40954.go new file mode 100644 index 0000000000..53e9ccf387 --- /dev/null +++ b/test/fixedbugs/issue40954.go @@ -0,0 +1,35 @@ +// run + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "unsafe" +) + +//go:notinheap +type S struct{ x int } + +func main() { + var i int + p := (*S)(unsafe.Pointer(uintptr(unsafe.Pointer(&i)))) + v := uintptr(unsafe.Pointer(p)) + // p is a pointer to a go:notinheap type. Like some C libraries, + // we stored an integer in that pointer. That integer just happens + // to be the address of i. + // v is also the address of i. + // p has a base type which is marked go:notinheap, so it + // should not be adjusted when the stack is copied. + recurse(100, p, v) +} +func recurse(n int, p *S, v uintptr) { + if n > 0 { + recurse(n-1, p, v) + } + if uintptr(unsafe.Pointer(p)) != v { + panic("adjusted notinheap pointer") + } +} -- cgit v1.2.3-54-g00ecf From 37f261010f837f945eaa2d33d90cd822b4e93459 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 27 Aug 2020 14:05:52 -0700 Subject: cmd/compile: make go:notinheap error message friendlier for cgo Update #40954 Change-Id: Ifaab7349631ccb12fc892882bbdf7f0ebf3d845f Reviewed-on: https://go-review.googlesource.com/c/go/+/251158 Run-TryBot: Keith Randall Reviewed-by: Ian Lance Taylor TryBot-Result: Go Bot Trust: Keith Randall --- src/cmd/compile/internal/gc/escape.go | 2 +- src/cmd/compile/internal/gc/subr.go | 4 ++-- src/cmd/compile/internal/gc/typecheck.go | 6 +++--- src/cmd/compile/internal/gc/walk.go | 8 ++++---- test/notinheap.go | 14 +++++++------- test/notinheap2.go | 14 +++++++------- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/cmd/compile/internal/gc/escape.go b/src/cmd/compile/internal/gc/escape.go index 75da439bb7..f435d8ff6a 100644 --- a/src/cmd/compile/internal/gc/escape.go +++ b/src/cmd/compile/internal/gc/escape.go @@ -1030,7 +1030,7 @@ func (e *Escape) newLoc(n *Node, transient bool) *EscLocation { Fatalf("e.curfn isn't set") } if n != nil && n.Type != nil && n.Type.NotInHeap() { - yyerrorl(n.Pos, "%v is go:notinheap; stack allocation disallowed", n.Type) + yyerrorl(n.Pos, "%v is incomplete (or unallocatable); stack allocation disallowed", n.Type) } n = canonicalNode(n) diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index 8883e75c49..b5527e2f83 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -689,14 +689,14 @@ func convertop(srcConstant bool, src, dst *types.Type, why *string) Op { // (a) Disallow (*T) to (*U) where T is go:notinheap but U isn't. if src.IsPtr() && dst.IsPtr() && dst.Elem().NotInHeap() && !src.Elem().NotInHeap() { if why != nil { - *why = fmt.Sprintf(":\n\t%v is go:notinheap, but %v is not", dst.Elem(), src.Elem()) + *why = fmt.Sprintf(":\n\t%v is incomplete (or unallocatable), but %v is not", dst.Elem(), src.Elem()) } return OXXX } // (b) Disallow string to []T where T is go:notinheap. if src.IsString() && dst.IsSlice() && dst.Elem().NotInHeap() && (dst.Elem().Etype == types.Bytetype.Etype || dst.Elem().Etype == types.Runetype.Etype) { if why != nil { - *why = fmt.Sprintf(":\n\t%v is go:notinheap", dst.Elem()) + *why = fmt.Sprintf(":\n\t%v is incomplete (or unallocatable)", dst.Elem()) } return OXXX } diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 9bb3c69cd0..8d777c399e 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -471,10 +471,10 @@ func typecheck1(n *Node, top int) (res *Node) { return n } if l.Type.NotInHeap() { - yyerror("go:notinheap map key not allowed") + yyerror("incomplete (or unallocatable) map key not allowed") } if r.Type.NotInHeap() { - yyerror("go:notinheap map value not allowed") + yyerror("incomplete (or unallocatable) map value not allowed") } setTypeNode(n, types.NewMap(l.Type, r.Type)) @@ -491,7 +491,7 @@ func typecheck1(n *Node, top int) (res *Node) { return n } if l.Type.NotInHeap() { - yyerror("chan of go:notinheap type not allowed") + yyerror("chan of incomplete (or unallocatable) type not allowed") } setTypeNode(n, types.NewChan(l.Type, n.TChanDir())) diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index c3a740d4cc..2db352c8d5 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -648,7 +648,7 @@ opswitch: // x = append(...) r := n.Right if r.Type.Elem().NotInHeap() { - yyerror("%v is go:notinheap; heap allocation disallowed", r.Type.Elem()) + yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", r.Type.Elem()) } switch { case isAppendOfMake(r): @@ -1164,7 +1164,7 @@ opswitch: case ONEW: if n.Type.Elem().NotInHeap() { - yyerror("%v is go:notinheap; heap allocation disallowed", n.Type.Elem()) + yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", n.Type.Elem()) } if n.Esc == EscNone { if n.Type.Elem().Width >= maxImplicitStackVarSize { @@ -1335,7 +1335,7 @@ opswitch: } t := n.Type if t.Elem().NotInHeap() { - yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem()) + yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem()) } if n.Esc == EscNone { if !isSmallMakeSlice(n) { @@ -1412,7 +1412,7 @@ opswitch: t := n.Type if t.Elem().NotInHeap() { - yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem()) + yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem()) } length := conv(n.Left, types.Types[TINT]) diff --git a/test/notinheap.go b/test/notinheap.go index a2284a5068..5dd4997a65 100644 --- a/test/notinheap.go +++ b/test/notinheap.go @@ -23,11 +23,11 @@ type embed3 struct { // ERROR "must be go:notinheap" x [1]nih } -type embed4 map[nih]int // ERROR "go:notinheap map key not allowed" +type embed4 map[nih]int // ERROR "incomplete \(or unallocatable\) map key not allowed" -type embed5 map[int]nih // ERROR "go:notinheap map value not allowed" +type embed5 map[int]nih // ERROR "incomplete \(or unallocatable\) map value not allowed" -type emebd6 chan nih // ERROR "chan of go:notinheap type not allowed" +type emebd6 chan nih // ERROR "chan of incomplete \(or unallocatable\) type not allowed" type okay1 *nih @@ -64,8 +64,8 @@ var sink interface{} func i() { sink = new(t1) // no error - sink = (*t2)(new(t1)) // ERROR "cannot convert(.|\n)*t2 is go:notinheap" - sink = (*t2)(new(struct{ x int })) // ERROR "cannot convert(.|\n)*t2 is go:notinheap" - sink = []t3("foo") // ERROR "cannot convert(.|\n)*t3 is go:notinheap" - sink = []t4("bar") // ERROR "cannot convert(.|\n)*t4 is go:notinheap" + sink = (*t2)(new(t1)) // ERROR "cannot convert(.|\n)*t2 is incomplete \(or unallocatable\)" + sink = (*t2)(new(struct{ x int })) // ERROR "cannot convert(.|\n)*t2 is incomplete \(or unallocatable\)" + sink = []t3("foo") // ERROR "cannot convert(.|\n)*t3 is incomplete \(or unallocatable\)" + sink = []t4("bar") // ERROR "cannot convert(.|\n)*t4 is incomplete \(or unallocatable\)" } diff --git a/test/notinheap2.go b/test/notinheap2.go index 09d0fc0b7b..23d4b0ae77 100644 --- a/test/notinheap2.go +++ b/test/notinheap2.go @@ -20,7 +20,7 @@ var x nih // Stack variables are not okay. func f() { - var y nih // ERROR "nih is go:notinheap; stack allocation disallowed" + var y nih // ERROR "nih is incomplete \(or unallocatable\); stack allocation disallowed" x = y } @@ -34,13 +34,13 @@ var w []nih var n int func g() { - y = new(nih) // ERROR "heap allocation disallowed" - y2 = new(struct{ x nih }) // ERROR "heap allocation disallowed" - y3 = new([1]nih) // ERROR "heap allocation disallowed" - z = make([]nih, 1) // ERROR "heap allocation disallowed" - z = append(z, x) // ERROR "heap allocation disallowed" + y = new(nih) // ERROR "can't be allocated in Go" + y2 = new(struct{ x nih }) // ERROR "can't be allocated in Go" + y3 = new([1]nih) // ERROR "can't be allocated in Go" + z = make([]nih, 1) // ERROR "can't be allocated in Go" + z = append(z, x) // ERROR "can't be allocated in Go" // Test for special case of OMAKESLICECOPY - x := make([]nih, n) // ERROR "heap allocation disallowed" + x := make([]nih, n) // ERROR "can't be allocated in Go" copy(x, z) z = x } -- cgit v1.2.3-54-g00ecf From 10dfb1dd3d1d26122cf18f29468ec17eb7222c3f Mon Sep 17 00:00:00 2001 From: Michael Anthony Knyszek Date: Wed, 16 Sep 2020 17:08:55 +0000 Subject: runtime: actually fix locking in BenchmarkMSpanCountAlloc I just submitted CL 255297 which mostly fixed this problem, but totally forgot to actually acquire/release the heap lock. Oops. Updates #41391. Change-Id: I45b42f20a9fc765c4de52476db3654d4bfe9feb3 Reviewed-on: https://go-review.googlesource.com/c/go/+/255298 Trust: Michael Knyszek Run-TryBot: Michael Knyszek Reviewed-by: Keith Randall TryBot-Result: Go Bot --- src/runtime/export_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index e3d6441c18..f2fa11dc98 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -991,7 +991,9 @@ type MSpan mspan func AllocMSpan() *MSpan { var s *mspan systemstack(func() { + lock(&mheap_.lock) s = (*mspan)(mheap_.spanalloc.alloc()) + unlock(&mheap_.lock) }) return (*MSpan)(s) } @@ -999,7 +1001,9 @@ func AllocMSpan() *MSpan { // Free an allocated mspan. func FreeMSpan(s *MSpan) { systemstack(func() { + lock(&mheap_.lock) mheap_.spanalloc.free(unsafe.Pointer(s)) + unlock(&mheap_.lock) }) } -- cgit v1.2.3-54-g00ecf From 7ee35cb301eddf4d53e7bb2d5bf0873922d63a6e Mon Sep 17 00:00:00 2001 From: Alberto Donizetti Date: Wed, 16 Sep 2020 13:13:50 +0200 Subject: cmd/compile: be more specific in cannot assign errors "cannot assign to" compiler errors are very laconic: they never explain why the lhs cannot be assigned to (with one exception, when assigning to a struct field in a map). This change makes them a little more specific, in two more cases: when assigning to a string, or to a const; by giving a very brief reason why the lhs cannot be assigned to. Change-Id: I244cca7fc3c3814e00e0ccadeec62f747c293979 Reviewed-on: https://go-review.googlesource.com/c/go/+/255199 Trust: Alberto Donizetti Run-TryBot: Alberto Donizetti TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/typecheck.go | 9 +++++++-- test/cannotassign.go | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 test/cannotassign.go diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 8d777c399e..55773641ed 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -3135,9 +3135,14 @@ func checkassign(stmt *Node, n *Node) { return } - if n.Op == ODOT && n.Left.Op == OINDEXMAP { + switch { + case n.Op == ODOT && n.Left.Op == OINDEXMAP: yyerror("cannot assign to struct field %v in map", n) - } else { + case (n.Op == OINDEX && n.Left.Type.IsString()) || n.Op == OSLICESTR: + yyerror("cannot assign to %v (strings are immutable)", n) + case n.Op == OLITERAL && n.Sym != nil && n.isGoConst(): + yyerror("cannot assign to %v (declared const)", n) + default: yyerror("cannot assign to %v", n) } n.Type = nil diff --git a/test/cannotassign.go b/test/cannotassign.go new file mode 100644 index 0000000000..0de04ecad0 --- /dev/null +++ b/test/cannotassign.go @@ -0,0 +1,33 @@ +// errorcheck + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test "cannot assign" errors + +package main + +func main() { + var s string = "hello" + s[1:2] = "a" // ERROR "cannot assign to .* \(strings are immutable\)" + s[3] = "b" // ERROR "cannot assign to .* \(strings are immutable\)" + + const n int = 1 + const cs string = "hello" + n = 2 // ERROR "cannot assign to .* \(declared const\)" + cs = "hi" // ERROR "cannot assign to .* \(declared const\)" + true = false // ERROR "cannot assign to .* \(declared const\)" + + var m map[int]struct{ n int } + m[0].n = 7 // ERROR "cannot assign to struct field .* in map$" + + 1 = 7 // ERROR "cannot assign to 1$" + "hi" = 7 // ERROR `cannot assign to "hi"$` + nil = 7 // ERROR "cannot assign to nil$" + len("") = 7 // ERROR `cannot assign to len\(""\)$` + []int{} = nil // ERROR "cannot assign to \[\]int\{\}$" + + var x int = 7 + x + 1 = 7 // ERROR "cannot assign to x \+ 1$" +} -- cgit v1.2.3-54-g00ecf From b4ef49e527787ec932d0b371bb24c3fc370b1e8d Mon Sep 17 00:00:00 2001 From: David Chase Date: Fri, 12 Jun 2020 13:48:26 -0400 Subject: cmd/compile: introduce special ssa Aux type for calls This is prerequisite to moving call expansion later into SSA, and probably a good idea anyway. Passes tests. This is the first minimal CL that does a 1-for-1 substitution of *ssa.AuxCall for *obj.LSym. Next step (next CL) is to make this change for all calls so that additional information can be stored in AuxCall. Change-Id: Ia3a7715648fd9fb1a176850767a726e6f5b959eb Reviewed-on: https://go-review.googlesource.com/c/go/+/237680 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/plive.go | 6 +-- src/cmd/compile/internal/gc/ssa.go | 40 ++++++++++------- src/cmd/compile/internal/ssa/check.go | 12 +++++ src/cmd/compile/internal/ssa/func_test.go | 7 +++ src/cmd/compile/internal/ssa/fuse_test.go | 4 +- src/cmd/compile/internal/ssa/gen/386Ops.go | 2 +- src/cmd/compile/internal/ssa/gen/AMD64Ops.go | 2 +- src/cmd/compile/internal/ssa/gen/ARM64Ops.go | 2 +- src/cmd/compile/internal/ssa/gen/ARMOps.go | 2 +- src/cmd/compile/internal/ssa/gen/MIPS64Ops.go | 2 +- src/cmd/compile/internal/ssa/gen/MIPSOps.go | 2 +- src/cmd/compile/internal/ssa/gen/PPC64Ops.go | 2 +- src/cmd/compile/internal/ssa/gen/RISCV64Ops.go | 6 +-- src/cmd/compile/internal/ssa/gen/S390XOps.go | 2 +- src/cmd/compile/internal/ssa/gen/WasmOps.go | 2 +- src/cmd/compile/internal/ssa/gen/generic.rules | 12 ++--- src/cmd/compile/internal/ssa/gen/genericOps.go | 6 +-- src/cmd/compile/internal/ssa/gen/rulegen.go | 10 ++++- src/cmd/compile/internal/ssa/loopreschedchecks.go | 2 +- src/cmd/compile/internal/ssa/op.go | 13 ++++++ src/cmd/compile/internal/ssa/opGen.go | 53 +++++++++-------------- src/cmd/compile/internal/ssa/regalloc_test.go | 12 ++--- src/cmd/compile/internal/ssa/rewrite.go | 33 +++++++------- src/cmd/compile/internal/ssa/rewritegeneric.go | 40 ++++++++--------- src/cmd/compile/internal/ssa/value.go | 4 +- src/cmd/compile/internal/ssa/writebarrier.go | 4 +- src/cmd/compile/internal/wasm/ssa.go | 5 ++- 27 files changed, 164 insertions(+), 123 deletions(-) diff --git a/src/cmd/compile/internal/gc/plive.go b/src/cmd/compile/internal/gc/plive.go index 8976ed657a..a9ea37701e 100644 --- a/src/cmd/compile/internal/gc/plive.go +++ b/src/cmd/compile/internal/gc/plive.go @@ -861,7 +861,7 @@ func (lv *Liveness) hasStackMap(v *ssa.Value) bool { // typedmemclr and typedmemmove are write barriers and // deeply non-preemptible. They are unsafe points and // hence should not have liveness maps. - if sym, _ := v.Aux.(*obj.LSym); sym == typedmemclr || sym == typedmemmove { + if sym, ok := v.Aux.(*ssa.AuxCall); ok && (sym.Fn == typedmemclr || sym.Fn == typedmemmove) { return false } return true @@ -1231,8 +1231,8 @@ func (lv *Liveness) showlive(v *ssa.Value, live bvec) { s := "live at " if v == nil { s += fmt.Sprintf("entry to %s:", lv.fn.funcname()) - } else if sym, ok := v.Aux.(*obj.LSym); ok { - fn := sym.Name + } else if sym, ok := v.Aux.(*ssa.AuxCall); ok && sym.Fn != nil { + fn := sym.Fn.Name if pos := strings.Index(fn, "."); pos >= 0 { fn = fn[pos+1:] } diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 3bdb5b0b9f..0ee31bd9a3 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -805,6 +805,11 @@ func (s *state) newValue2(op ssa.Op, t *types.Type, arg0, arg1 *ssa.Value) *ssa. return s.curBlock.NewValue2(s.peekPos(), op, t, arg0, arg1) } +// newValue2A adds a new value with two arguments and an aux value to the current block. +func (s *state) newValue2A(op ssa.Op, t *types.Type, aux interface{}, arg0, arg1 *ssa.Value) *ssa.Value { + return s.curBlock.NewValue2A(s.peekPos(), op, t, aux, arg0, arg1) +} + // newValue2Apos adds a new value with two arguments and an aux value to the current block. // isStmt determines whether the created values may be a statement or not // (i.e., false means never, yes means maybe). @@ -4297,10 +4302,10 @@ func (s *state) openDeferExit() { v := s.load(r.closure.Type.Elem(), r.closure) s.maybeNilCheckClosure(v, callDefer) codeptr := s.rawLoad(types.Types[TUINTPTR], v) - call = s.newValue3(ssa.OpClosureCall, types.TypeMem, codeptr, v, s.mem()) + call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, nil, codeptr, v, s.mem()) } else { // Do a static call if the original call was a static function or method - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, fn.Sym.Linksym(), s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: fn.Sym.Linksym()}, s.mem()) } call.AuxInt = stksize s.vars[&memVar] = call @@ -4432,7 +4437,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // Call runtime.deferprocStack with pointer to _defer record. arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize()) s.store(types.Types[TUINTPTR], arg0, addr) - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, deferprocStack, s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: deferprocStack}, s.mem()) if stksize < int64(Widthptr) { // We need room for both the call to deferprocStack and the call to // the deferred function. @@ -4477,9 +4482,9 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // call target switch { case k == callDefer: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, deferproc, s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: deferproc}, s.mem()) case k == callGo: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, newproc, s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: newproc}, s.mem()) case closure != nil: // rawLoad because loading the code pointer from a // closure is always safe, but IsSanitizerSafeAddr @@ -4487,11 +4492,11 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // critical that we not clobber any arguments already // stored onto the stack. codeptr = s.rawLoad(types.Types[TUINTPTR], closure) - call = s.newValue3(ssa.OpClosureCall, types.TypeMem, codeptr, closure, s.mem()) + call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, nil, codeptr, closure, s.mem()) case codeptr != nil: - call = s.newValue2(ssa.OpInterCall, types.TypeMem, codeptr, s.mem()) + call = s.newValue2A(ssa.OpInterCall, types.TypeMem, nil, codeptr, s.mem()) case sym != nil: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, sym.Linksym(), s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: sym.Linksym()}, s.mem()) default: s.Fatalf("bad call type %v %v", n.Op, n) } @@ -4924,7 +4929,7 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args . off = Rnd(off, int64(Widthreg)) // Issue call - call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, fn, s.mem()) + call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: fn}, s.mem()) s.vars[&memVar] = call if !returns { @@ -6355,6 +6360,9 @@ func AddAux2(a *obj.Addr, v *ssa.Value, offset int64) { } // Add symbol's offset from its base register. switch n := v.Aux.(type) { + case *ssa.AuxCall: + a.Name = obj.NAME_EXTERN + a.Sym = n.Fn case *obj.LSym: a.Name = obj.NAME_EXTERN a.Sym = n @@ -6541,10 +6549,10 @@ func (s *SSAGenState) Call(v *ssa.Value) *obj.Prog { } else { p.Pos = v.Pos.WithNotStmt() } - if sym, ok := v.Aux.(*obj.LSym); ok { + if sym, ok := v.Aux.(*ssa.AuxCall); ok && sym.Fn != nil { p.To.Type = obj.TYPE_MEM p.To.Name = obj.NAME_EXTERN - p.To.Sym = sym + p.To.Sym = sym.Fn } else { // TODO(mdempsky): Can these differences be eliminated? switch thearch.LinkArch.Family { @@ -6567,12 +6575,14 @@ func (s *SSAGenState) PrepareCall(v *ssa.Value) { idx := s.livenessMap.Get(v) if !idx.StackMapValid() { // See Liveness.hasStackMap. - if sym, _ := v.Aux.(*obj.LSym); !(sym == typedmemclr || sym == typedmemmove) { + if sym, ok := v.Aux.(*ssa.AuxCall); !ok || !(sym.Fn == typedmemclr || sym.Fn == typedmemmove) { Fatalf("missing stack map index for %v", v.LongString()) } } - if sym, _ := v.Aux.(*obj.LSym); sym == Deferreturn { + call, ok := v.Aux.(*ssa.AuxCall) + + if ok && call.Fn == Deferreturn { // Deferred calls will appear to be returning to // the CALL deferreturn(SB) that we are about to emit. // However, the stack trace code will show the line @@ -6584,11 +6594,11 @@ func (s *SSAGenState) PrepareCall(v *ssa.Value) { thearch.Ginsnopdefer(s.pp) } - if sym, ok := v.Aux.(*obj.LSym); ok { + if ok { // Record call graph information for nowritebarrierrec // analysis. if nowritebarrierrecCheck != nil { - nowritebarrierrecCheck.recordCall(s.pp.curfn, sym, v.Pos) + nowritebarrierrecCheck.recordCall(s.pp.curfn, call.Fn, v.Pos) } } diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go index 828f645b39..9ce87e0aea 100644 --- a/src/cmd/compile/internal/ssa/check.go +++ b/src/cmd/compile/internal/ssa/check.go @@ -165,6 +165,18 @@ func checkFunc(f *Func) { f.Fatalf("value %v has Aux type %T, want string", v, v.Aux) } canHaveAux = true + case auxCallOff: + canHaveAuxInt = true + fallthrough + case auxCall: + if ac, ok := v.Aux.(*AuxCall); ok { + if v.Op == OpStaticCall && ac.Fn == nil { + f.Fatalf("value %v has *AuxCall with nil Fn", v) + } + } else { + f.Fatalf("value %v has Aux type %T, want *AuxCall", v, v.Aux) + } + canHaveAux = true case auxSym, auxTyp: canHaveAux = true case auxSymOff, auxSymValAndOff, auxTypSize: diff --git a/src/cmd/compile/internal/ssa/func_test.go b/src/cmd/compile/internal/ssa/func_test.go index 5f6f80f72a..568c6436f5 100644 --- a/src/cmd/compile/internal/ssa/func_test.go +++ b/src/cmd/compile/internal/ssa/func_test.go @@ -38,6 +38,7 @@ package ssa import ( "cmd/compile/internal/types" + "cmd/internal/obj" "cmd/internal/src" "fmt" "reflect" @@ -140,6 +141,12 @@ var emptyPass pass = pass{ name: "empty pass", } +// AuxCallLSym returns an AuxCall initialized with an LSym that should pass "check" +// as the Aux of a static call. +func AuxCallLSym(name string) *AuxCall { + return &AuxCall{Fn: &obj.LSym{}} +} + // Fun takes the name of an entry bloc and a series of Bloc calls, and // returns a fun containing the composed Func. entry must be a name // supplied to one of the Bloc functions. Each of the bloc names and diff --git a/src/cmd/compile/internal/ssa/fuse_test.go b/src/cmd/compile/internal/ssa/fuse_test.go index 5fe3da93ca..15190997f2 100644 --- a/src/cmd/compile/internal/ssa/fuse_test.go +++ b/src/cmd/compile/internal/ssa/fuse_test.go @@ -142,10 +142,10 @@ func TestFuseSideEffects(t *testing.T) { Valu("b", OpArg, c.config.Types.Bool, 0, nil), If("b", "then", "else")), Bloc("then", - Valu("call1", OpStaticCall, types.TypeMem, 0, nil, "mem"), + Valu("call1", OpStaticCall, types.TypeMem, 0, AuxCallLSym("_"), "mem"), Goto("empty")), Bloc("else", - Valu("call2", OpStaticCall, types.TypeMem, 0, nil, "mem"), + Valu("call2", OpStaticCall, types.TypeMem, 0, AuxCallLSym("_"), "mem"), Goto("empty")), Bloc("empty", Goto("loop")), diff --git a/src/cmd/compile/internal/ssa/gen/386Ops.go b/src/cmd/compile/internal/ssa/gen/386Ops.go index 1061e5579d..64a17cb7a3 100644 --- a/src/cmd/compile/internal/ssa/gen/386Ops.go +++ b/src/cmd/compile/internal/ssa/gen/386Ops.go @@ -463,7 +463,7 @@ func init() { faultOnNilArg0: true, }, - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index e6d66957dd..d267fe8753 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -767,7 +767,7 @@ func init() { faultOnNilArg0: true, }, - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go index e9af261a6a..f52d68dc33 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go @@ -471,7 +471,7 @@ func init() { {name: "CSEL0", argLength: 2, reg: gp1flags1, asm: "CSEL", aux: "CCop"}, // auxint(flags) ? arg0 : 0 // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/ARMOps.go b/src/cmd/compile/internal/ssa/gen/ARMOps.go index 068fecf74c..1e6b4546da 100644 --- a/src/cmd/compile/internal/ssa/gen/ARMOps.go +++ b/src/cmd/compile/internal/ssa/gen/ARMOps.go @@ -428,7 +428,7 @@ func init() { {name: "SRAcond", argLength: 3, reg: gp2flags1, asm: "SRA"}, // arg0 >> 31 if flags indicates HS, arg0 >> arg1 otherwise, signed shift, arg2=flags // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R7"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go index 5f00c080af..dc2e9d3ec9 100644 --- a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go @@ -273,7 +273,7 @@ func init() { {name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32 // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/MIPSOps.go b/src/cmd/compile/internal/ssa/gen/MIPSOps.go index a5f6c8df54..c66adcf93a 100644 --- a/src/cmd/compile/internal/ssa/gen/MIPSOps.go +++ b/src/cmd/compile/internal/ssa/gen/MIPSOps.go @@ -255,7 +255,7 @@ func init() { {name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32 // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go index 44f6a74c63..0c04e561ad 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go @@ -414,7 +414,7 @@ func init() { {name: "LoweredRound32F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true}, {name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true}, - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{callptr, ctxt, 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{callptr}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go index 8ab4abe04a..17970918e2 100644 --- a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go @@ -224,9 +224,9 @@ func init() { {name: "MOVconvert", argLength: 2, reg: gp11, asm: "MOV"}, // arg0, but converted to int/ptr as appropriate; arg1=mem // Calls - {name: "CALLstatic", argLength: 1, reg: call, aux: "SymOff", call: true, symEffect: "None"}, // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: callClosure, aux: "Int64", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: callInter, aux: "Int64", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: call, aux: "CallOff", call: true}, // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: callClosure, aux: "Int64", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: callInter, aux: "Int64", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // Generic moves and zeros diff --git a/src/cmd/compile/internal/ssa/gen/S390XOps.go b/src/cmd/compile/internal/ssa/gen/S390XOps.go index 710beaddbb..eede8a654b 100644 --- a/src/cmd/compile/internal/ssa/gen/S390XOps.go +++ b/src/cmd/compile/internal/ssa/gen/S390XOps.go @@ -475,7 +475,7 @@ func init() { {name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Write"}, - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/WasmOps.go b/src/cmd/compile/internal/ssa/gen/WasmOps.go index e43eae17e9..3286a68fb0 100644 --- a/src/cmd/compile/internal/ssa/gen/WasmOps.go +++ b/src/cmd/compile/internal/ssa/gen/WasmOps.go @@ -122,7 +122,7 @@ func init() { ) var WasmOps = []opData{ - {name: "LoweredStaticCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "LoweredStaticCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "LoweredClosureCall", argLength: 3, reg: regInfo{inputs: []regMask{gp, gp, 0}, clobbers: callerSave}, aux: "Int64", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "LoweredInterCall", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules index f7e6bbebac..df70838aa9 100644 --- a/src/cmd/compile/internal/ssa/gen/generic.rules +++ b/src/cmd/compile/internal/ssa/gen/generic.rules @@ -1933,30 +1933,30 @@ // recognize runtime.newobject and don't Zero/Nilcheck it (Zero (Load (OffPtr [c] (SP)) mem) mem) && mem.Op == OpStaticCall - && isSameSym(mem.Aux, "runtime.newobject") + && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize // offset of return value => mem (Store (Load (OffPtr [c] (SP)) mem) x mem) && isConstZero(x) && mem.Op == OpStaticCall - && isSameSym(mem.Aux, "runtime.newobject") + && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize // offset of return value => mem (Store (OffPtr (Load (OffPtr [c] (SP)) mem)) x mem) && isConstZero(x) && mem.Op == OpStaticCall - && isSameSym(mem.Aux, "runtime.newobject") + && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize // offset of return value => mem // nil checks just need to rewrite to something useless. // they will be deadcode eliminated soon afterwards. (NilCheck (Load (OffPtr [c] (SP)) (StaticCall {sym} _)) _) - && symNamed(sym, "runtime.newobject") + && isSameCall(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize // offset of return value && warnRule(fe.Debug_checknil(), v, "removed nil check") => (Invalid) (NilCheck (OffPtr (Load (OffPtr [c] (SP)) (StaticCall {sym} _))) _) - && symNamed(sym, "runtime.newobject") + && isSameCall(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize // offset of return value && warnRule(fe.Debug_checknil(), v, "removed nil check") => (Invalid) @@ -2010,7 +2010,7 @@ // See the comment in op Move in genericOps.go for discussion of the type. (StaticCall {sym} s1:(Store _ (Const(64|32) [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))) && sz >= 0 - && symNamed(sym, "runtime.memmove") + && isSameCall(sym, "runtime.memmove") && t.IsPtr() // avoids TUINTPTR, see issue 30061 && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index 5df0a164bf..acfe222089 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -387,9 +387,9 @@ var genericOps = []opData{ // as a phantom first argument. // TODO(josharian): ClosureCall and InterCall should have Int32 aux // to match StaticCall's 32 bit arg size limit. - {name: "ClosureCall", argLength: 3, aux: "Int64", call: true}, // arg0=code pointer, arg1=context ptr, arg2=memory. auxint=arg size. Returns memory. - {name: "StaticCall", argLength: 1, aux: "SymOff", call: true, symEffect: "None"}, // call function aux.(*obj.LSym), arg0=memory. auxint=arg size. Returns memory. - {name: "InterCall", argLength: 2, aux: "Int64", call: true}, // interface call. arg0=code pointer, arg1=memory, auxint=arg size. Returns memory. + {name: "ClosureCall", argLength: 3, aux: "Int64", call: true}, // arg0=code pointer, arg1=context ptr, arg2=memory. auxint=arg size. Returns memory. + {name: "StaticCall", argLength: 1, aux: "CallOff", call: true}, // call function aux.(*obj.LSym), arg0=memory. auxint=arg size. Returns memory. + {name: "InterCall", argLength: 2, aux: "Int64", call: true}, // interface call. arg0=code pointer, arg1=memory, auxint=arg size. Returns memory. // Conversions: signed extensions, zero (unsigned) extensions, truncations {name: "SignExt8to16", argLength: 1, typ: "Int16"}, diff --git a/src/cmd/compile/internal/ssa/gen/rulegen.go b/src/cmd/compile/internal/ssa/gen/rulegen.go index 9e2e112cd7..be51a7c5f8 100644 --- a/src/cmd/compile/internal/ssa/gen/rulegen.go +++ b/src/cmd/compile/internal/ssa/gen/rulegen.go @@ -1424,7 +1424,7 @@ func parseValue(val string, arch arch, loc string) (op opData, oparch, typ, auxi func opHasAuxInt(op opData) bool { switch op.aux { case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", - "SymOff", "SymValAndOff", "TypSize", "ARM64BitField", "FlagConstant", "CCop": + "SymOff", "CallOff", "SymValAndOff", "TypSize", "ARM64BitField", "FlagConstant", "CCop": return true } return false @@ -1432,7 +1432,7 @@ func opHasAuxInt(op opData) bool { func opHasAux(op opData) bool { switch op.aux { - case "String", "Sym", "SymOff", "SymValAndOff", "Typ", "TypSize", + case "String", "Sym", "SymOff", "Call", "CallOff", "SymValAndOff", "Typ", "TypSize", "S390XCCMask", "S390XRotateParams": return true } @@ -1775,6 +1775,10 @@ func (op opData) auxType() string { return "Sym" case "SymOff": return "Sym" + case "Call": + return "Call" + case "CallOff": + return "Call" case "SymValAndOff": return "Sym" case "Typ": @@ -1809,6 +1813,8 @@ func (op opData) auxIntType() string { return "float32" case "Float64": return "float64" + case "CallOff": + return "int32" case "SymOff": return "int32" case "SymValAndOff": diff --git a/src/cmd/compile/internal/ssa/loopreschedchecks.go b/src/cmd/compile/internal/ssa/loopreschedchecks.go index 1932f9d23a..4a720fdede 100644 --- a/src/cmd/compile/internal/ssa/loopreschedchecks.go +++ b/src/cmd/compile/internal/ssa/loopreschedchecks.go @@ -246,7 +246,7 @@ func insertLoopReschedChecks(f *Func) { // mem1 := call resched (mem0) // goto header resched := f.fe.Syslook("goschedguarded") - mem1 := sched.NewValue1A(bb.Pos, OpStaticCall, types.TypeMem, resched, mem0) + mem1 := sched.NewValue1A(bb.Pos, OpStaticCall, types.TypeMem, &AuxCall{resched}, mem0) sched.AddEdgeTo(h) headerMemPhi.AddArg(mem1) diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go index 063998c6a1..3aa506e3ab 100644 --- a/src/cmd/compile/internal/ssa/op.go +++ b/src/cmd/compile/internal/ssa/op.go @@ -67,6 +67,17 @@ type regInfo struct { type auxType int8 +type AuxCall struct { + Fn *obj.LSym +} + +func (a *AuxCall) String() string { + if a.Fn == nil { + return "AuxCall(nil)" + } + return fmt.Sprintf("AuxCall(%v)", a.Fn) +} + const ( auxNone auxType = iota auxBool // auxInt is 0/1 for false/true @@ -85,6 +96,8 @@ const ( auxTyp // aux is a type auxTypSize // aux is a type, auxInt is a size, must have Aux.(Type).Size() == AuxInt auxCCop // aux is a ssa.Op that represents a flags-to-bool conversion (e.g. LessThan) + auxCall // aux is a *ssa.AuxCall + auxCallOff // aux is a *ssa.AuxCall, AuxInt is int64 param (in+out) size // architecture specific aux types auxARM64BitField // aux is an arm64 bitfield lsb and width packed into auxInt diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 45401898c8..797d82f2d1 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -5816,11 +5816,10 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLstatic", - auxType: auxSymOff, + auxType: auxCallOff, argLen: 1, clobberFlags: true, call: true, - symEffect: SymNone, reg: regInfo{ clobbers: 65519, // AX CX DX BX BP SI DI X0 X1 X2 X3 X4 X5 X6 X7 }, @@ -13152,11 +13151,10 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLstatic", - auxType: auxSymOff, + auxType: auxCallOff, argLen: 1, clobberFlags: true, call: true, - symEffect: SymNone, reg: regInfo{ clobbers: 4294967279, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 }, @@ -16922,11 +16920,10 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLstatic", - auxType: auxSymOff, + auxType: auxCallOff, argLen: 1, clobberFlags: true, call: true, - symEffect: SymNone, reg: regInfo{ clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, @@ -20556,11 +20553,10 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLstatic", - auxType: auxSymOff, + auxType: auxCallOff, argLen: 1, clobberFlags: true, call: true, - symEffect: SymNone, reg: regInfo{ clobbers: 9223372035512336383, // 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 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, @@ -22257,11 +22253,10 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLstatic", - auxType: auxSymOff, + auxType: auxCallOff, argLen: 1, clobberFlags: true, call: true, - symEffect: SymNone, reg: regInfo{ clobbers: 140737421246462, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31 F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30 HI LO }, @@ -23804,11 +23799,10 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLstatic", - auxType: auxSymOff, + auxType: auxCallOff, argLen: 1, clobberFlags: true, call: true, - symEffect: SymNone, reg: regInfo{ clobbers: 4611686018393833470, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 HI LO }, @@ -26504,11 +26498,10 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLstatic", - auxType: auxSymOff, + auxType: auxCallOff, argLen: 1, clobberFlags: true, call: true, - symEffect: SymNone, reg: regInfo{ clobbers: 576460745860964344, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 }, @@ -27787,11 +27780,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "CALLstatic", - auxType: auxSymOff, - argLen: 1, - call: true, - symEffect: SymNone, + name: "CALLstatic", + auxType: auxCallOff, + argLen: 1, + call: true, reg: regInfo{ clobbers: 9223372035781033980, // X3 g X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X27 X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, @@ -31386,11 +31378,10 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLstatic", - auxType: auxSymOff, + auxType: auxCallOff, argLen: 1, clobberFlags: true, call: true, - symEffect: SymNone, reg: regInfo{ clobbers: 4294933503, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 g R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, @@ -32031,11 +32022,10 @@ var opcodeTable = [...]opInfo{ }, { - name: "LoweredStaticCall", - auxType: auxSymOff, - argLen: 1, - call: true, - symEffect: SymNone, + name: "LoweredStaticCall", + auxType: auxCallOff, + argLen: 1, + call: true, reg: regInfo{ clobbers: 844424930131967, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 g }, @@ -34779,12 +34769,11 @@ var opcodeTable = [...]opInfo{ generic: true, }, { - name: "StaticCall", - auxType: auxSymOff, - argLen: 1, - call: true, - symEffect: SymNone, - generic: true, + name: "StaticCall", + auxType: auxCallOff, + argLen: 1, + call: true, + generic: true, }, { name: "InterCall", diff --git a/src/cmd/compile/internal/ssa/regalloc_test.go b/src/cmd/compile/internal/ssa/regalloc_test.go index bb8be5e7ac..d990cac47b 100644 --- a/src/cmd/compile/internal/ssa/regalloc_test.go +++ b/src/cmd/compile/internal/ssa/regalloc_test.go @@ -68,7 +68,7 @@ func TestNoGetgLoadReg(t *testing.T) { Exit("v16"), ), Bloc("b2", - Valu("v12", OpARM64CALLstatic, types.TypeMem, 0, nil, "v1"), + Valu("v12", OpARM64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "v1"), Goto("b3"), ), ) @@ -99,7 +99,7 @@ func TestSpillWithLoop(t *testing.T) { ), Bloc("loop", Valu("memphi", OpPhi, types.TypeMem, 0, nil, "mem", "call"), - Valu("call", OpAMD64CALLstatic, types.TypeMem, 0, nil, "memphi"), + Valu("call", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "memphi"), Valu("test", OpAMD64CMPBconst, types.TypeFlags, 0, nil, "cond"), Eq("test", "next", "exit"), ), @@ -140,12 +140,12 @@ func TestSpillMove1(t *testing.T) { Bloc("exit1", // store before call, y is available in a register Valu("mem2", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem"), - Valu("mem3", OpAMD64CALLstatic, types.TypeMem, 0, nil, "mem2"), + Valu("mem3", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem2"), Exit("mem3"), ), Bloc("exit2", // store after call, y must be loaded from a spill location - Valu("mem4", OpAMD64CALLstatic, types.TypeMem, 0, nil, "mem"), + Valu("mem4", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem"), Valu("mem5", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem4"), Exit("mem5"), ), @@ -188,13 +188,13 @@ func TestSpillMove2(t *testing.T) { ), Bloc("exit1", // store after call, y must be loaded from a spill location - Valu("mem2", OpAMD64CALLstatic, types.TypeMem, 0, nil, "mem"), + Valu("mem2", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem"), Valu("mem3", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem2"), Exit("mem3"), ), Bloc("exit2", // store after call, y must be loaded from a spill location - Valu("mem4", OpAMD64CALLstatic, types.TypeMem, 0, nil, "mem"), + Valu("mem4", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem"), Valu("mem5", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem4"), Exit("mem5"), ), diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index 09f94ef53e..8195d407e0 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -393,15 +393,9 @@ func canMergeLoad(target, load *Value) bool { return true } -// symNamed reports whether sym's name is name. -func symNamed(sym Sym, name string) bool { - return sym.String() == name -} - -// isSameSym reports whether sym is the same as the given named symbol -func isSameSym(sym interface{}, name string) bool { - s, ok := sym.(fmt.Stringer) - return ok && s.String() == name +// isSameCall reports whether sym is the same as the given named symbol +func isSameCall(sym interface{}, name string) bool { + return sym.(*AuxCall).Fn.String() == name } // nlz returns the number of leading zeros. @@ -713,6 +707,9 @@ func auxToSym(i interface{}) Sym { func auxToType(i interface{}) *types.Type { return i.(*types.Type) } +func auxToCall(i interface{}) *AuxCall { + return i.(*AuxCall) +} func auxToS390xCCMask(i interface{}) s390x.CCMask { return i.(s390x.CCMask) } @@ -726,6 +723,9 @@ func stringToAux(s string) interface{} { func symToAux(s Sym) interface{} { return s } +func callToAux(s *AuxCall) interface{} { + return s +} func typeToAux(t *types.Type) interface{} { return t } @@ -743,7 +743,7 @@ func uaddOvf(a, b int64) bool { // de-virtualize an InterCall // 'sym' is the symbol for the itab -func devirt(v *Value, sym Sym, offset int64) *obj.LSym { +func devirt(v *Value, sym Sym, offset int64) *AuxCall { f := v.Block.Func n, ok := sym.(*obj.LSym) if !ok { @@ -757,7 +757,10 @@ func devirt(v *Value, sym Sym, offset int64) *obj.LSym { f.Warnl(v.Pos, "couldn't de-virtualize call") } } - return lsym + if lsym == nil { + return nil + } + return &AuxCall{Fn: lsym} } // isSamePtr reports whether p1 and p2 point to the same address. @@ -1377,12 +1380,12 @@ func registerizable(b *Block, typ *types.Type) bool { } // needRaceCleanup reports whether this call to racefuncenter/exit isn't needed. -func needRaceCleanup(sym Sym, v *Value) bool { +func needRaceCleanup(sym *AuxCall, v *Value) bool { f := v.Block.Func if !f.Config.Race { return false } - if !symNamed(sym, "runtime.racefuncenter") && !symNamed(sym, "runtime.racefuncexit") { + if !isSameCall(sym, "runtime.racefuncenter") && !isSameCall(sym, "runtime.racefuncexit") { return false } for _, b := range f.Blocks { @@ -1391,7 +1394,7 @@ func needRaceCleanup(sym Sym, v *Value) bool { case OpStaticCall: // Check for racefuncenter will encounter racefuncexit and vice versa. // Allow calls to panic* - s := v.Aux.(fmt.Stringer).String() + s := v.Aux.(*AuxCall).Fn.String() switch s { case "runtime.racefuncenter", "runtime.racefuncexit", "runtime.panicdivide", "runtime.panicwrap", @@ -1409,7 +1412,7 @@ func needRaceCleanup(sym Sym, v *Value) bool { } } } - if symNamed(sym, "runtime.racefuncenter") { + if isSameCall(sym, "runtime.racefuncenter") { // If we're removing racefuncenter, remove its argument as well. if v.Args[0].Op != OpStore { return false diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index 180e48b34c..4b388a68cd 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -8515,7 +8515,7 @@ func rewriteValuegeneric_OpInterCall(v *Value) bool { } v.reset(OpStaticCall) v.AuxInt = int32ToAuxInt(int32(argsize)) - v.Aux = symToAux(devirt(v, itab, off)) + v.Aux = callToAux(devirt(v, itab, off)) v.AddArg(mem) return true } @@ -16022,7 +16022,7 @@ func rewriteValuegeneric_OpNilCheck(v *Value) bool { return true } // match: (NilCheck (Load (OffPtr [c] (SP)) (StaticCall {sym} _)) _) - // cond: symNamed(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check") + // cond: isSameCall(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check") // result: (Invalid) for { if v_0.Op != OpLoad { @@ -16042,15 +16042,15 @@ func rewriteValuegeneric_OpNilCheck(v *Value) bool { if v_0_1.Op != OpStaticCall { break } - sym := auxToSym(v_0_1.Aux) - if !(symNamed(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check")) { + sym := auxToCall(v_0_1.Aux) + if !(isSameCall(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check")) { break } v.reset(OpInvalid) return true } // match: (NilCheck (OffPtr (Load (OffPtr [c] (SP)) (StaticCall {sym} _))) _) - // cond: symNamed(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check") + // cond: isSameCall(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check") // result: (Invalid) for { if v_0.Op != OpOffPtr { @@ -16074,8 +16074,8 @@ func rewriteValuegeneric_OpNilCheck(v *Value) bool { if v_0_0_1.Op != OpStaticCall { break } - sym := auxToSym(v_0_0_1.Aux) - if !(symNamed(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check")) { + sym := auxToCall(v_0_0_1.Aux) + if !(isSameCall(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check")) { break } v.reset(OpInvalid) @@ -21067,10 +21067,10 @@ func rewriteValuegeneric_OpStaticCall(v *Value) bool { b := v.Block config := b.Func.Config // match: (StaticCall {sym} s1:(Store _ (Const64 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))) - // cond: sz >= 0 && symNamed(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3) + // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3) // result: (Move {t.Elem()} [int64(sz)] dst src mem) for { - sym := auxToSym(v.Aux) + sym := auxToCall(v.Aux) s1 := v_0 if s1.Op != OpStore { break @@ -21094,7 +21094,7 @@ func rewriteValuegeneric_OpStaticCall(v *Value) bool { t := auxToType(s3.Aux) mem := s3.Args[2] dst := s3.Args[1] - if !(sz >= 0 && symNamed(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3)) { + if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3)) { break } v.reset(OpMove) @@ -21104,10 +21104,10 @@ func rewriteValuegeneric_OpStaticCall(v *Value) bool { return true } // match: (StaticCall {sym} s1:(Store _ (Const32 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))) - // cond: sz >= 0 && symNamed(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3) + // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3) // result: (Move {t.Elem()} [int64(sz)] dst src mem) for { - sym := auxToSym(v.Aux) + sym := auxToCall(v.Aux) s1 := v_0 if s1.Op != OpStore { break @@ -21131,7 +21131,7 @@ func rewriteValuegeneric_OpStaticCall(v *Value) bool { t := auxToType(s3.Aux) mem := s3.Args[2] dst := s3.Args[1] - if !(sz >= 0 && symNamed(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3)) { + if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3)) { break } v.reset(OpMove) @@ -21144,7 +21144,7 @@ func rewriteValuegeneric_OpStaticCall(v *Value) bool { // cond: needRaceCleanup(sym, v) // result: x for { - sym := auxToSym(v.Aux) + sym := auxToCall(v.Aux) x := v_0 if !(needRaceCleanup(sym, v)) { break @@ -21608,7 +21608,7 @@ func rewriteValuegeneric_OpStore(v *Value) bool { return true } // match: (Store (Load (OffPtr [c] (SP)) mem) x mem) - // cond: isConstZero(x) && mem.Op == OpStaticCall && isSameSym(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize + // cond: isConstZero(x) && mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize // result: mem for { if v_0.Op != OpLoad { @@ -21625,14 +21625,14 @@ func rewriteValuegeneric_OpStore(v *Value) bool { break } x := v_1 - if mem != v_2 || !(isConstZero(x) && mem.Op == OpStaticCall && isSameSym(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize) { + if mem != v_2 || !(isConstZero(x) && mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize) { break } v.copyOf(mem) return true } // match: (Store (OffPtr (Load (OffPtr [c] (SP)) mem)) x mem) - // cond: isConstZero(x) && mem.Op == OpStaticCall && isSameSym(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize + // cond: isConstZero(x) && mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize // result: mem for { if v_0.Op != OpOffPtr { @@ -21653,7 +21653,7 @@ func rewriteValuegeneric_OpStore(v *Value) bool { break } x := v_1 - if mem != v_2 || !(isConstZero(x) && mem.Op == OpStaticCall && isSameSym(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize) { + if mem != v_2 || !(isConstZero(x) && mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize) { break } v.copyOf(mem) @@ -24337,7 +24337,7 @@ func rewriteValuegeneric_OpZero(v *Value) bool { b := v.Block config := b.Func.Config // match: (Zero (Load (OffPtr [c] (SP)) mem) mem) - // cond: mem.Op == OpStaticCall && isSameSym(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize + // cond: mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize // result: mem for { if v_0.Op != OpLoad { @@ -24350,7 +24350,7 @@ func rewriteValuegeneric_OpZero(v *Value) bool { } c := auxIntToInt64(v_0_0.AuxInt) v_0_0_0 := v_0_0.Args[0] - if v_0_0_0.Op != OpSP || mem != v_1 || !(mem.Op == OpStaticCall && isSameSym(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize) { + if v_0_0_0.Op != OpSP || mem != v_1 || !(mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize) { break } v.copyOf(mem) diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go index 6692df7921..94b8763d5d 100644 --- a/src/cmd/compile/internal/ssa/value.go +++ b/src/cmd/compile/internal/ssa/value.go @@ -193,11 +193,11 @@ func (v *Value) auxString() string { return fmt.Sprintf(" [%g]", v.AuxFloat()) case auxString: return fmt.Sprintf(" {%q}", v.Aux) - case auxSym, auxTyp: + case auxSym, auxCall, auxTyp: if v.Aux != nil { return fmt.Sprintf(" {%v}", v.Aux) } - case auxSymOff, auxTypSize: + case auxSymOff, auxCallOff, auxTypSize: s := "" if v.Aux != nil { s = fmt.Sprintf(" {%v}", v.Aux) diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go index 214798a1ab..c358406862 100644 --- a/src/cmd/compile/internal/ssa/writebarrier.go +++ b/src/cmd/compile/internal/ssa/writebarrier.go @@ -523,7 +523,7 @@ func wbcall(pos src.XPos, b *Block, fn, typ *obj.LSym, ptr, val, mem, sp, sb *Va off = round(off, config.PtrSize) // issue call - mem = b.NewValue1A(pos, OpStaticCall, types.TypeMem, fn, mem) + mem = b.NewValue1A(pos, OpStaticCall, types.TypeMem, &AuxCall{fn}, mem) mem.AuxInt = off - config.ctxt.FixedFrameSize() return mem } @@ -582,7 +582,7 @@ func IsNewObject(v *Value, mem *Value) bool { if mem.Op != OpStaticCall { return false } - if !isSameSym(mem.Aux, "runtime.newobject") { + if !isSameCall(mem.Aux, "runtime.newobject") { return false } if v.Args[0].Op != OpOffPtr { diff --git a/src/cmd/compile/internal/wasm/ssa.go b/src/cmd/compile/internal/wasm/ssa.go index 7861667b88..a36fbca4e0 100644 --- a/src/cmd/compile/internal/wasm/ssa.go +++ b/src/cmd/compile/internal/wasm/ssa.go @@ -122,7 +122,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { switch v.Op { case ssa.OpWasmLoweredStaticCall, ssa.OpWasmLoweredClosureCall, ssa.OpWasmLoweredInterCall: s.PrepareCall(v) - if v.Aux == gc.Deferreturn { + if call, ok := v.Aux.(*ssa.AuxCall); ok && call.Fn == gc.Deferreturn { // add a resume point before call to deferreturn so it can be called again via jmpdefer s.Prog(wasm.ARESUMEPOINT) } @@ -130,7 +130,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { getValue64(s, v.Args[1]) setReg(s, wasm.REG_CTXT) } - if sym, ok := v.Aux.(*obj.LSym); ok { + if call, ok := v.Aux.(*ssa.AuxCall); ok && call.Fn != nil { + sym := call.Fn p := s.Prog(obj.ACALL) p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: sym} p.Pos = v.Pos -- cgit v1.2.3-54-g00ecf From 3c85e995efd0c2adf8578ed27565ad3b427f1a43 Mon Sep 17 00:00:00 2001 From: David Chase Date: Mon, 15 Jun 2020 18:27:02 -0400 Subject: cmd/compile: extend ssa.AuxCall to closure and interface calls Also introduce helper methods. Change-Id: I11a744ed002bae0ca9ebabba3206e1c14147e03d Reviewed-on: https://go-review.googlesource.com/c/go/+/239080 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 18 +++++----- src/cmd/compile/internal/ssa/gen/386Ops.go | 6 ++-- src/cmd/compile/internal/ssa/gen/AMD64Ops.go | 6 ++-- src/cmd/compile/internal/ssa/gen/ARM64Ops.go | 6 ++-- src/cmd/compile/internal/ssa/gen/ARMOps.go | 6 ++-- src/cmd/compile/internal/ssa/gen/MIPS64Ops.go | 6 ++-- src/cmd/compile/internal/ssa/gen/MIPSOps.go | 6 ++-- src/cmd/compile/internal/ssa/gen/PPC64Ops.go | 6 ++-- src/cmd/compile/internal/ssa/gen/RISCV64Ops.go | 6 ++-- src/cmd/compile/internal/ssa/gen/S390XOps.go | 6 ++-- src/cmd/compile/internal/ssa/gen/WasmOps.go | 8 ++--- src/cmd/compile/internal/ssa/gen/genericOps.go | 6 ++-- src/cmd/compile/internal/ssa/loopreschedchecks.go | 2 +- src/cmd/compile/internal/ssa/op.go | 15 ++++++++ src/cmd/compile/internal/ssa/opGen.go | 44 +++++++++++------------ src/cmd/compile/internal/ssa/rewrite.go | 2 +- src/cmd/compile/internal/ssa/rewritegeneric.go | 2 +- src/cmd/compile/internal/ssa/writebarrier.go | 2 +- 18 files changed, 84 insertions(+), 69 deletions(-) diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 0ee31bd9a3..542b1b51c2 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4302,10 +4302,10 @@ func (s *state) openDeferExit() { v := s.load(r.closure.Type.Elem(), r.closure) s.maybeNilCheckClosure(v, callDefer) codeptr := s.rawLoad(types.Types[TUINTPTR], v) - call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, nil, codeptr, v, s.mem()) + call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(), codeptr, v, s.mem()) } else { // Do a static call if the original call was a static function or method - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: fn.Sym.Linksym()}, s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn.Sym.Linksym()), s.mem()) } call.AuxInt = stksize s.vars[&memVar] = call @@ -4437,7 +4437,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // Call runtime.deferprocStack with pointer to _defer record. arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize()) s.store(types.Types[TUINTPTR], arg0, addr) - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: deferprocStack}, s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferprocStack), s.mem()) if stksize < int64(Widthptr) { // We need room for both the call to deferprocStack and the call to // the deferred function. @@ -4482,9 +4482,9 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // call target switch { case k == callDefer: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: deferproc}, s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferproc), s.mem()) case k == callGo: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: newproc}, s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(newproc), s.mem()) case closure != nil: // rawLoad because loading the code pointer from a // closure is always safe, but IsSanitizerSafeAddr @@ -4492,11 +4492,11 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // critical that we not clobber any arguments already // stored onto the stack. codeptr = s.rawLoad(types.Types[TUINTPTR], closure) - call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, nil, codeptr, closure, s.mem()) + call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(), codeptr, closure, s.mem()) case codeptr != nil: - call = s.newValue2A(ssa.OpInterCall, types.TypeMem, nil, codeptr, s.mem()) + call = s.newValue2A(ssa.OpInterCall, types.TypeMem, ssa.InterfaceAuxCall(), codeptr, s.mem()) case sym != nil: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: sym.Linksym()}, s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(sym.Linksym()), s.mem()) default: s.Fatalf("bad call type %v %v", n.Op, n) } @@ -4929,7 +4929,7 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args . off = Rnd(off, int64(Widthreg)) // Issue call - call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: fn}, s.mem()) + call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn), s.mem()) s.vars[&memVar] = call if !returns { diff --git a/src/cmd/compile/internal/ssa/gen/386Ops.go b/src/cmd/compile/internal/ssa/gen/386Ops.go index 64a17cb7a3..ddabde7d3d 100644 --- a/src/cmd/compile/internal/ssa/gen/386Ops.go +++ b/src/cmd/compile/internal/ssa/gen/386Ops.go @@ -463,9 +463,9 @@ func init() { faultOnNilArg0: true, }, - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // arg0 = destination pointer // arg1 = source pointer diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index d267fe8753..2df5016d59 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -767,9 +767,9 @@ func init() { faultOnNilArg0: true, }, - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // arg0 = destination pointer // arg1 = source pointer diff --git a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go index f52d68dc33..9ff53f7e4e 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go @@ -471,9 +471,9 @@ func init() { {name: "CSEL0", argLength: 2, reg: gp1flags1, asm: "CSEL", aux: "CCop"}, // auxint(flags) ? arg0 : 0 // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // pseudo-ops {name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil. arg1=mem. diff --git a/src/cmd/compile/internal/ssa/gen/ARMOps.go b/src/cmd/compile/internal/ssa/gen/ARMOps.go index 1e6b4546da..70c789937a 100644 --- a/src/cmd/compile/internal/ssa/gen/ARMOps.go +++ b/src/cmd/compile/internal/ssa/gen/ARMOps.go @@ -428,9 +428,9 @@ func init() { {name: "SRAcond", argLength: 3, reg: gp2flags1, asm: "SRA"}, // arg0 >> 31 if flags indicates HS, arg0 >> arg1 otherwise, signed shift, arg2=flags // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R7"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R7"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // pseudo-ops {name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil. arg1=mem. diff --git a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go index dc2e9d3ec9..e1e3933502 100644 --- a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go @@ -273,9 +273,9 @@ func init() { {name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32 // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // duffzero // arg0 = address of memory to zero diff --git a/src/cmd/compile/internal/ssa/gen/MIPSOps.go b/src/cmd/compile/internal/ssa/gen/MIPSOps.go index c66adcf93a..cd7357f62b 100644 --- a/src/cmd/compile/internal/ssa/gen/MIPSOps.go +++ b/src/cmd/compile/internal/ssa/gen/MIPSOps.go @@ -255,9 +255,9 @@ func init() { {name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32 // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // atomic ops diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go index 0c04e561ad..37706b2dd9 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go @@ -414,9 +414,9 @@ func init() { {name: "LoweredRound32F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true}, {name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true}, - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{callptr, ctxt, 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{callptr}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{callptr, ctxt, 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{callptr}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // large or unaligned zeroing // arg0 = address of memory to zero (in R3, changed as side effect) diff --git a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go index 17970918e2..b06b86075e 100644 --- a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go @@ -224,9 +224,9 @@ func init() { {name: "MOVconvert", argLength: 2, reg: gp11, asm: "MOV"}, // arg0, but converted to int/ptr as appropriate; arg1=mem // Calls - {name: "CALLstatic", argLength: 1, reg: call, aux: "CallOff", call: true}, // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: callClosure, aux: "Int64", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: callInter, aux: "Int64", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: call, aux: "CallOff", call: true}, // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: callClosure, aux: "CallOff", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: callInter, aux: "CallOff", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // Generic moves and zeros diff --git a/src/cmd/compile/internal/ssa/gen/S390XOps.go b/src/cmd/compile/internal/ssa/gen/S390XOps.go index eede8a654b..417b33cf91 100644 --- a/src/cmd/compile/internal/ssa/gen/S390XOps.go +++ b/src/cmd/compile/internal/ssa/gen/S390XOps.go @@ -475,9 +475,9 @@ func init() { {name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Write"}, - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // (InvertFlags (CMP a b)) == (CMP b a) // InvertFlags is a pseudo-op which can't appear in assembly output. diff --git a/src/cmd/compile/internal/ssa/gen/WasmOps.go b/src/cmd/compile/internal/ssa/gen/WasmOps.go index 3286a68fb0..36c53bc78c 100644 --- a/src/cmd/compile/internal/ssa/gen/WasmOps.go +++ b/src/cmd/compile/internal/ssa/gen/WasmOps.go @@ -122,9 +122,9 @@ func init() { ) var WasmOps = []opData{ - {name: "LoweredStaticCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "LoweredClosureCall", argLength: 3, reg: regInfo{inputs: []regMask{gp, gp, 0}, clobbers: callerSave}, aux: "Int64", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "LoweredInterCall", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "LoweredStaticCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "LoweredClosureCall", argLength: 3, reg: regInfo{inputs: []regMask{gp, gp, 0}, clobbers: callerSave}, aux: "CallOff", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "LoweredInterCall", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem {name: "LoweredAddr", argLength: 1, reg: gp11, aux: "SymOff", rematerializeable: true, symEffect: "Addr"}, // returns base+aux+auxint, arg0=base {name: "LoweredMove", argLength: 3, reg: regInfo{inputs: []regMask{gp, gp}}, aux: "Int64"}, // large move. arg0=dst, arg1=src, arg2=mem, auxint=len/8, returns mem @@ -137,7 +137,7 @@ func init() { {name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{gp, gp}}, aux: "Sym", symEffect: "None"}, // invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier // LoweredConvert converts between pointers and integers. - // We have a special op for this so as to not confuse GC + // We have a special op for this so as to not confuse GCCallOff // (particularly stack maps). It takes a memory arg so it // gets correctly ordered with respect to GC safepoints. // arg0=ptr/int arg1=mem, output=int/ptr diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index acfe222089..145ba2d50c 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -387,9 +387,9 @@ var genericOps = []opData{ // as a phantom first argument. // TODO(josharian): ClosureCall and InterCall should have Int32 aux // to match StaticCall's 32 bit arg size limit. - {name: "ClosureCall", argLength: 3, aux: "Int64", call: true}, // arg0=code pointer, arg1=context ptr, arg2=memory. auxint=arg size. Returns memory. - {name: "StaticCall", argLength: 1, aux: "CallOff", call: true}, // call function aux.(*obj.LSym), arg0=memory. auxint=arg size. Returns memory. - {name: "InterCall", argLength: 2, aux: "Int64", call: true}, // interface call. arg0=code pointer, arg1=memory, auxint=arg size. Returns memory. + {name: "ClosureCall", argLength: 3, aux: "CallOff", call: true}, // arg0=code pointer, arg1=context ptr, arg2=memory. auxint=arg size. Returns memory. + {name: "StaticCall", argLength: 1, aux: "CallOff", call: true}, // call function aux.(*obj.LSym), arg0=memory. auxint=arg size. Returns memory. + {name: "InterCall", argLength: 2, aux: "CallOff", call: true}, // interface call. arg0=code pointer, arg1=memory, auxint=arg size. Returns memory. // Conversions: signed extensions, zero (unsigned) extensions, truncations {name: "SignExt8to16", argLength: 1, typ: "Int16"}, diff --git a/src/cmd/compile/internal/ssa/loopreschedchecks.go b/src/cmd/compile/internal/ssa/loopreschedchecks.go index 4a720fdede..ebd23b34c7 100644 --- a/src/cmd/compile/internal/ssa/loopreschedchecks.go +++ b/src/cmd/compile/internal/ssa/loopreschedchecks.go @@ -246,7 +246,7 @@ func insertLoopReschedChecks(f *Func) { // mem1 := call resched (mem0) // goto header resched := f.fe.Syslook("goschedguarded") - mem1 := sched.NewValue1A(bb.Pos, OpStaticCall, types.TypeMem, &AuxCall{resched}, mem0) + mem1 := sched.NewValue1A(bb.Pos, OpStaticCall, types.TypeMem, StaticAuxCall(resched), mem0) sched.AddEdgeTo(h) headerMemPhi.AddArg(mem1) diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go index 3aa506e3ab..c498a288a1 100644 --- a/src/cmd/compile/internal/ssa/op.go +++ b/src/cmd/compile/internal/ssa/op.go @@ -78,6 +78,21 @@ func (a *AuxCall) String() string { return fmt.Sprintf("AuxCall(%v)", a.Fn) } +// StaticAuxCall returns an AuxCall for a static call. +func StaticAuxCall(sym *obj.LSym) *AuxCall { + return &AuxCall{Fn: sym} +} + +// InterfaceAuxCall returns an AuxCall for an interface call. +func InterfaceAuxCall() *AuxCall { + return &AuxCall{} +} + +// ClosureAuxCall returns an AuxCall for a closure call. +func ClosureAuxCall() *AuxCall { + return &AuxCall{} +} + const ( auxNone auxType = iota auxBool // auxInt is 0/1 for false/true diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 797d82f2d1..d95943231a 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -5826,7 +5826,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, clobberFlags: true, call: true, @@ -5840,7 +5840,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, clobberFlags: true, call: true, @@ -13161,7 +13161,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, clobberFlags: true, call: true, @@ -13175,7 +13175,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, clobberFlags: true, call: true, @@ -16930,7 +16930,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, clobberFlags: true, call: true, @@ -16944,7 +16944,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, clobberFlags: true, call: true, @@ -20563,7 +20563,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, clobberFlags: true, call: true, @@ -20577,7 +20577,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, clobberFlags: true, call: true, @@ -22263,7 +22263,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, clobberFlags: true, call: true, @@ -22277,7 +22277,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, clobberFlags: true, call: true, @@ -23809,7 +23809,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, clobberFlags: true, call: true, @@ -23823,7 +23823,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, clobberFlags: true, call: true, @@ -26508,7 +26508,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, clobberFlags: true, call: true, @@ -26522,7 +26522,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, clobberFlags: true, call: true, @@ -27790,7 +27790,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, call: true, reg: regInfo{ @@ -27803,7 +27803,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, call: true, reg: regInfo{ @@ -31388,7 +31388,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, clobberFlags: true, call: true, @@ -31402,7 +31402,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, clobberFlags: true, call: true, @@ -32032,7 +32032,7 @@ var opcodeTable = [...]opInfo{ }, { name: "LoweredClosureCall", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, call: true, reg: regInfo{ @@ -32045,7 +32045,7 @@ var opcodeTable = [...]opInfo{ }, { name: "LoweredInterCall", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, call: true, reg: regInfo{ @@ -34763,7 +34763,7 @@ var opcodeTable = [...]opInfo{ }, { name: "ClosureCall", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, call: true, generic: true, @@ -34777,7 +34777,7 @@ var opcodeTable = [...]opInfo{ }, { name: "InterCall", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, call: true, generic: true, diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index 8195d407e0..eb371ce38b 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -760,7 +760,7 @@ func devirt(v *Value, sym Sym, offset int64) *AuxCall { if lsym == nil { return nil } - return &AuxCall{Fn: lsym} + return StaticAuxCall(lsym) } // isSamePtr reports whether p1 and p2 point to the same address. diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index 4b388a68cd..1ecfabf7cb 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -8483,7 +8483,7 @@ func rewriteValuegeneric_OpInterCall(v *Value) bool { // cond: devirt(v, itab, off) != nil // result: (StaticCall [int32(argsize)] {devirt(v, itab, off)} mem) for { - argsize := auxIntToInt64(v.AuxInt) + argsize := auxIntToInt32(v.AuxInt) if v_0.Op != OpLoad { break } diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go index c358406862..4322a85c90 100644 --- a/src/cmd/compile/internal/ssa/writebarrier.go +++ b/src/cmd/compile/internal/ssa/writebarrier.go @@ -523,7 +523,7 @@ func wbcall(pos src.XPos, b *Block, fn, typ *obj.LSym, ptr, val, mem, sp, sb *Va off = round(off, config.PtrSize) // issue call - mem = b.NewValue1A(pos, OpStaticCall, types.TypeMem, &AuxCall{fn}, mem) + mem = b.NewValue1A(pos, OpStaticCall, types.TypeMem, StaticAuxCall(fn), mem) mem.AuxInt = off - config.ctxt.FixedFrameSize() return mem } -- cgit v1.2.3-54-g00ecf From acde81e0a9c17ea23a6fc545b40bfabc80133f78 Mon Sep 17 00:00:00 2001 From: David Chase Date: Fri, 19 Jun 2020 15:29:51 -0400 Subject: cmd/compile: initialize ACArgs and ACResults AuxCall fields for static and interface calls. Extend use of AuxCall Change-Id: I68b6d9bad09506532e1415fd70d44cf6c15b4b93 Reviewed-on: https://go-review.googlesource.com/c/go/+/239081 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 53 ++++++++++++++++++----- src/cmd/compile/internal/ssa/gen/generic.rules | 4 +- src/cmd/compile/internal/ssa/loopreschedchecks.go | 2 +- src/cmd/compile/internal/ssa/op.go | 50 ++++++++++++++++++--- src/cmd/compile/internal/ssa/rewrite.go | 5 ++- src/cmd/compile/internal/ssa/rewritegeneric.go | 11 ++--- src/cmd/compile/internal/ssa/writebarrier.go | 6 ++- 7 files changed, 102 insertions(+), 29 deletions(-) diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 542b1b51c2..6c0b027c17 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4280,16 +4280,20 @@ func (s *state) openDeferExit() { argStart := Ctxt.FixedFrameSize() fn := r.n.Left stksize := fn.Type.ArgWidth() + var ACArgs []ssa.Param + var ACResults []ssa.Param if r.rcvr != nil { // rcvr in case of OCALLINTER v := s.load(r.rcvr.Type.Elem(), r.rcvr) addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart) + ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart)}) s.store(types.Types[TUINTPTR], addr, v) } for j, argAddrVal := range r.argVals { f := getParam(r.n, j) pt := types.NewPtr(f.Type) addr := s.constOffPtrSP(pt, argStart+f.Offset) + ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart + f.Offset)}) if !canSSAType(f.Type) { s.move(f.Type, addr, argAddrVal) } else { @@ -4305,7 +4309,7 @@ func (s *state) openDeferExit() { call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(), codeptr, v, s.mem()) } else { // Do a static call if the original call was a static function or method - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn.Sym.Linksym()), s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn.Sym.Linksym(), ACArgs, ACResults), s.mem()) } call.AuxInt = stksize s.vars[&memVar] = call @@ -4340,6 +4344,17 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { var codeptr *ssa.Value // ptr to target code (if dynamic) var rcvr *ssa.Value // receiver to set fn := n.Left + var ACArgs []ssa.Param + var ACResults []ssa.Param + res := n.Left.Type.Results() + if k == callNormal { + nf := res.NumFields() + for i := 0; i < nf; i++ { + fp := res.Field(i) + ACResults = append(ACResults, ssa.Param{Type: fp.Type, Offset: int32(fp.Offset + Ctxt.FixedFrameSize())}) + } + } + switch n.Op { case OCALLFUNC: if k == callNormal && fn.Op == ONAME && fn.Class() == PFUNC { @@ -4437,10 +4452,12 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // Call runtime.deferprocStack with pointer to _defer record. arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize()) s.store(types.Types[TUINTPTR], arg0, addr) - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferprocStack), s.mem()) + ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(Ctxt.FixedFrameSize())}) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferprocStack, ACArgs, ACResults), s.mem()) if stksize < int64(Widthptr) { // We need room for both the call to deferprocStack and the call to // the deferred function. + // TODO Revisit this if/when we pass args in registers. stksize = int64(Widthptr) } call.AuxInt = stksize @@ -4453,8 +4470,10 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // Write argsize and closure (args to newproc/deferproc). argsize := s.constInt32(types.Types[TUINT32], int32(stksize)) addr := s.constOffPtrSP(s.f.Config.Types.UInt32Ptr, argStart) + ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINT32], Offset: int32(argStart)}) s.store(types.Types[TUINT32], addr, argsize) addr = s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart+int64(Widthptr)) + ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart) + int32(Widthptr)}) s.store(types.Types[TUINTPTR], addr, closure) stksize += 2 * int64(Widthptr) argStart += 2 * int64(Widthptr) @@ -4463,6 +4482,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // Set receiver (for interface calls). if rcvr != nil { addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart) + ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart)}) s.store(types.Types[TUINTPTR], addr, rcvr) } @@ -4471,20 +4491,20 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { args := n.Rlist.Slice() if n.Op == OCALLMETH { f := t.Recv() - s.storeArg(args[0], f.Type, argStart+f.Offset) + ACArgs = append(ACArgs, s.storeArg(args[0], f.Type, argStart+f.Offset)) args = args[1:] } for i, n := range args { f := t.Params().Field(i) - s.storeArg(n, f.Type, argStart+f.Offset) + ACArgs = append(ACArgs, s.storeArg(n, f.Type, argStart+f.Offset)) } // call target switch { case k == callDefer: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferproc), s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferproc, ACArgs, ACResults), s.mem()) case k == callGo: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(newproc), s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(newproc, ACArgs, ACResults), s.mem()) case closure != nil: // rawLoad because loading the code pointer from a // closure is always safe, but IsSanitizerSafeAddr @@ -4494,9 +4514,9 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { codeptr = s.rawLoad(types.Types[TUINTPTR], closure) call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(), codeptr, closure, s.mem()) case codeptr != nil: - call = s.newValue2A(ssa.OpInterCall, types.TypeMem, ssa.InterfaceAuxCall(), codeptr, s.mem()) + call = s.newValue2A(ssa.OpInterCall, types.TypeMem, ssa.InterfaceAuxCall(ACArgs, ACResults), codeptr, s.mem()) case sym != nil: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(sym.Linksym()), s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(sym.Linksym(), ACArgs, ACResults), s.mem()) default: s.Fatalf("bad call type %v %v", n.Op, n) } @@ -4522,7 +4542,6 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { s.startBlock(bNext) } - res := n.Left.Type.Results() if res.NumFields() == 0 || k != callNormal { // call has no return value. Continue with the next statement. return nil @@ -4918,18 +4937,29 @@ func (s *state) intDivide(n *Node, a, b *ssa.Value) *ssa.Value { func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args ...*ssa.Value) []*ssa.Value { // Write args to the stack off := Ctxt.FixedFrameSize() + var ACArgs []ssa.Param + var ACResults []ssa.Param for _, arg := range args { t := arg.Type off = Rnd(off, t.Alignment()) ptr := s.constOffPtrSP(t.PtrTo(), off) size := t.Size() + ACArgs = append(ACArgs, ssa.Param{Type: t, Offset: int32(off)}) s.store(t, ptr, arg) off += size } off = Rnd(off, int64(Widthreg)) + // Accumulate results types and offsets + offR := off + for _, t := range results { + offR = Rnd(offR, t.Alignment()) + ACResults = append(ACResults, ssa.Param{Type: t, Offset: int32(offR)}) + offR += t.Size() + } + // Issue call - call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn), s.mem()) + call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn, ACArgs, ACResults), s.mem()) s.vars[&memVar] = call if !returns { @@ -5064,8 +5094,9 @@ func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) { } } -func (s *state) storeArg(n *Node, t *types.Type, off int64) { +func (s *state) storeArg(n *Node, t *types.Type, off int64) ssa.Param { s.storeArgWithBase(n, t, s.sp, off) + return ssa.Param{Type: t, Offset: int32(off)} } func (s *state) storeArgWithBase(n *Node, t *types.Type, base *ssa.Value, off int64) { diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules index df70838aa9..39f8cc8889 100644 --- a/src/cmd/compile/internal/ssa/gen/generic.rules +++ b/src/cmd/compile/internal/ssa/gen/generic.rules @@ -2021,8 +2021,8 @@ // Note that (ITab (IMake)) doesn't get // rewritten until after the first opt pass, // so this rule should trigger reliably. -(InterCall [argsize] (Load (OffPtr [off] (ITab (IMake (Addr {itab} (SB)) _))) _) mem) && devirt(v, itab, off) != nil => - (StaticCall [int32(argsize)] {devirt(v, itab, off)} mem) +(InterCall [argsize] {auxCall} (Load (OffPtr [off] (ITab (IMake (Addr {itab} (SB)) _))) _) mem) && devirt(v, auxCall, itab, off) != nil => + (StaticCall [int32(argsize)] {devirt(v, auxCall, itab, off)} mem) // Move and Zero optimizations. // Move source and destination may overlap. diff --git a/src/cmd/compile/internal/ssa/loopreschedchecks.go b/src/cmd/compile/internal/ssa/loopreschedchecks.go index ebd23b34c7..9c73bcff26 100644 --- a/src/cmd/compile/internal/ssa/loopreschedchecks.go +++ b/src/cmd/compile/internal/ssa/loopreschedchecks.go @@ -246,7 +246,7 @@ func insertLoopReschedChecks(f *Func) { // mem1 := call resched (mem0) // goto header resched := f.fe.Syslook("goschedguarded") - mem1 := sched.NewValue1A(bb.Pos, OpStaticCall, types.TypeMem, StaticAuxCall(resched), mem0) + mem1 := sched.NewValue1A(bb.Pos, OpStaticCall, types.TypeMem, StaticAuxCall(resched, nil, nil), mem0) sched.AddEdgeTo(h) headerMemPhi.AddArg(mem1) diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go index c498a288a1..f94399028a 100644 --- a/src/cmd/compile/internal/ssa/op.go +++ b/src/cmd/compile/internal/ssa/op.go @@ -5,6 +5,7 @@ package ssa import ( + "cmd/compile/internal/types" "cmd/internal/obj" "fmt" ) @@ -67,25 +68,60 @@ type regInfo struct { type auxType int8 +type Param struct { + Type *types.Type + Offset int32 // TODO someday this will be a register +} + type AuxCall struct { - Fn *obj.LSym + Fn *obj.LSym + args []Param // Includes receiver for method calls. Does NOT include hidden closure pointer. + results []Param } func (a *AuxCall) String() string { + var fn string if a.Fn == nil { - return "AuxCall(nil)" + fn = "AuxCall{nil" // could be interface/closure etc. + } else { + fn = fmt.Sprintf("AuxCall{%v", a.Fn) + } + + if len(a.args) == 0 { + fn += "()" + } else { + s := "(" + for _, arg := range a.args { + fn += fmt.Sprintf("%s[%v,%v]", s, arg.Type, arg.Offset) + s = "," + } + fn += ")" } - return fmt.Sprintf("AuxCall(%v)", a.Fn) + + if len(a.results) > 0 { // usual is zero or one; only some RT calls have more than one. + if len(a.results) == 1 { + fn += fmt.Sprintf("[%v,%v]", a.results[0].Type, a.results[0].Offset) + } else { + s := "(" + for _, result := range a.results { + fn += fmt.Sprintf("%s[%v,%v]", s, result.Type, result.Offset) + s = "," + } + fn += ")" + } + } + + return fn + "}" } // StaticAuxCall returns an AuxCall for a static call. -func StaticAuxCall(sym *obj.LSym) *AuxCall { - return &AuxCall{Fn: sym} +func StaticAuxCall(sym *obj.LSym, args []Param, results []Param) *AuxCall { + return &AuxCall{Fn: sym, args: args, results: results} } // InterfaceAuxCall returns an AuxCall for an interface call. -func InterfaceAuxCall() *AuxCall { - return &AuxCall{} +func InterfaceAuxCall(args []Param, results []Param) *AuxCall { + return &AuxCall{Fn: nil, args: args, results: results} } // ClosureAuxCall returns an AuxCall for a closure call. diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index eb371ce38b..2ab310ad85 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -743,7 +743,7 @@ func uaddOvf(a, b int64) bool { // de-virtualize an InterCall // 'sym' is the symbol for the itab -func devirt(v *Value, sym Sym, offset int64) *AuxCall { +func devirt(v *Value, aux interface{}, sym Sym, offset int64) *AuxCall { f := v.Block.Func n, ok := sym.(*obj.LSym) if !ok { @@ -760,7 +760,8 @@ func devirt(v *Value, sym Sym, offset int64) *AuxCall { if lsym == nil { return nil } - return StaticAuxCall(lsym) + va := aux.(*AuxCall) + return StaticAuxCall(lsym, va.args, va.results) } // isSamePtr reports whether p1 and p2 point to the same address. diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index 1ecfabf7cb..925ff53fd1 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -8479,11 +8479,12 @@ func rewriteValuegeneric_OpIMake(v *Value) bool { func rewriteValuegeneric_OpInterCall(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - // match: (InterCall [argsize] (Load (OffPtr [off] (ITab (IMake (Addr {itab} (SB)) _))) _) mem) - // cond: devirt(v, itab, off) != nil - // result: (StaticCall [int32(argsize)] {devirt(v, itab, off)} mem) + // match: (InterCall [argsize] {auxCall} (Load (OffPtr [off] (ITab (IMake (Addr {itab} (SB)) _))) _) mem) + // cond: devirt(v, auxCall, itab, off) != nil + // result: (StaticCall [int32(argsize)] {devirt(v, auxCall, itab, off)} mem) for { argsize := auxIntToInt32(v.AuxInt) + auxCall := auxToCall(v.Aux) if v_0.Op != OpLoad { break } @@ -8510,12 +8511,12 @@ func rewriteValuegeneric_OpInterCall(v *Value) bool { break } mem := v_1 - if !(devirt(v, itab, off) != nil) { + if !(devirt(v, auxCall, itab, off) != nil) { break } v.reset(OpStaticCall) v.AuxInt = int32ToAuxInt(int32(argsize)) - v.Aux = callToAux(devirt(v, itab, off)) + v.Aux = callToAux(devirt(v, auxCall, itab, off)) v.AddArg(mem) return true } diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go index 4322a85c90..7cc8bf7af9 100644 --- a/src/cmd/compile/internal/ssa/writebarrier.go +++ b/src/cmd/compile/internal/ssa/writebarrier.go @@ -501,29 +501,33 @@ func wbcall(pos src.XPos, b *Block, fn, typ *obj.LSym, ptr, val, mem, sp, sb *Va // put arguments on stack off := config.ctxt.FixedFrameSize() + var ACArgs []Param if typ != nil { // for typedmemmove taddr := b.NewValue1A(pos, OpAddr, b.Func.Config.Types.Uintptr, typ, sb) off = round(off, taddr.Type.Alignment()) arg := b.NewValue1I(pos, OpOffPtr, taddr.Type.PtrTo(), off, sp) mem = b.NewValue3A(pos, OpStore, types.TypeMem, ptr.Type, arg, taddr, mem) + ACArgs = append(ACArgs, Param{Type: b.Func.Config.Types.Uintptr, Offset: int32(off)}) off += taddr.Type.Size() } off = round(off, ptr.Type.Alignment()) arg := b.NewValue1I(pos, OpOffPtr, ptr.Type.PtrTo(), off, sp) mem = b.NewValue3A(pos, OpStore, types.TypeMem, ptr.Type, arg, ptr, mem) + ACArgs = append(ACArgs, Param{Type: ptr.Type, Offset: int32(off)}) off += ptr.Type.Size() if val != nil { off = round(off, val.Type.Alignment()) arg = b.NewValue1I(pos, OpOffPtr, val.Type.PtrTo(), off, sp) mem = b.NewValue3A(pos, OpStore, types.TypeMem, val.Type, arg, val, mem) + ACArgs = append(ACArgs, Param{Type: val.Type, Offset: int32(off)}) off += val.Type.Size() } off = round(off, config.PtrSize) // issue call - mem = b.NewValue1A(pos, OpStaticCall, types.TypeMem, StaticAuxCall(fn), mem) + mem = b.NewValue1A(pos, OpStaticCall, types.TypeMem, StaticAuxCall(fn, ACArgs, nil), mem) mem.AuxInt = off - config.ctxt.FixedFrameSize() return mem } -- cgit v1.2.3-54-g00ecf From 39da81da5e35e50da74ebb8c4fe12fd363bf41b9 Mon Sep 17 00:00:00 2001 From: David Chase Date: Wed, 24 Jun 2020 17:00:48 -0400 Subject: cmd/compile: populate AuxCall fields for OpClosureCall Change-Id: Ib5f62826d5249c1727b57d9f8ff2f3a1d6dc5032 Reviewed-on: https://go-review.googlesource.com/c/go/+/240185 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 4 ++-- src/cmd/compile/internal/ssa/op.go | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 6c0b027c17..75fdbbae04 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4306,7 +4306,7 @@ func (s *state) openDeferExit() { v := s.load(r.closure.Type.Elem(), r.closure) s.maybeNilCheckClosure(v, callDefer) codeptr := s.rawLoad(types.Types[TUINTPTR], v) - call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(), codeptr, v, s.mem()) + call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(ACArgs, ACResults), codeptr, v, s.mem()) } else { // Do a static call if the original call was a static function or method call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn.Sym.Linksym(), ACArgs, ACResults), s.mem()) @@ -4512,7 +4512,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // critical that we not clobber any arguments already // stored onto the stack. codeptr = s.rawLoad(types.Types[TUINTPTR], closure) - call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(), codeptr, closure, s.mem()) + call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(ACArgs, ACResults), codeptr, closure, s.mem()) case codeptr != nil: call = s.newValue2A(ssa.OpInterCall, types.TypeMem, ssa.InterfaceAuxCall(ACArgs, ACResults), codeptr, s.mem()) case sym != nil: diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go index f94399028a..02ecdef5e6 100644 --- a/src/cmd/compile/internal/ssa/op.go +++ b/src/cmd/compile/internal/ssa/op.go @@ -79,6 +79,10 @@ type AuxCall struct { results []Param } +// String returns +// "AuxCall{()}" if len(results) == 0; +// "AuxCall{()}" if len(results) == 1; +// "AuxCall{()()}" otherwise. func (a *AuxCall) String() string { var fn string if a.Fn == nil { @@ -125,8 +129,8 @@ func InterfaceAuxCall(args []Param, results []Param) *AuxCall { } // ClosureAuxCall returns an AuxCall for a closure call. -func ClosureAuxCall() *AuxCall { - return &AuxCall{} +func ClosureAuxCall(args []Param, results []Param) *AuxCall { + return &AuxCall{Fn: nil, args: args, results: results} } const ( -- cgit v1.2.3-54-g00ecf From 396688af7ee121d478e9b8d2cc9d06999ba7fc6e Mon Sep 17 00:00:00 2001 From: David Chase Date: Fri, 26 Jun 2020 18:19:01 -0400 Subject: cmd/compile: make translation to calls for SSA look more "value-oriented" The existing translation assumes an in-memory return values, thus it returns the address of the result(s). Most consumers immediately load from the address to get the value, and in late call expansion that is the favored idiom, and it is also the favored idiom when arguments and results use registers instead of memory. Change-Id: Ie0ccc70f399682a42509d847b330ef3956462d56 Reviewed-on: https://go-review.googlesource.com/c/go/+/240186 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 44 +++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 75fdbbae04..c59945f206 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -1076,7 +1076,7 @@ func (s *state) stmt(n *Node) { fallthrough case OCALLMETH, OCALLINTER: - s.call(n, callNormal) + s.callAddr(n, callNormal) if n.Op == OCALLFUNC && n.Left.Op == ONAME && n.Left.Class() == PFUNC { if fn := n.Left.Sym.Name; compiling_runtime && fn == "throw" || n.Left.Sym.Pkg == Runtimepkg && (fn == "throwinit" || fn == "gopanic" || fn == "panicwrap" || fn == "block" || fn == "panicmakeslicelen" || fn == "panicmakeslicecap") { @@ -1108,10 +1108,10 @@ func (s *state) stmt(n *Node) { if n.Esc == EscNever { d = callDeferStack } - s.call(n.Left, d) + s.callAddr(n.Left, d) } case OGO: - s.call(n.Left, callGo) + s.callAddr(n.Left, callGo) case OAS2DOTTYPE: res, resok := s.dottype(n.Right, true) @@ -2715,8 +2715,7 @@ func (s *state) expr(n *Node) *ssa.Value { fallthrough case OCALLINTER, OCALLMETH: - a := s.call(n, callNormal) - return s.load(n.Type, a) + return s.callResult(n, callNormal) case OGETG: return s.newValue1(ssa.OpGetG, n.Type, s.mem()) @@ -3589,8 +3588,7 @@ func init() { addF("math", "FMA", func(s *state, n *Node, args []*ssa.Value) *ssa.Value { if !s.config.UseFMA { - a := s.call(n, callNormal) - s.vars[n] = s.load(types.Types[TFLOAT64], a) + s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64] return s.variable(n, types.Types[TFLOAT64]) } v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[TBOOL], x86HasFMA) @@ -3611,8 +3609,7 @@ func init() { // Call the pure Go version. s.startBlock(bFalse) - a := s.call(n, callNormal) - s.vars[n] = s.load(types.Types[TFLOAT64], a) + s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64] s.endBlock().AddEdgeTo(bEnd) // Merge results. @@ -3623,8 +3620,7 @@ func init() { addF("math", "FMA", func(s *state, n *Node, args []*ssa.Value) *ssa.Value { if !s.config.UseFMA { - a := s.call(n, callNormal) - s.vars[n] = s.load(types.Types[TFLOAT64], a) + s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64] return s.variable(n, types.Types[TFLOAT64]) } addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), armHasVFPv4, s.sb) @@ -3646,8 +3642,7 @@ func init() { // Call the pure Go version. s.startBlock(bFalse) - a := s.call(n, callNormal) - s.vars[n] = s.load(types.Types[TFLOAT64], a) + s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64] s.endBlock().AddEdgeTo(bEnd) // Merge results. @@ -3676,8 +3671,7 @@ func init() { // Call the pure Go version. s.startBlock(bFalse) - a := s.call(n, callNormal) - s.vars[n] = s.load(types.Types[TFLOAT64], a) + s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64] s.endBlock().AddEdgeTo(bEnd) // Merge results. @@ -3887,8 +3881,7 @@ func init() { // Call the pure Go version. s.startBlock(bFalse) - a := s.call(n, callNormal) - s.vars[n] = s.load(types.Types[TINT], a) + s.vars[n] = s.callResult(n, callNormal) // types.Types[TINT] s.endBlock().AddEdgeTo(bEnd) // Merge results. @@ -4336,9 +4329,17 @@ func (s *state) openDeferExit() { } } +func (s *state) callResult(n *Node, k callKind) *ssa.Value { + return s.call(n, k, false) +} + +func (s *state) callAddr(n *Node, k callKind) *ssa.Value { + return s.call(n, k, true) +} + // Calls the function n using the specified call type. // Returns the address of the return value (or nil if none). -func (s *state) call(n *Node, k callKind) *ssa.Value { +func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { var sym *types.Sym // target symbol (if static) var closure *ssa.Value // ptr to closure to run (if dynamic) var codeptr *ssa.Value // ptr to target code (if dynamic) @@ -4547,7 +4548,10 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { return nil } fp := res.Field(0) - return s.constOffPtrSP(types.NewPtr(fp.Type), fp.Offset+Ctxt.FixedFrameSize()) + if returnResultAddr { + return s.constOffPtrSP(types.NewPtr(fp.Type), fp.Offset+Ctxt.FixedFrameSize()) + } + return s.load(n.Type, s.constOffPtrSP(types.NewPtr(fp.Type), fp.Offset+Ctxt.FixedFrameSize())) } // maybeNilCheckClosure checks if a nil check of a closure is needed in some @@ -4676,7 +4680,7 @@ func (s *state) addr(n *Node) *ssa.Value { addr := s.addr(n.Left) return s.newValue1(ssa.OpCopy, t, addr) // ensure that addr has the right type case OCALLFUNC, OCALLINTER, OCALLMETH: - return s.call(n, callNormal) + return s.callAddr(n, callNormal) case ODOTTYPE: v, _ := s.dottype(n, false) if v.Op != ssa.OpLoad { -- cgit v1.2.3-54-g00ecf From f5d59d0e382dc59195537a128fe9423a49a4cea8 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 16 Sep 2020 15:41:47 -0700 Subject: cmd/compile: skip looking for OCLOSURE nodes in xtop xtop holds package's top-level declaration statements, but OCLOSURE only appears in expression contexts. xtop will instead hold the synthetic ODCLFUNC representing OCLOSURE's function body. This CL makes the loop consistent with the later phases that only look for ODCLFUNC nodes in xtop. Passes toolstash-check. Change-Id: I852a10ef1bf75bb3351e3da0357ca8b2e26aec6e Reviewed-on: https://go-review.googlesource.com/c/go/+/255340 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky Reviewed-by: Cuong Manh Le TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 8783cb4e46..7ad3bfe0c8 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -617,7 +617,7 @@ func Main(archInit func(*Arch)) { var fcount int64 for i := 0; i < len(xtop); i++ { n := xtop[i] - if op := n.Op; op == ODCLFUNC || op == OCLOSURE { + if n.Op == ODCLFUNC { Curfn = n decldepth = 1 saveerrors() -- cgit v1.2.3-54-g00ecf From 7f24142b7b289df7e98ed3e1ccd673824dd1d0ee Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 16 Sep 2020 22:15:13 +0200 Subject: syscall, cmd/go/internal/lockedfile/internal/filelock: add and use Flock on illumos Copy the syscall wrapper from golang.org/x/sys/unix CL 255377 to provide Flock on illumos and switch cmd/go/internal/lockedfile/internal/filelock to use it. Fixes #35618 Change-Id: I876a2b782329a988fa85361fb1ea58eb6f329af1 Reviewed-on: https://go-review.googlesource.com/c/go/+/255258 Trust: Tobias Klauser Run-TryBot: Tobias Klauser TryBot-Result: Go Bot Reviewed-by: Bryan C. Mills --- .../lockedfile/internal/filelock/filelock_fcntl.go | 2 +- .../lockedfile/internal/filelock/filelock_test.go | 2 +- .../lockedfile/internal/filelock/filelock_unix.go | 2 +- src/syscall/syscall_illumos.go | 25 ++++++++++++++++++++++ src/syscall/types_illumos_amd64.go | 17 +++++++++++++++ 5 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 src/syscall/syscall_illumos.go create mode 100644 src/syscall/types_illumos_amd64.go diff --git a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go index c60a78ed92..dc7bbe263f 100644 --- a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go +++ b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix solaris +// +build aix solaris,!illumos // This code implements the filelock API using POSIX 'fcntl' locks, which attach // to an (inode, process) pair rather than a file descriptor. To avoid unlocking diff --git a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_test.go b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_test.go index faf73446f7..8301fb6b6e 100644 --- a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_test.go +++ b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_test.go @@ -161,7 +161,7 @@ func TestRLockExcludesOnlyLock(t *testing.T) { doUnlockTF := false switch runtime.GOOS { - case "aix", "illumos", "solaris": + case "aix", "solaris": // When using POSIX locks (as on Solaris), we can't safely read-lock the // same inode through two different descriptors at the same time: when the // first descriptor is closed, the second descriptor would still be open but diff --git a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_unix.go b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_unix.go index 00c4262832..78f2c51129 100644 --- a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_unix.go +++ b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd illumos linux netbsd openbsd package filelock diff --git a/src/syscall/syscall_illumos.go b/src/syscall/syscall_illumos.go new file mode 100644 index 0000000000..1484337e1b --- /dev/null +++ b/src/syscall/syscall_illumos.go @@ -0,0 +1,25 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build illumos + +// Illumos system calls not present on Solaris. + +package syscall + +import "unsafe" + +//go:cgo_import_dynamic libc_flock flock "libc.so" + +//go:linkname procFlock libc_flock + +var procFlock libcFunc + +func Flock(fd int, how int) error { + _, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procFlock)), 2, uintptr(fd), uintptr(how), 0, 0, 0, 0) + if errno != 0 { + return errno + } + return nil +} diff --git a/src/syscall/types_illumos_amd64.go b/src/syscall/types_illumos_amd64.go new file mode 100644 index 0000000000..abb282f3e4 --- /dev/null +++ b/src/syscall/types_illumos_amd64.go @@ -0,0 +1,17 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build illumos + +// Illumos consts not present on Solaris. These are added manually rather than +// auto-generated by mkerror.sh + +package syscall + +const ( + LOCK_EX = 0x2 + LOCK_NB = 0x4 + LOCK_SH = 0x1 + LOCK_UN = 0x8 +) -- cgit v1.2.3-54-g00ecf From 0dde60a5fefcb1447c97efa5c7bb4dbcf3575736 Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Wed, 19 Aug 2020 03:07:26 +1000 Subject: cmd/internal/obj/riscv: clean up lowerJALR This cleans up the last of the direct obj.Prog rewriting, removing lowerJALR and replacing it with correct handling for AJALR during instruction encoding. Change-Id: Ieea125bde30d4c0edd2d9ed1e50160543aa8f330 Reviewed-on: https://go-review.googlesource.com/c/go/+/249077 Reviewed-by: Cherry Zhang Run-TryBot: Cherry Zhang TryBot-Result: Go Bot Trust: Joel Sing --- src/cmd/internal/obj/riscv/obj.go | 43 ++++++++------------------------------- 1 file changed, 9 insertions(+), 34 deletions(-) diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go index 77d383b290..841b30d85c 100644 --- a/src/cmd/internal/obj/riscv/obj.go +++ b/src/cmd/internal/obj/riscv/obj.go @@ -58,30 +58,14 @@ func jalrToSym(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc, lr int16) *ob p.As = AJALR p.From.Type = obj.TYPE_REG p.From.Reg = lr - p.From.Sym = to.Sym p.Reg = 0 p.To.Type = obj.TYPE_REG p.To.Reg = REG_TMP - lowerJALR(p) + p.To.Sym = to.Sym return p } -// lowerJALR normalizes a JALR instruction. -func lowerJALR(p *obj.Prog) { - if p.As != AJALR { - panic("lowerJALR: not a JALR") - } - - // JALR gets parsed like JAL - the linkage pointer goes in From, - // and the target is in To. However, we need to assemble it as an - // I-type instruction, so place the linkage pointer in To, the - // target register in Reg, and the offset in From. - p.Reg = p.To.Reg - p.From, p.To = p.To, p.From - p.From.Type, p.From.Reg = obj.TYPE_CONST, obj.REG_NONE -} - // progedit is called individually for each *obj.Prog. It normalizes instruction // formats and eliminates as many pseudo-instructions as possible. func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { @@ -125,7 +109,6 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { switch p.As { case obj.AJMP: // Turn JMP into JAL ZERO or JALR ZERO. - // p.From is actually an _output_ for this instruction. p.From.Type = obj.TYPE_REG p.From.Reg = REG_ZERO @@ -136,7 +119,6 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { switch p.To.Name { case obj.NAME_NONE: p.As = AJALR - lowerJALR(p) case obj.NAME_EXTERN: // Handled in preprocess. default: @@ -154,14 +136,10 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { p.As = AJALR p.From.Type = obj.TYPE_REG p.From.Reg = REG_LR - lowerJALR(p) default: ctxt.Diag("unknown destination type %+v in CALL: %v", p.To.Type, p) } - case AJALR: - lowerJALR(p) - case obj.AUNDEF: p.As = AEBREAK @@ -454,7 +432,7 @@ func containsCall(sym *obj.LSym) bool { case obj.ACALL: return true case AJAL, AJALR: - if p.To.Type == obj.TYPE_REG && p.To.Reg == REG_LR { + if p.From.Type == obj.TYPE_REG && p.From.Reg == REG_LR { return true } } @@ -731,11 +709,9 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p = jalrToSym(ctxt, p, newprog, REG_ZERO) } else { p.As = AJALR - p.From.Type = obj.TYPE_CONST - p.From.Offset = 0 - p.Reg = REG_LR - p.To.Type = obj.TYPE_REG - p.To.Reg = REG_ZERO + p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_ZERO} + p.Reg = 0 + p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR} } // "Add back" the stack removed in the previous instruction. @@ -917,9 +893,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // it is reserved by SSA. jmp := obj.Appendp(p, newprog) jmp.As = AJALR - jmp.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0} - jmp.To = p.From - jmp.Reg = REG_TMP + jmp.From = p.From + jmp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP} // p.From is not generally valid, however will be // fixed up in the next loop. @@ -1801,8 +1776,8 @@ func instructionsForProg(p *obj.Prog) []*instruction { inss := []*instruction{ins} switch ins.as { - case AJAL: - ins.rd, ins.rs2 = uint32(p.From.Reg), obj.REG_NONE + case AJAL, AJALR: + ins.rd, ins.rs1, ins.rs2 = uint32(p.From.Reg), uint32(p.To.Reg), obj.REG_NONE ins.imm = p.To.Offset case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ: -- cgit v1.2.3-54-g00ecf From 967465da2975fe4322080703ce5a77ea90752829 Mon Sep 17 00:00:00 2001 From: Lynn Boger Date: Mon, 31 Aug 2020 09:43:40 -0400 Subject: cmd/compile: use combined shifts to improve array addressing on ppc64x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change adds rules to find pairs of instructions that can be combined into a single shifts. These instruction sequences are common in array addressing within loops. Improvements can be seen in many crypto packages and the hash packages. These are based on the extended mnemonics found in the ISA sections C.8.1 and C.8.2. Some rules in PPC64.rules were moved because the ordering prevented some matching. The following results were generated on power9. hash/crc32: CRC32/poly=Koopman/size=40/align=0 195ns ± 0% 163ns ± 0% -16.41% CRC32/poly=Koopman/size=40/align=1 200ns ± 0% 163ns ± 0% -18.50% CRC32/poly=Koopman/size=512/align=0 1.98µs ± 0% 1.67µs ± 0% -15.46% CRC32/poly=Koopman/size=512/align=1 1.98µs ± 0% 1.69µs ± 0% -14.80% CRC32/poly=Koopman/size=1kB/align=0 3.90µs ± 0% 3.31µs ± 0% -15.27% CRC32/poly=Koopman/size=1kB/align=1 3.85µs ± 0% 3.31µs ± 0% -14.15% CRC32/poly=Koopman/size=4kB/align=0 15.3µs ± 0% 13.1µs ± 0% -14.22% CRC32/poly=Koopman/size=4kB/align=1 15.4µs ± 0% 13.1µs ± 0% -14.79% CRC32/poly=Koopman/size=32kB/align=0 137µs ± 0% 105µs ± 0% -23.56% CRC32/poly=Koopman/size=32kB/align=1 137µs ± 0% 105µs ± 0% -23.53% crypto/rc4: RC4_128 733ns ± 0% 650ns ± 0% -11.32% (p=1.000 n=1+1) RC4_1K 5.80µs ± 0% 5.17µs ± 0% -10.89% (p=1.000 n=1+1) RC4_8K 45.7µs ± 0% 40.8µs ± 0% -10.73% (p=1.000 n=1+1) crypto/sha1: Hash8Bytes 635ns ± 0% 613ns ± 0% -3.46% (p=1.000 n=1+1) Hash320Bytes 2.30µs ± 0% 2.18µs ± 0% -5.38% (p=1.000 n=1+1) Hash1K 5.88µs ± 0% 5.38µs ± 0% -8.62% (p=1.000 n=1+1) Hash8K 42.0µs ± 0% 37.9µs ± 0% -9.75% (p=1.000 n=1+1) There are other improvements found in golang.org/x/crypto which are all in the range of 5-15%. Change-Id: I193471fbcf674151ffe2edab212799d9b08dfb8c Reviewed-on: https://go-review.googlesource.com/c/go/+/252097 Trust: Lynn Boger Run-TryBot: Lynn Boger TryBot-Result: Go Bot Reviewed-by: Carlos Eduardo Seo --- src/cmd/asm/internal/asm/testdata/ppc64enc.s | 4 + src/cmd/compile/internal/ppc64/ssa.go | 36 ++ src/cmd/compile/internal/ssa/gen/PPC64.rules | 63 ++- src/cmd/compile/internal/ssa/gen/PPC64Ops.go | 5 + src/cmd/compile/internal/ssa/opGen.go | 45 ++ src/cmd/compile/internal/ssa/rewrite.go | 38 ++ src/cmd/compile/internal/ssa/rewritePPC64.go | 787 +++++++++++++++++++++++++++ src/cmd/internal/obj/ppc64/a.out.go | 4 + src/cmd/internal/obj/ppc64/anames.go | 4 + src/cmd/internal/obj/ppc64/asm9.go | 74 ++- test/codegen/shift.go | 55 ++ 11 files changed, 1087 insertions(+), 28 deletions(-) diff --git a/src/cmd/asm/internal/asm/testdata/ppc64enc.s b/src/cmd/asm/internal/asm/testdata/ppc64enc.s index 10a05ec402..e26f6f8933 100644 --- a/src/cmd/asm/internal/asm/testdata/ppc64enc.s +++ b/src/cmd/asm/internal/asm/testdata/ppc64enc.s @@ -284,6 +284,10 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 RLDICLCC $0, R4, $15, R6 // 788603c1 RLDICR $0, R4, $15, R6 // 788603c4 RLDICRCC $0, R4, $15, R6 // 788603c5 + RLDIC $0, R4, $15, R6 // 788603c8 + RLDICCC $0, R4, $15, R6 // 788603c9 + CLRLSLWI $16, R5, $8, R4 // 54a4861e + CLRLSLDI $2, R4, $24, R3 // 78831588 BEQ 0(PC) // 41820000 BGE 0(PC) // 40800000 diff --git a/src/cmd/compile/internal/ppc64/ssa.go b/src/cmd/compile/internal/ppc64/ssa.go index f8d9ac2379..4a83a0bdd7 100644 --- a/src/cmd/compile/internal/ppc64/ssa.go +++ b/src/cmd/compile/internal/ppc64/ssa.go @@ -565,6 +565,42 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p = s.Prog(obj.ANOP) gc.Patch(pbover, p) + case ssa.OpPPC64CLRLSLWI: + r := v.Reg() + r1 := v.Args[0].Reg() + shifts := v.AuxInt + p := s.Prog(v.Op.Asm()) + // clrlslwi ra,rs,sh,mb will become rlwinm ra,rs,sh,mb-sh,31-n as described in ISA + p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: ssa.GetPPC64Shiftsh(shifts)} + p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: ssa.GetPPC64Shiftmb(shifts)}) + p.Reg = r1 + p.To.Type = obj.TYPE_REG + p.To.Reg = r + + case ssa.OpPPC64CLRLSLDI: + r := v.Reg() + r1 := v.Args[0].Reg() + shifts := v.AuxInt + p := s.Prog(v.Op.Asm()) + // clrlsldi ra,rs,sh,mb will become rldic ra,rs,sh,mb-sh + p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: ssa.GetPPC64Shiftsh(shifts)} + p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: ssa.GetPPC64Shiftmb(shifts)}) + p.Reg = r1 + p.To.Type = obj.TYPE_REG + p.To.Reg = r + + // Mask has been set as sh + case ssa.OpPPC64RLDICL: + r := v.Reg() + r1 := v.Args[0].Reg() + shifts := v.AuxInt + p := s.Prog(v.Op.Asm()) + p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: ssa.GetPPC64Shiftsh(shifts)} + p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: ssa.GetPPC64Shiftmb(shifts)}) + p.Reg = r1 + p.To.Type = obj.TYPE_REG + p.To.Reg = r + case ssa.OpPPC64ADD, ssa.OpPPC64FADD, ssa.OpPPC64FADDS, ssa.OpPPC64SUB, ssa.OpPPC64FSUB, ssa.OpPPC64FSUBS, ssa.OpPPC64MULLD, ssa.OpPPC64MULLW, ssa.OpPPC64DIVDU, ssa.OpPPC64DIVWU, ssa.OpPPC64SRAD, ssa.OpPPC64SRAW, ssa.OpPPC64SRD, ssa.OpPPC64SRW, ssa.OpPPC64SLD, ssa.OpPPC64SLW, diff --git a/src/cmd/compile/internal/ssa/gen/PPC64.rules b/src/cmd/compile/internal/ssa/gen/PPC64.rules index e5fb1e98c2..774d5096de 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64.rules +++ b/src/cmd/compile/internal/ssa/gen/PPC64.rules @@ -79,6 +79,23 @@ (Abs ...) => (FABS ...) (FMA ...) => (FMADD ...) +// Lowering extension +// Note: we always extend to 64 bits even though some ops don't need that many result bits. +(SignExt8to(16|32|64) ...) => (MOVBreg ...) +(SignExt16to(32|64) ...) => (MOVHreg ...) +(SignExt32to64 ...) => (MOVWreg ...) + +(ZeroExt8to(16|32|64) ...) => (MOVBZreg ...) +(ZeroExt16to(32|64) ...) => (MOVHZreg ...) +(ZeroExt32to64 ...) => (MOVWZreg ...) + +(Trunc(16|32|64)to8 x) && isSigned(t) => (MOVBreg x) +(Trunc(16|32|64)to8 x) => (MOVBZreg x) +(Trunc(32|64)to16 x) && isSigned(t) => (MOVHreg x) +(Trunc(32|64)to16 x) => (MOVHZreg x) +(Trunc64to32 x) && isSigned(t) => (MOVWreg x) +(Trunc64to32 x) => (MOVWZreg x) + // Lowering constants (Const(64|32|16|8) [val]) => (MOVDconst [int64(val)]) (Const(32|64)F ...) => (FMOV(S|D)const ...) @@ -780,6 +797,21 @@ (MOVWreg y:(MOVWZreg x)) => (MOVWreg x) (MOVWZreg y:(MOVWreg x)) => (MOVWZreg x) +// Truncate then logical then truncate: omit first, lesser or equal truncate +(MOVWZreg ((OR|XOR|AND) x (MOVWZreg y))) => (MOVWZreg ((OR|XOR|AND) x y)) +(MOVHZreg ((OR|XOR|AND) x (MOVWZreg y))) => (MOVHZreg ((OR|XOR|AND) x y)) +(MOVHZreg ((OR|XOR|AND) x (MOVHZreg y))) => (MOVHZreg ((OR|XOR|AND) x y)) +(MOVBZreg ((OR|XOR|AND) x (MOVWZreg y))) => (MOVBZreg ((OR|XOR|AND) x y)) +(MOVBZreg ((OR|XOR|AND) x (MOVHZreg y))) => (MOVBZreg ((OR|XOR|AND) x y)) +(MOVBZreg ((OR|XOR|AND) x (MOVBZreg y))) => (MOVBZreg ((OR|XOR|AND) x y)) + +(MOV(B|H|W)Zreg z:(ANDconst [c] (MOVBZload ptr x))) => z +(MOVBZreg z:(AND y (MOVBZload ptr x))) => z +(MOV(H|W)Zreg z:(ANDconst [c] (MOVHZload ptr x))) => z +(MOVHZreg z:(AND y (MOVHZload ptr x))) => z +(MOVWZreg z:(ANDconst [c] (MOVWZload ptr x))) => z +(MOVWZreg z:(AND y (MOVWZload ptr x))) => z + // Arithmetic constant ops (ADD x (MOVDconst [c])) && is32Bit(c) => (ADDconst [c] x) @@ -949,23 +981,6 @@ (AtomicAnd8 ...) => (LoweredAtomicAnd8 ...) (AtomicOr8 ...) => (LoweredAtomicOr8 ...) -// Lowering extension -// Note: we always extend to 64 bits even though some ops don't need that many result bits. -(SignExt8to(16|32|64) ...) => (MOVBreg ...) -(SignExt16to(32|64) ...) => (MOVHreg ...) -(SignExt32to64 ...) => (MOVWreg ...) - -(ZeroExt8to(16|32|64) ...) => (MOVBZreg ...) -(ZeroExt16to(32|64) ...) => (MOVHZreg ...) -(ZeroExt32to64 ...) => (MOVWZreg ...) - -(Trunc(16|32|64)to8 x) && isSigned(t) => (MOVBreg x) -(Trunc(16|32|64)to8 x) => (MOVBZreg x) -(Trunc(32|64)to16 x) && isSigned(t) => (MOVHreg x) -(Trunc(32|64)to16 x) => (MOVHZreg x) -(Trunc64to32 x) && isSigned(t) => (MOVWreg x) -(Trunc64to32 x) => (MOVWZreg x) - (Slicemask x) => (SRADconst (NEG x) [63]) // Note that MOV??reg returns a 64-bit int, x is not necessarily that wide @@ -996,6 +1011,20 @@ (MOVWreg (MOVDconst [c])) => (MOVDconst [int64(int32(c))]) (MOVWZreg (MOVDconst [c])) => (MOVDconst [int64(uint32(c))]) +// Implement clrsldi and clrslwi extended mnemonics as described in +// ISA 3.0 section C.8. AuxInt field contains values needed for +// the instructions, packed together since there is only one available. +(SLDconst [c] z:(MOVBZreg x)) && c < 8 && z.Uses == 1 => (CLRLSLDI [newPPC64ShiftAuxInt(c,56,63,64)] x) +(SLDconst [c] z:(MOVHZreg x)) && c < 16 && z.Uses == 1 => (CLRLSLDI [newPPC64ShiftAuxInt(c,48,63,64)] x) +(SLDconst [c] z:(MOVWZreg x)) && c < 32 && z.Uses == 1 => (CLRLSLDI [newPPC64ShiftAuxInt(c,32,63,64)] x) + +(SLDconst [c] z:(ANDconst [d] x)) && z.Uses == 1 && isPPC64ValidShiftMask(d) => (CLRLSLDI [newPPC64ShiftAuxInt(c,64-getPPC64ShiftMaskLength(d),63,64)] x) +(SLDconst [c] z:(AND (MOVDconst [d]) x)) && z.Uses == 1 && isPPC64ValidShiftMask(d) => (CLRLSLDI [newPPC64ShiftAuxInt(c,64-getPPC64ShiftMaskLength(d),63,64)] x) +(SLWconst [c] z:(MOVBZreg x)) && z.Uses == 1 && c < 8 => (CLRLSLWI [newPPC64ShiftAuxInt(c,24,31,32)] x) +(SLWconst [c] z:(MOVHZreg x)) && z.Uses == 1 && c < 16 => (CLRLSLWI [newPPC64ShiftAuxInt(c,16,31,32)] x) +(SLWconst [c] z:(MOVWZreg x)) && z.Uses == 1 && c < 24 => (CLRLSLWI [newPPC64ShiftAuxInt(c,8,31,32)] x) +(SLWconst [c] z:(ANDconst [d] x)) && z.Uses == 1 && isPPC64ValidShiftMask(d) => (CLRLSLWI [newPPC64ShiftAuxInt(c,32-getPPC64ShiftMaskLength(d),31,32)] x) +(SLWconst [c] z:(AND (MOVDconst [d]) x)) && z.Uses == 1 && isPPC64ValidShiftMask(d) => (CLRLSLWI [newPPC64ShiftAuxInt(c,32-getPPC64ShiftMaskLength(d),31,32)] x) // Lose widening ops fed to stores (MOVBstore [off] {sym} ptr (MOV(B|BZ|H|HZ|W|WZ)reg x) mem) => (MOVBstore [off] {sym} ptr x mem) diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go index 37706b2dd9..ed99c40cd2 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go @@ -206,6 +206,11 @@ func init() { {name: "ROTL", argLength: 2, reg: gp21, asm: "ROTL"}, // arg0 rotate left by arg1 mod 64 {name: "ROTLW", argLength: 2, reg: gp21, asm: "ROTLW"}, // uint32(arg0) rotate left by arg1 mod 32 + // The following are ops to implement the extended mnemonics for shifts as described in section C.8 of the ISA. + // The constant shift values are packed into the aux int32. + {name: "RLDICL", argLength: 1, reg: gp11, asm: "RLDICL", aux: "Int32"}, // arg0 extract bits identified by shift params" + {name: "CLRLSLWI", argLength: 1, reg: gp11, asm: "CLRLSLWI", aux: "Int32"}, // + {name: "CLRLSLDI", argLength: 1, reg: gp11, asm: "CLRLSLDI", aux: "Int32"}, // {name: "LoweredAdd64Carry", argLength: 3, reg: gp32, resultNotInArgs: true}, // arg0 + arg1 + carry, returns (sum, carry) diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index d95943231a..f00dc3f7f5 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -1853,6 +1853,9 @@ const ( OpPPC64SLW OpPPC64ROTL OpPPC64ROTLW + OpPPC64RLDICL + OpPPC64CLRLSLWI + OpPPC64CLRLSLDI OpPPC64LoweredAdd64Carry OpPPC64SRADconst OpPPC64SRAWconst @@ -24672,6 +24675,48 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "RLDICL", + auxType: auxInt32, + argLen: 1, + asm: ppc64.ARLDICL, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + outputs: []outputInfo{ + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, + { + name: "CLRLSLWI", + auxType: auxInt32, + argLen: 1, + asm: ppc64.ACLRLSLWI, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + outputs: []outputInfo{ + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, + { + name: "CLRLSLDI", + auxType: auxInt32, + argLen: 1, + asm: ppc64.ACLRLSLDI, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + outputs: []outputInfo{ + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, { name: "LoweredAdd64Carry", argLen: 3, diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index 2ab310ad85..d9c3e455a0 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -1325,6 +1325,44 @@ func hasSmallRotate(c *Config) bool { } } +func newPPC64ShiftAuxInt(sh, mb, me, sz int64) int32 { + if sh < 0 || sh >= sz { + panic("PPC64 shift arg sh out of range") + } + if mb < 0 || mb >= sz { + panic("PPC64 shift arg mb out of range") + } + if me < 0 || me >= sz { + panic("PPC64 shift arg me out of range") + } + return int32(sh<<16 | mb<<8 | me) +} + +func GetPPC64Shiftsh(auxint int64) int64 { + return int64(int8(auxint >> 16)) +} + +func GetPPC64Shiftmb(auxint int64) int64 { + return int64(int8(auxint >> 8)) +} + +func GetPPC64Shiftme(auxint int64) int64 { + return int64(int8(auxint)) +} + +// Catch the simple ones first +// TODO: Later catch more cases +func isPPC64ValidShiftMask(v int64) bool { + if ((v + 1) & v) == 0 { + return true + } + return false +} + +func getPPC64ShiftMaskLength(v int64) int64 { + return int64(bits.Len64(uint64(v))) +} + // encodes the lsb and width for arm(64) bitfield ops into the expected auxInt format. func armBFAuxInt(lsb, width int64) arm64BitField { if lsb < 0 || lsb > 63 { diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go index 152cdfdf4d..12b08824b5 100644 --- a/src/cmd/compile/internal/ssa/rewritePPC64.go +++ b/src/cmd/compile/internal/ssa/rewritePPC64.go @@ -586,8 +586,12 @@ func rewriteValuePPC64(v *Value) bool { return rewriteValuePPC64_OpPPC64ROTLW(v) case OpPPC64SLD: return rewriteValuePPC64_OpPPC64SLD(v) + case OpPPC64SLDconst: + return rewriteValuePPC64_OpPPC64SLDconst(v) case OpPPC64SLW: return rewriteValuePPC64_OpPPC64SLW(v) + case OpPPC64SLWconst: + return rewriteValuePPC64_OpPPC64SLWconst(v) case OpPPC64SRAD: return rewriteValuePPC64_OpPPC64SRAD(v) case OpPPC64SRAW: @@ -6565,6 +6569,255 @@ func rewriteValuePPC64_OpPPC64MOVBZreg(v *Value) bool { v.AddArg(x) return true } + // match: (MOVBZreg (OR x (MOVWZreg y))) + // result: (MOVBZreg (OR x y)) + for { + if v_0.Op != OpPPC64OR { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVWZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVBZreg) + v0 := b.NewValue0(v.Pos, OpPPC64OR, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVBZreg (XOR x (MOVWZreg y))) + // result: (MOVBZreg (XOR x y)) + for { + if v_0.Op != OpPPC64XOR { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVWZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVBZreg) + v0 := b.NewValue0(v.Pos, OpPPC64XOR, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVBZreg (AND x (MOVWZreg y))) + // result: (MOVBZreg (AND x y)) + for { + if v_0.Op != OpPPC64AND { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVWZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVBZreg) + v0 := b.NewValue0(v.Pos, OpPPC64AND, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVBZreg (OR x (MOVHZreg y))) + // result: (MOVBZreg (OR x y)) + for { + if v_0.Op != OpPPC64OR { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVHZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVBZreg) + v0 := b.NewValue0(v.Pos, OpPPC64OR, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVBZreg (XOR x (MOVHZreg y))) + // result: (MOVBZreg (XOR x y)) + for { + if v_0.Op != OpPPC64XOR { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVHZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVBZreg) + v0 := b.NewValue0(v.Pos, OpPPC64XOR, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVBZreg (AND x (MOVHZreg y))) + // result: (MOVBZreg (AND x y)) + for { + if v_0.Op != OpPPC64AND { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVHZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVBZreg) + v0 := b.NewValue0(v.Pos, OpPPC64AND, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVBZreg (OR x (MOVBZreg y))) + // result: (MOVBZreg (OR x y)) + for { + if v_0.Op != OpPPC64OR { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVBZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVBZreg) + v0 := b.NewValue0(v.Pos, OpPPC64OR, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVBZreg (XOR x (MOVBZreg y))) + // result: (MOVBZreg (XOR x y)) + for { + if v_0.Op != OpPPC64XOR { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVBZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVBZreg) + v0 := b.NewValue0(v.Pos, OpPPC64XOR, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVBZreg (AND x (MOVBZreg y))) + // result: (MOVBZreg (AND x y)) + for { + if v_0.Op != OpPPC64AND { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVBZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVBZreg) + v0 := b.NewValue0(v.Pos, OpPPC64AND, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVBZreg z:(ANDconst [c] (MOVBZload ptr x))) + // result: z + for { + z := v_0 + if z.Op != OpPPC64ANDconst { + break + } + z_0 := z.Args[0] + if z_0.Op != OpPPC64MOVBZload { + break + } + v.copyOf(z) + return true + } + // match: (MOVBZreg z:(AND y (MOVBZload ptr x))) + // result: z + for { + z := v_0 + if z.Op != OpPPC64AND { + break + } + _ = z.Args[1] + z_0 := z.Args[0] + z_1 := z.Args[1] + for _i0 := 0; _i0 <= 1; _i0, z_0, z_1 = _i0+1, z_1, z_0 { + if z_1.Op != OpPPC64MOVBZload { + continue + } + v.copyOf(z) + return true + } + break + } // match: (MOVBZreg x:(MOVBZload _ _)) // result: x for { @@ -8507,6 +8760,197 @@ func rewriteValuePPC64_OpPPC64MOVHZreg(v *Value) bool { v.AddArg(x) return true } + // match: (MOVHZreg (OR x (MOVWZreg y))) + // result: (MOVHZreg (OR x y)) + for { + if v_0.Op != OpPPC64OR { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVWZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVHZreg) + v0 := b.NewValue0(v.Pos, OpPPC64OR, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVHZreg (XOR x (MOVWZreg y))) + // result: (MOVHZreg (XOR x y)) + for { + if v_0.Op != OpPPC64XOR { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVWZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVHZreg) + v0 := b.NewValue0(v.Pos, OpPPC64XOR, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVHZreg (AND x (MOVWZreg y))) + // result: (MOVHZreg (AND x y)) + for { + if v_0.Op != OpPPC64AND { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVWZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVHZreg) + v0 := b.NewValue0(v.Pos, OpPPC64AND, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVHZreg (OR x (MOVHZreg y))) + // result: (MOVHZreg (OR x y)) + for { + if v_0.Op != OpPPC64OR { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVHZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVHZreg) + v0 := b.NewValue0(v.Pos, OpPPC64OR, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVHZreg (XOR x (MOVHZreg y))) + // result: (MOVHZreg (XOR x y)) + for { + if v_0.Op != OpPPC64XOR { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVHZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVHZreg) + v0 := b.NewValue0(v.Pos, OpPPC64XOR, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVHZreg (AND x (MOVHZreg y))) + // result: (MOVHZreg (AND x y)) + for { + if v_0.Op != OpPPC64AND { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVHZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVHZreg) + v0 := b.NewValue0(v.Pos, OpPPC64AND, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVHZreg z:(ANDconst [c] (MOVBZload ptr x))) + // result: z + for { + z := v_0 + if z.Op != OpPPC64ANDconst { + break + } + z_0 := z.Args[0] + if z_0.Op != OpPPC64MOVBZload { + break + } + v.copyOf(z) + return true + } + // match: (MOVHZreg z:(ANDconst [c] (MOVHZload ptr x))) + // result: z + for { + z := v_0 + if z.Op != OpPPC64ANDconst { + break + } + z_0 := z.Args[0] + if z_0.Op != OpPPC64MOVHZload { + break + } + v.copyOf(z) + return true + } + // match: (MOVHZreg z:(AND y (MOVHZload ptr x))) + // result: z + for { + z := v_0 + if z.Op != OpPPC64AND { + break + } + _ = z.Args[1] + z_0 := z.Args[0] + z_1 := z.Args[1] + for _i0 := 0; _i0 <= 1; _i0, z_0, z_1 = _i0+1, z_1, z_0 { + if z_1.Op != OpPPC64MOVHZload { + continue + } + v.copyOf(z) + return true + } + break + } // match: (MOVHZreg x:(MOVBZload _ _)) // result: x for { @@ -9657,6 +10101,139 @@ func rewriteValuePPC64_OpPPC64MOVWZreg(v *Value) bool { v.AddArg(x) return true } + // match: (MOVWZreg (OR x (MOVWZreg y))) + // result: (MOVWZreg (OR x y)) + for { + if v_0.Op != OpPPC64OR { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVWZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVWZreg) + v0 := b.NewValue0(v.Pos, OpPPC64OR, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVWZreg (XOR x (MOVWZreg y))) + // result: (MOVWZreg (XOR x y)) + for { + if v_0.Op != OpPPC64XOR { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVWZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVWZreg) + v0 := b.NewValue0(v.Pos, OpPPC64XOR, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVWZreg (AND x (MOVWZreg y))) + // result: (MOVWZreg (AND x y)) + for { + if v_0.Op != OpPPC64AND { + break + } + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpPPC64MOVWZreg { + continue + } + y := v_0_1.Args[0] + v.reset(OpPPC64MOVWZreg) + v0 := b.NewValue0(v.Pos, OpPPC64AND, t) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (MOVWZreg z:(ANDconst [c] (MOVBZload ptr x))) + // result: z + for { + z := v_0 + if z.Op != OpPPC64ANDconst { + break + } + z_0 := z.Args[0] + if z_0.Op != OpPPC64MOVBZload { + break + } + v.copyOf(z) + return true + } + // match: (MOVWZreg z:(ANDconst [c] (MOVHZload ptr x))) + // result: z + for { + z := v_0 + if z.Op != OpPPC64ANDconst { + break + } + z_0 := z.Args[0] + if z_0.Op != OpPPC64MOVHZload { + break + } + v.copyOf(z) + return true + } + // match: (MOVWZreg z:(ANDconst [c] (MOVWZload ptr x))) + // result: z + for { + z := v_0 + if z.Op != OpPPC64ANDconst { + break + } + z_0 := z.Args[0] + if z_0.Op != OpPPC64MOVWZload { + break + } + v.copyOf(z) + return true + } + // match: (MOVWZreg z:(AND y (MOVWZload ptr x))) + // result: z + for { + z := v_0 + if z.Op != OpPPC64AND { + break + } + _ = z.Args[1] + z_0 := z.Args[0] + z_1 := z.Args[1] + for _i0 := 0; _i0 <= 1; _i0, z_0, z_1 = _i0+1, z_1, z_0 { + if z_1.Op != OpPPC64MOVWZload { + continue + } + v.copyOf(z) + return true + } + break + } // match: (MOVWZreg x:(MOVBZload _ _)) // result: x for { @@ -12197,6 +12774,111 @@ func rewriteValuePPC64_OpPPC64SLD(v *Value) bool { } return false } +func rewriteValuePPC64_OpPPC64SLDconst(v *Value) bool { + v_0 := v.Args[0] + // match: (SLDconst [c] z:(MOVBZreg x)) + // cond: c < 8 && z.Uses == 1 + // result: (CLRLSLDI [newPPC64ShiftAuxInt(c,56,63,64)] x) + for { + c := auxIntToInt64(v.AuxInt) + z := v_0 + if z.Op != OpPPC64MOVBZreg { + break + } + x := z.Args[0] + if !(c < 8 && z.Uses == 1) { + break + } + v.reset(OpPPC64CLRLSLDI) + v.AuxInt = int32ToAuxInt(newPPC64ShiftAuxInt(c, 56, 63, 64)) + v.AddArg(x) + return true + } + // match: (SLDconst [c] z:(MOVHZreg x)) + // cond: c < 16 && z.Uses == 1 + // result: (CLRLSLDI [newPPC64ShiftAuxInt(c,48,63,64)] x) + for { + c := auxIntToInt64(v.AuxInt) + z := v_0 + if z.Op != OpPPC64MOVHZreg { + break + } + x := z.Args[0] + if !(c < 16 && z.Uses == 1) { + break + } + v.reset(OpPPC64CLRLSLDI) + v.AuxInt = int32ToAuxInt(newPPC64ShiftAuxInt(c, 48, 63, 64)) + v.AddArg(x) + return true + } + // match: (SLDconst [c] z:(MOVWZreg x)) + // cond: c < 32 && z.Uses == 1 + // result: (CLRLSLDI [newPPC64ShiftAuxInt(c,32,63,64)] x) + for { + c := auxIntToInt64(v.AuxInt) + z := v_0 + if z.Op != OpPPC64MOVWZreg { + break + } + x := z.Args[0] + if !(c < 32 && z.Uses == 1) { + break + } + v.reset(OpPPC64CLRLSLDI) + v.AuxInt = int32ToAuxInt(newPPC64ShiftAuxInt(c, 32, 63, 64)) + v.AddArg(x) + return true + } + // match: (SLDconst [c] z:(ANDconst [d] x)) + // cond: z.Uses == 1 && isPPC64ValidShiftMask(d) + // result: (CLRLSLDI [newPPC64ShiftAuxInt(c,64-getPPC64ShiftMaskLength(d),63,64)] x) + for { + c := auxIntToInt64(v.AuxInt) + z := v_0 + if z.Op != OpPPC64ANDconst { + break + } + d := auxIntToInt64(z.AuxInt) + x := z.Args[0] + if !(z.Uses == 1 && isPPC64ValidShiftMask(d)) { + break + } + v.reset(OpPPC64CLRLSLDI) + v.AuxInt = int32ToAuxInt(newPPC64ShiftAuxInt(c, 64-getPPC64ShiftMaskLength(d), 63, 64)) + v.AddArg(x) + return true + } + // match: (SLDconst [c] z:(AND (MOVDconst [d]) x)) + // cond: z.Uses == 1 && isPPC64ValidShiftMask(d) + // result: (CLRLSLDI [newPPC64ShiftAuxInt(c,64-getPPC64ShiftMaskLength(d),63,64)] x) + for { + c := auxIntToInt64(v.AuxInt) + z := v_0 + if z.Op != OpPPC64AND { + break + } + _ = z.Args[1] + z_0 := z.Args[0] + z_1 := z.Args[1] + for _i0 := 0; _i0 <= 1; _i0, z_0, z_1 = _i0+1, z_1, z_0 { + if z_0.Op != OpPPC64MOVDconst { + continue + } + d := auxIntToInt64(z_0.AuxInt) + x := z_1 + if !(z.Uses == 1 && isPPC64ValidShiftMask(d)) { + continue + } + v.reset(OpPPC64CLRLSLDI) + v.AuxInt = int32ToAuxInt(newPPC64ShiftAuxInt(c, 64-getPPC64ShiftMaskLength(d), 63, 64)) + v.AddArg(x) + return true + } + break + } + return false +} func rewriteValuePPC64_OpPPC64SLW(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -12215,6 +12897,111 @@ func rewriteValuePPC64_OpPPC64SLW(v *Value) bool { } return false } +func rewriteValuePPC64_OpPPC64SLWconst(v *Value) bool { + v_0 := v.Args[0] + // match: (SLWconst [c] z:(MOVBZreg x)) + // cond: z.Uses == 1 && c < 8 + // result: (CLRLSLWI [newPPC64ShiftAuxInt(c,24,31,32)] x) + for { + c := auxIntToInt64(v.AuxInt) + z := v_0 + if z.Op != OpPPC64MOVBZreg { + break + } + x := z.Args[0] + if !(z.Uses == 1 && c < 8) { + break + } + v.reset(OpPPC64CLRLSLWI) + v.AuxInt = int32ToAuxInt(newPPC64ShiftAuxInt(c, 24, 31, 32)) + v.AddArg(x) + return true + } + // match: (SLWconst [c] z:(MOVHZreg x)) + // cond: z.Uses == 1 && c < 16 + // result: (CLRLSLWI [newPPC64ShiftAuxInt(c,16,31,32)] x) + for { + c := auxIntToInt64(v.AuxInt) + z := v_0 + if z.Op != OpPPC64MOVHZreg { + break + } + x := z.Args[0] + if !(z.Uses == 1 && c < 16) { + break + } + v.reset(OpPPC64CLRLSLWI) + v.AuxInt = int32ToAuxInt(newPPC64ShiftAuxInt(c, 16, 31, 32)) + v.AddArg(x) + return true + } + // match: (SLWconst [c] z:(MOVWZreg x)) + // cond: z.Uses == 1 && c < 24 + // result: (CLRLSLWI [newPPC64ShiftAuxInt(c,8,31,32)] x) + for { + c := auxIntToInt64(v.AuxInt) + z := v_0 + if z.Op != OpPPC64MOVWZreg { + break + } + x := z.Args[0] + if !(z.Uses == 1 && c < 24) { + break + } + v.reset(OpPPC64CLRLSLWI) + v.AuxInt = int32ToAuxInt(newPPC64ShiftAuxInt(c, 8, 31, 32)) + v.AddArg(x) + return true + } + // match: (SLWconst [c] z:(ANDconst [d] x)) + // cond: z.Uses == 1 && isPPC64ValidShiftMask(d) + // result: (CLRLSLWI [newPPC64ShiftAuxInt(c,32-getPPC64ShiftMaskLength(d),31,32)] x) + for { + c := auxIntToInt64(v.AuxInt) + z := v_0 + if z.Op != OpPPC64ANDconst { + break + } + d := auxIntToInt64(z.AuxInt) + x := z.Args[0] + if !(z.Uses == 1 && isPPC64ValidShiftMask(d)) { + break + } + v.reset(OpPPC64CLRLSLWI) + v.AuxInt = int32ToAuxInt(newPPC64ShiftAuxInt(c, 32-getPPC64ShiftMaskLength(d), 31, 32)) + v.AddArg(x) + return true + } + // match: (SLWconst [c] z:(AND (MOVDconst [d]) x)) + // cond: z.Uses == 1 && isPPC64ValidShiftMask(d) + // result: (CLRLSLWI [newPPC64ShiftAuxInt(c,32-getPPC64ShiftMaskLength(d),31,32)] x) + for { + c := auxIntToInt64(v.AuxInt) + z := v_0 + if z.Op != OpPPC64AND { + break + } + _ = z.Args[1] + z_0 := z.Args[0] + z_1 := z.Args[1] + for _i0 := 0; _i0 <= 1; _i0, z_0, z_1 = _i0+1, z_1, z_0 { + if z_0.Op != OpPPC64MOVDconst { + continue + } + d := auxIntToInt64(z_0.AuxInt) + x := z_1 + if !(z.Uses == 1 && isPPC64ValidShiftMask(d)) { + continue + } + v.reset(OpPPC64CLRLSLWI) + v.AuxInt = int32ToAuxInt(newPPC64ShiftAuxInt(c, 32-getPPC64ShiftMaskLength(d), 31, 32)) + v.AddArg(x) + return true + } + break + } + return false +} func rewriteValuePPC64_OpPPC64SRAD(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] diff --git a/src/cmd/internal/obj/ppc64/a.out.go b/src/cmd/internal/obj/ppc64/a.out.go index 8b32692778..f438803fb5 100644 --- a/src/cmd/internal/obj/ppc64/a.out.go +++ b/src/cmd/internal/obj/ppc64/a.out.go @@ -575,6 +575,7 @@ const ( ARLWMICC ARLWNM ARLWNMCC + ACLRLSLWI ASLW ASLWCC ASRW @@ -716,6 +717,9 @@ const ( ARLDCLCC ARLDICL ARLDICLCC + ARLDIC + ARLDICCC + ACLRLSLDI AROTL AROTLW ASLBIA diff --git a/src/cmd/internal/obj/ppc64/anames.go b/src/cmd/internal/obj/ppc64/anames.go index 287011877c..accd87fe00 100644 --- a/src/cmd/internal/obj/ppc64/anames.go +++ b/src/cmd/internal/obj/ppc64/anames.go @@ -180,6 +180,7 @@ var Anames = []string{ "RLWMICC", "RLWNM", "RLWNMCC", + "CLRLSLWI", "SLW", "SLWCC", "SRW", @@ -312,6 +313,9 @@ var Anames = []string{ "RLDCLCC", "RLDICL", "RLDICLCC", + "RLDIC", + "RLDICCC", + "CLRLSLDI", "ROTL", "ROTLW", "SLBIA", diff --git a/src/cmd/internal/obj/ppc64/asm9.go b/src/cmd/internal/obj/ppc64/asm9.go index 98b453de6c..60dda72507 100644 --- a/src/cmd/internal/obj/ppc64/asm9.go +++ b/src/cmd/internal/obj/ppc64/asm9.go @@ -1904,6 +1904,7 @@ func buildop(ctxt *obj.Link) { opset(ARLWMICC, r0) opset(ARLWNM, r0) opset(ARLWNMCC, r0) + opset(ACLRLSLWI, r0) case ARLDMI: opset(ARLDMICC, r0) @@ -1922,6 +1923,9 @@ func buildop(ctxt *obj.Link) { opset(ARLDICLCC, r0) opset(ARLDICR, r0) opset(ARLDICRCC, r0) + opset(ARLDIC, r0) + opset(ARLDICCC, r0) + opset(ACLRLSLDI, r0) case AFMOVD: opset(AFMOVDCC, r0) @@ -2734,13 +2738,31 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) { case ARLDICR, ARLDICRCC: me := int(d) sh := c.regoff(&p.From) + if me < 0 || me > 63 || sh > 63 { + c.ctxt.Diag("Invalid me or sh for RLDICR: %x %x\n%v", int(d), sh) + } o1 = AOP_RLDIC(c.oprrr(p.As), uint32(p.To.Reg), uint32(r), uint32(sh), uint32(me)) - case ARLDICL, ARLDICLCC: + case ARLDICL, ARLDICLCC, ARLDIC, ARLDICCC: mb := int(d) sh := c.regoff(&p.From) + if mb < 0 || mb > 63 || sh > 63 { + c.ctxt.Diag("Invalid mb or sh for RLDIC, RLDICL: %x %x\n%v", mb, sh) + } o1 = AOP_RLDIC(c.oprrr(p.As), uint32(p.To.Reg), uint32(r), uint32(sh), uint32(mb)) + case ACLRLSLDI: + // This is an extended mnemonic defined in the ISA section C.8.1 + // clrlsldi ra,rs,n,b --> rldic ra,rs,n,b-n + // It maps onto RLDIC so is directly generated here based on the operands from + // the clrlsldi. + b := int(d) + n := c.regoff(&p.From) + if n > int32(b) || b > 63 { + c.ctxt.Diag("Invalid n or b for CLRLSLDI: %x %x\n%v", n, b) + } + o1 = AOP_RLDIC(OP_RLDIC, uint32(p.To.Reg), uint32(r), uint32(n), uint32(b)-uint32(n)) + default: c.ctxt.Diag("unexpected op in rldc case\n%v", p) a = 0 @@ -3354,18 +3376,43 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) { case 62: /* rlwmi $sh,s,$mask,a */ v := c.regoff(&p.From) - - var mask [2]uint8 - c.maskgen(p, mask[:], uint32(c.regoff(p.GetFrom3()))) - o1 = AOP_RRR(c.opirr(p.As), uint32(p.Reg), uint32(p.To.Reg), uint32(v)) - o1 |= (uint32(mask[0])&31)<<6 | (uint32(mask[1])&31)<<1 + switch p.As { + case ACLRLSLWI: + b := c.regoff(p.GetFrom3()) + // This is an extended mnemonic described in the ISA C.8.2 + // clrlslwi ra,rs,n,b -> rlwinm ra,rs,n,b-n,31-n + // It maps onto rlwinm which is directly generated here. + if v < 0 || v > 32 || b > 32 { + c.ctxt.Diag("Invalid n or b for CLRLSLWI: %x %x\n%v", v, b) + } + o1 = OP_RLW(OP_RLWINM, uint32(p.To.Reg), uint32(p.Reg), uint32(v), uint32(b-v), uint32(31-v)) + default: + var mask [2]uint8 + c.maskgen(p, mask[:], uint32(c.regoff(p.GetFrom3()))) + o1 = AOP_RRR(c.opirr(p.As), uint32(p.Reg), uint32(p.To.Reg), uint32(v)) + o1 |= (uint32(mask[0])&31)<<6 | (uint32(mask[1])&31)<<1 + } case 63: /* rlwmi b,s,$mask,a */ - var mask [2]uint8 - c.maskgen(p, mask[:], uint32(c.regoff(p.GetFrom3()))) - - o1 = AOP_RRR(c.opirr(p.As), uint32(p.Reg), uint32(p.To.Reg), uint32(p.From.Reg)) - o1 |= (uint32(mask[0])&31)<<6 | (uint32(mask[1])&31)<<1 + v := c.regoff(&p.From) + switch p.As { + case ACLRLSLWI: + b := c.regoff(p.GetFrom3()) + if v > b || b > 32 { + // Message will match operands from the ISA even though in the + // code it uses 'v' + c.ctxt.Diag("Invalid n or b for CLRLSLWI: %x %x\n%v", v, b) + } + // This is an extended mnemonic described in the ISA C.8.2 + // clrlslwi ra,rs,n,b -> rlwinm ra,rs,n,b-n,31-n + // It generates the rlwinm directly here. + o1 = OP_RLW(OP_RLWINM, uint32(p.To.Reg), uint32(p.Reg), uint32(v), uint32(b-v), uint32(31-v)) + default: + var mask [2]uint8 + c.maskgen(p, mask[:], uint32(c.regoff(p.GetFrom3()))) + o1 = AOP_RRR(c.opirr(p.As), uint32(p.Reg), uint32(p.To.Reg), uint32(v)) + o1 |= (uint32(mask[0])&31)<<6 | (uint32(mask[1])&31)<<1 + } case 64: /* mtfsf fr[, $m] {,fpcsr} */ var v int32 @@ -4277,6 +4324,11 @@ func (c *ctxt9) oprrr(a obj.As) uint32 { case ARLDICRCC: return OPVCC(30, 0, 0, 1) | 2<<1 // rldicr. + case ARLDIC: + return OPVCC(30, 0, 0, 0) | 4<<1 // rldic + case ARLDICCC: + return OPVCC(30, 0, 0, 1) | 4<<1 // rldic. + case ASYSCALL: return OPVCC(17, 1, 0, 0) diff --git a/test/codegen/shift.go b/test/codegen/shift.go index 5e50ea6bff..32214851b5 100644 --- a/test/codegen/shift.go +++ b/test/codegen/shift.go @@ -150,6 +150,61 @@ func lshGuarded64(v int64, s uint) int64 { panic("shift too large") } +func checkUnneededTrunc(tab *[100000]uint32, d uint64, v uint32, h uint16, b byte) (uint32, uint64) { + + // ppc64le:-".*RLWINM",-".*RLDICR",".*CLRLSLDI" + // ppc64:-".*RLWINM",-".*RLDICR",".*CLRLSLDI" + f := tab[byte(v)^b] + // ppc64le:-".*RLWINM",-".*RLDICR",".*CLRLSLDI" + // ppc64:-".*RLWINM",-".*RLDICR",".*CLRLSLDI" + f += tab[byte(v)&b] + // ppc64le:-".*RLWINM",-".*RLDICR",".*CLRLSLDI" + // ppc64:-".*RLWINM",-".*RLDICR",".*CLRLSLDI" + f += tab[byte(v)|b] + // ppc64le:-".*RLWINM",-".*RLDICR",".*CLRLSLDI" + // ppc64:-".*RLWINM",-".*RLDICR",".*CLRLSLDI" + f += tab[uint16(v)&h] + // ppc64le:-".*RLWINM",-".*RLDICR",".*CLRLSLDI" + // ppc64:-".*RLWINM",-".*RLDICR",".*CLRLSLDI" + f += tab[uint16(v)^h] + // ppc64le:-".*RLWINM",-".*RLDICR",".*CLRLSLDI" + // ppc64:-".*RLWINM",-".*RLDICR",".*CLRLSLDI" + f += tab[uint16(v)|h] + // ppc64le:-".*AND",-"RLDICR",".*CLRLSLDI" + // ppc64:-".*AND",-"RLDICR",".*CLRLSLDI" + f += tab[v&0xff] + // ppc64le:-".*AND",".*CLRLSLWI" + // ppc64:-".*AND",".*CLRLSLWI" + f += 2*uint32(uint16(d)) + // ppc64le:-".*AND",-"RLDICR",".*CLRLSLDI" + // ppc64:-".*AND",-"RLDICR",".*CLRLSLDI" + g := 2*uint64(uint32(d)) + return f, g +} + +func checkCombinedShifts(v8 uint8, v16 uint16, v32 uint32, v64 uint64) (uint8, uint16, uint32, uint64) { + + // ppc64le:-"AND","CLRLSLWI" + // ppc64:-"AND","CLRLSLWI" + f := (v8 &0xF) << 2 + // ppc64le:-"AND","CLRLSLWI" + // ppc64:-"AND","CLRLSLWI" + f += byte(v16)<<3 + // ppc64le:-"AND","CLRLSLWI" + // ppc64:-"AND","CLRLSLWI" + g := (v16 & 0xFF) << 3 + // ppc64le:-"AND","CLRLSLWI" + // ppc64:-"AND","CLRLSLWI" + h := (v32 & 0xFFFFF) << 2 + // ppc64le:-"AND","CLRLSLWI" + // ppc64:-"AND","CLRLSLWI" + h += uint32(v64)<<4 + // ppc64le:-"AND","CLRLSLDI" + // ppc64:-"AND","CLRLSLDI" + i := (v64 & 0xFFFFFFFF) << 5 + return f, g, h, i +} + func checkWidenAfterShift(v int64, u uint64) (int64, uint64) { // ppc64le:-".*MOVW" -- cgit v1.2.3-54-g00ecf From 5abba0c73723a843315c0f7ed014617445af6243 Mon Sep 17 00:00:00 2001 From: Jay Conrod Date: Tue, 15 Sep 2020 12:59:05 -0400 Subject: cmd/go: prepare tests for GO111MODULE=on by default Set GO111MODULE=off explicitly in tests specific to GOPATH mode. Added a go.mod file to other tests that assumed GOPATH mode. Fixed an issue in the build metadata file generated in modload/build.go, which did not end with a newline. This broke the build_dash_x test, which expects to be able to run the script printed by 'go build -x' to produce the same result. The script is broken if the build metadata file doesn't end with a newline. Change-Id: I59f2a492a9f5a66f6c4aa702f429909d5c5e815d Reviewed-on: https://go-review.googlesource.com/c/go/+/255051 Run-TryBot: Jay Conrod TryBot-Result: Go Bot Reviewed-by: Bryan C. Mills Trust: Jay Conrod --- src/cmd/go/internal/modload/build.go | 4 +- .../go/testdata/script/build_cache_arch_mode.txt | 10 ++-- .../go/testdata/script/build_cache_disabled.txt | 8 ++- .../testdata/script/build_cd_gopath_different.txt | 1 + .../script/build_cgo_consistent_results.txt | 6 ++- src/cmd/go/testdata/script/build_dash_x.txt | 2 +- src/cmd/go/testdata/script/build_exe.txt | 12 +++-- src/cmd/go/testdata/script/build_gopath_order.txt | 3 +- .../go/testdata/script/build_import_comment.txt | 57 ++++++++++++++------- src/cmd/go/testdata/script/build_import_cycle.txt | 3 ++ src/cmd/go/testdata/script/build_internal.txt | 45 ++++++++++++----- src/cmd/go/testdata/script/build_issue6480.txt | 8 ++- .../script/build_link_x_import_path_escape.txt | 8 ++- src/cmd/go/testdata/script/build_n_cgo.txt | 4 ++ src/cmd/go/testdata/script/build_no_go.txt | 4 ++ src/cmd/go/testdata/script/build_output.txt | 8 ++- .../script/build_patterns_outside_gopath.txt | 9 +++- .../go/testdata/script/build_plugin_non_main.txt | 17 +++---- src/cmd/go/testdata/script/build_test_only.txt | 8 ++- src/cmd/go/testdata/script/build_vendor.txt | 1 + src/cmd/go/testdata/script/cgo_asm_error.txt | 8 ++- src/cmd/go/testdata/script/cgo_bad_directives.txt | 43 ++++++++-------- .../go/testdata/script/cgo_depends_on_syscall.txt | 6 ++- src/cmd/go/testdata/script/cover_asm.txt | 10 ++-- .../go/testdata/script/cover_blank_func_decl.txt | 10 ++-- src/cmd/go/testdata/script/cover_cgo.txt | 8 ++- .../go/testdata/script/cover_cgo_extra_file.txt | 12 +++-- .../go/testdata/script/cover_cgo_extra_test.txt | 12 +++-- src/cmd/go/testdata/script/cover_cgo_xtest.txt | 10 ++-- src/cmd/go/testdata/script/cover_dash_c.txt | 10 ++-- src/cmd/go/testdata/script/cover_dep_loop.txt | 10 ++-- src/cmd/go/testdata/script/cover_dot_import.txt | 20 +++++--- src/cmd/go/testdata/script/cover_error.txt | 18 ++++--- .../go/testdata/script/cover_import_main_loop.txt | 10 ++-- src/cmd/go/testdata/script/cover_pattern.txt | 10 ++-- src/cmd/go/testdata/script/cover_statements.txt | 4 ++ .../testdata/script/cover_sync_atomic_import.txt | 12 +++-- .../go/testdata/script/generate_bad_imports.txt | 6 ++- src/cmd/go/testdata/script/generate_invalid.txt | 6 ++- .../testdata/script/get_custom_domain_wildcard.txt | 3 +- src/cmd/go/testdata/script/get_dash_t.txt | 3 +- src/cmd/go/testdata/script/get_domain_root.txt | 3 +- .../go/testdata/script/get_dot_slash_download.txt | 3 +- src/cmd/go/testdata/script/get_goroot.txt | 3 +- .../testdata/script/get_insecure_custom_domain.txt | 3 +- src/cmd/go/testdata/script/get_insecure_update.txt | 3 +- .../go/testdata/script/get_internal_wildcard.txt | 3 +- src/cmd/go/testdata/script/get_issue11307.txt | 3 +- src/cmd/go/testdata/script/get_legacy.txt | 58 ++++++++++++++++++++++ src/cmd/go/testdata/script/get_race.txt | 3 +- src/cmd/go/testdata/script/get_test_only.txt | 3 +- src/cmd/go/testdata/script/get_update.txt | 3 +- .../script/get_update_unknown_protocol.txt | 3 +- src/cmd/go/testdata/script/get_update_wildcard.txt | 3 +- .../go/testdata/script/get_vcs_error_message.txt | 1 + src/cmd/go/testdata/script/get_vendor.txt | 3 +- .../go/testdata/script/gopath_vendor_dup_err.txt | 1 + .../go/testdata/script/install_cgo_excluded.txt | 6 ++- .../script/install_relative_gobin_fail.txt | 6 ++- .../go/testdata/script/install_shadow_gopath.txt | 3 +- .../go/testdata/script/link_syso_issue33139.txt | 6 ++- src/cmd/go/testdata/script/list_case_collision.txt | 22 ++++---- src/cmd/go/testdata/script/list_dedup_packages.txt | 1 + src/cmd/go/testdata/script/list_symlink.txt | 1 + .../go/testdata/script/list_symlink_internal.txt | 3 +- .../script/list_symlink_vendor_issue14054.txt | 3 +- .../script/list_symlink_vendor_issue15201.txt | 3 +- src/cmd/go/testdata/script/list_test_simple.txt | 20 ++++---- .../script/list_wildcard_skip_nonmatching.txt | 16 +++--- src/cmd/go/testdata/script/load_test_pkg_err.txt | 12 +++-- src/cmd/go/testdata/script/mod_get_legacy.txt | 57 --------------------- src/cmd/go/testdata/script/run_hello_pkg.txt | 9 ++-- src/cmd/go/testdata/script/run_vendor.txt | 1 + .../go/testdata/script/test_benchmark_fatal.txt | 6 ++- .../go/testdata/script/test_benchmark_labels.txt | 6 ++- src/cmd/go/testdata/script/test_build_failure.txt | 10 ++-- src/cmd/go/testdata/script/test_deadline.txt | 4 ++ src/cmd/go/testdata/script/test_empty.txt | 4 ++ src/cmd/go/testdata/script/test_example_goexit.txt | 6 ++- .../go/testdata/script/test_import_error_stack.txt | 11 ++++ src/cmd/go/testdata/script/test_json.txt | 18 ++++--- src/cmd/go/testdata/script/test_main_twice.txt | 6 ++- .../script/test_match_no_tests_build_failure.txt | 8 ++- src/cmd/go/testdata/script/test_no_run_example.txt | 8 ++- src/cmd/go/testdata/script/test_no_tests.txt | 6 ++- src/cmd/go/testdata/script/test_race.txt | 6 ++- .../script/test_race_cover_mode_issue20435.txt | 6 ++- src/cmd/go/testdata/script/test_race_install.txt | 6 ++- .../go/testdata/script/test_race_install_cgo.txt | 6 ++- src/cmd/go/testdata/script/test_regexps.txt | 8 ++- .../go/testdata/script/test_relative_import.txt | 3 +- .../script/test_relative_import_dash_i.txt | 3 +- .../script/test_syntax_error_says_fail.txt | 11 ++++ src/cmd/go/testdata/script/test_vendor.txt | 12 ++++- src/cmd/go/testdata/script/test_vet.txt | 16 +++--- .../script/test_write_profiles_on_timeout.txt | 9 ++-- .../go/testdata/script/test_xtestonly_works.txt | 8 ++- src/cmd/go/testdata/script/testing_issue40908.txt | 6 ++- .../testdata/script/vendor_gopath_issue11409.txt | 1 + src/cmd/go/testdata/script/vendor_import.txt | 1 + src/cmd/go/testdata/script/vendor_import_wrong.txt | 11 ++++ src/cmd/go/testdata/script/vendor_issue12156.txt | 1 + .../go/testdata/script/vendor_list_issue11977.txt | 3 +- src/cmd/go/testdata/script/vendor_resolve.txt | 1 + .../go/testdata/script/vendor_test_issue11864.txt | 3 +- .../go/testdata/script/vendor_test_issue14613.txt | 1 + src/cmd/go/testdata/script/vet.txt | 18 ++++--- 107 files changed, 652 insertions(+), 298 deletions(-) create mode 100644 src/cmd/go/testdata/script/get_legacy.txt delete mode 100644 src/cmd/go/testdata/script/mod_get_legacy.txt diff --git a/src/cmd/go/internal/modload/build.go b/src/cmd/go/internal/modload/build.go index 9ca6230500..2c7cfb732d 100644 --- a/src/cmd/go/internal/modload/build.go +++ b/src/cmd/go/internal/modload/build.go @@ -355,13 +355,13 @@ func ModInfoProg(info string, isgccgo bool) []byte { import _ "unsafe" //go:linkname __debug_modinfo__ runtime.modinfo var __debug_modinfo__ = %q - `, string(infoStart)+info+string(infoEnd))) +`, string(infoStart)+info+string(infoEnd))) } else { return []byte(fmt.Sprintf(`package main import _ "unsafe" //go:linkname __set_debug_modinfo__ runtime.setmodinfo func __set_debug_modinfo__(string) func init() { __set_debug_modinfo__(%q) } - `, string(infoStart)+info+string(infoEnd))) +`, string(infoStart)+info+string(infoEnd))) } } diff --git a/src/cmd/go/testdata/script/build_cache_arch_mode.txt b/src/cmd/go/testdata/script/build_cache_arch_mode.txt index 7e751d02b9..68e662555f 100644 --- a/src/cmd/go/testdata/script/build_cache_arch_mode.txt +++ b/src/cmd/go/testdata/script/build_cache_arch_mode.txt @@ -3,7 +3,6 @@ [short] skip # 386 -cd $GOPATH/src/mycmd env GOOS=linux env GOARCH=386 env GO386=387 @@ -12,7 +11,6 @@ env GO386=sse2 stale mycmd # arm -cd $GOPATH/src/mycmd env GOOS=linux env GOARCH=arm env GOARM=5 @@ -21,7 +19,11 @@ env GOARM=7 stale mycmd --- mycmd/x.go -- +-- go.mod -- +module mycmd + +go 1.16 +-- x.go -- package main -func main() {} \ No newline at end of file +func main() {} diff --git a/src/cmd/go/testdata/script/build_cache_disabled.txt b/src/cmd/go/testdata/script/build_cache_disabled.txt index 2e1327880b..8b005c857f 100644 --- a/src/cmd/go/testdata/script/build_cache_disabled.txt +++ b/src/cmd/go/testdata/script/build_cache_disabled.txt @@ -18,8 +18,6 @@ env GOCACHE=off go doc fmt stdout Printf -go fmt . - ! go tool compile -h stderr usage: @@ -40,6 +38,12 @@ stdout rsc.io/quote go mod verify + +# Commands that load but don't build packages should work. +go fmt . + +go doc . + -- main.go -- package main diff --git a/src/cmd/go/testdata/script/build_cd_gopath_different.txt b/src/cmd/go/testdata/script/build_cd_gopath_different.txt index 698b3d70f4..a7a4bf412d 100644 --- a/src/cmd/go/testdata/script/build_cd_gopath_different.txt +++ b/src/cmd/go/testdata/script/build_cd_gopath_different.txt @@ -1,4 +1,5 @@ [gccgo] skip 'gccgo does not support -ldflags -X' +env GO111MODULE=off go build run_go.go # Apply identity function to GOPATH diff --git a/src/cmd/go/testdata/script/build_cgo_consistent_results.txt b/src/cmd/go/testdata/script/build_cgo_consistent_results.txt index 42f1cc1a74..88a24de814 100644 --- a/src/cmd/go/testdata/script/build_cgo_consistent_results.txt +++ b/src/cmd/go/testdata/script/build_cgo_consistent_results.txt @@ -11,7 +11,11 @@ go build -x -o $WORK/exe2$GOEXE cgotest cmp $WORK/exe1$GOEXE $WORK/exe2$GOEXE --- cgotest/m.go -- +-- go.mod -- +module cgotest + +go 1.16 +-- m.go -- package cgotest import "C" diff --git a/src/cmd/go/testdata/script/build_dash_x.txt b/src/cmd/go/testdata/script/build_dash_x.txt index 3082095c5c..6fd5bbe182 100644 --- a/src/cmd/go/testdata/script/build_dash_x.txt +++ b/src/cmd/go/testdata/script/build_dash_x.txt @@ -46,4 +46,4 @@ func main() { -- header.txt -- set -e -- hello.txt -- -hello \ No newline at end of file +hello diff --git a/src/cmd/go/testdata/script/build_exe.txt b/src/cmd/go/testdata/script/build_exe.txt index fd13259fcc..a994d17088 100644 --- a/src/cmd/go/testdata/script/build_exe.txt +++ b/src/cmd/go/testdata/script/build_exe.txt @@ -1,12 +1,16 @@ -# go build with -o and -buildmode=exe should on a non-main package should throw an error +# go build with -o and -buildmode=exe should report an error on a non-main package. -! go build -buildmode=exe -o out$GOEXE not_main +! go build -buildmode=exe -o out$GOEXE ./not_main stderr '-buildmode=exe requires exactly one main package' ! exists out$GOEXE -! go build -buildmode=exe -o out$GOEXE main_one main_two +! go build -buildmode=exe -o out$GOEXE ./main_one ./main_two stderr '-buildmode=exe requires exactly one main package' ! exists out$GOEXE +-- go.mod -- +module m + +go 1.16 -- not_main/not_main.go -- package not_main @@ -18,4 +22,4 @@ func main() {} -- main_two/main_two.go -- package main -func main() {} \ No newline at end of file +func main() {} diff --git a/src/cmd/go/testdata/script/build_gopath_order.txt b/src/cmd/go/testdata/script/build_gopath_order.txt index ac26c28a9f..caf25022e4 100644 --- a/src/cmd/go/testdata/script/build_gopath_order.txt +++ b/src/cmd/go/testdata/script/build_gopath_order.txt @@ -3,6 +3,7 @@ # -I arguments to compiler could end up not in GOPATH order, # leading to unexpected import resolution in the compiler. +env GO111MODULE=off env GOPATH=$WORK/p1${:}$WORK/p2 mkdir $WORK/p1/src/foo $WORK/p2/src/baz mkdir $WORK/p2/pkg/${GOOS}_${GOARCH} $WORK/p1/src/bar @@ -32,4 +33,4 @@ bad -- bar.go -- package bar import _ "baz" -import _ "foo" \ No newline at end of file +import _ "foo" diff --git a/src/cmd/go/testdata/script/build_import_comment.txt b/src/cmd/go/testdata/script/build_import_comment.txt index 0ab643914d..b500340bfb 100644 --- a/src/cmd/go/testdata/script/build_import_comment.txt +++ b/src/cmd/go/testdata/script/build_import_comment.txt @@ -1,6 +1,6 @@ -# TODO: add a go.mod file and test with GO111MODULE explicitly on and off. -# We only report the 'expects import' error when modules are disabled. -# Do we report comment parse errors or conflicts in module mode? We shouldn't. +# Test in GOPATH mode first. +env GO111MODULE=off +cd m # Import comment matches go build -n works.go @@ -17,31 +17,52 @@ stderr 'cannot parse import comment' ! go build -n conflict.go stderr 'found import comments' --- bad.go -- + +# Test in module mode. +# We ignore import comments, so these commands should succeed. +env GO111MODULE=on + +# Import comment matches +go build -n works.go + +# Import comment mismatch +go build -n wrongplace.go + +# Import comment syntax error +go build -n bad.go + +# Import comment conflict +go build -n conflict.go + +-- m/go.mod -- +module m + +go 1.16 +-- m/bad.go -- package p -import "bad" --- conflict.go -- +import "m/bad" +-- m/conflict.go -- package p -import "conflict" --- works.go -- +import "m/conflict" +-- m/works.go -- package p -import _ "works/x" --- wrongplace.go -- +import _ "m/works/x" +-- m/wrongplace.go -- package p -import "wrongplace" --- bad/bad.go -- +import "m/wrongplace" +-- m/bad/bad.go -- package bad // import --- conflict/a.go -- +-- m/conflict/a.go -- package conflict // import "a" --- conflict/b.go -- +-- m/conflict/b.go -- package conflict /* import "b" */ --- works/x/x.go -- -package x // import "works/x" --- works/x/x1.go -- +-- m/works/x/x.go -- +package x // import "m/works/x" +-- m/works/x/x1.go -- package x // important! not an import comment --- wrongplace/x.go -- +-- m/wrongplace/x.go -- package x // import "my/x" diff --git a/src/cmd/go/testdata/script/build_import_cycle.txt b/src/cmd/go/testdata/script/build_import_cycle.txt index 0154305c27..16e4e87dae 100644 --- a/src/cmd/go/testdata/script/build_import_cycle.txt +++ b/src/cmd/go/testdata/script/build_import_cycle.txt @@ -1,3 +1,6 @@ +# mod_import_cycle covers this error in module mode. +env GO111MODULE=off + ! go build selfimport stderr -count=1 'import cycle not allowed' diff --git a/src/cmd/go/testdata/script/build_internal.txt b/src/cmd/go/testdata/script/build_internal.txt index 6fcc4e02aa..25aa18cfcb 100644 --- a/src/cmd/go/testdata/script/build_internal.txt +++ b/src/cmd/go/testdata/script/build_internal.txt @@ -1,44 +1,63 @@ # Test internal package errors are handled -go list ./testinternal3 +cd testinternal3 +go list . stdout 'testinternal3' # Test internal cache -env GOPATH=$WORK/gopath/src/testinternal4 -! go build p +cd ../testinternal4 +! go build testinternal4/p stderr 'internal' # Test internal packages outside GOROOT are respected -! go build -v ./testinternal2 -stderr 'testinternal2(\/|\\)p\.go\:3\:8\: use of internal package .*internal/w not allowed' +cd ../testinternal2 +! go build -v . +stderr 'p\.go:3:8: use of internal package .*internal/w not allowed' [gccgo] skip # gccgo does not have GOROOT -! go build -v ./testinternal -stderr 'testinternal(\/|\\)p\.go\:3\:8\: use of internal package net/http/internal not allowed' +cd ../testinternal +! go build -v . +stderr 'p\.go:3:8: use of internal package net/http/internal not allowed' +-- testinternal/go.mod -- +module testinternal + +go 1.16 -- testinternal/p.go -- package p import _ "net/http/internal" +-- testinternal2/go.mod -- +module testinternal2 + +go 1.16 -- testinternal2/p.go -- package p import _ "./x/y/z/internal/w" -- testinternal2/x/y/z/internal/w/w.go -- package w +-- testinternal3/go.mod -- +module testinternal3 + +go 1.16 -- testinternal3/t.go -- package t import _ "internal/does-not-exist" --- testinternal4/src/p/p.go -- +-- testinternal4/go.mod -- +module testinternal4 + +go 1.16 +-- testinternal4/p/p.go -- package p import ( - _ "q/internal/x" - _ "q/j" + _ "testinternal4/q/internal/x" + _ "testinternal4/q/j" ) --- testinternal4/src/q/internal/x/x.go -- +-- testinternal4/q/internal/x/x.go -- package x --- testinternal4/src/q/j/j.go -- +-- testinternal4/q/j/j.go -- package j -import _ "q/internal/x" +import _ "testinternal4/q/internal/x" diff --git a/src/cmd/go/testdata/script/build_issue6480.txt b/src/cmd/go/testdata/script/build_issue6480.txt index 857f364e81..ae99c60d99 100644 --- a/src/cmd/go/testdata/script/build_issue6480.txt +++ b/src/cmd/go/testdata/script/build_issue6480.txt @@ -8,7 +8,7 @@ # Install some commands to compare mtimes env GOBIN=$WORK/tmp/bin -go install now mtime before +go install m/now m/mtime m/before # Initial builds go test -c -test.bench=XXX errors @@ -34,6 +34,10 @@ exec $GOBIN/mtime errors2.test cp stdout errors2_mod_time.txt exec $GOBIN/before start_time.txt errors2_mod_time.txt +-- go.mod -- +module m + +go 1.16 -- now/now.go -- // Writes time.Now() to a file package main @@ -122,4 +126,4 @@ func main() { fmt.Fprintf(os.Stderr, "time in %v (%v) is not before time in %v (%v)", os.Args[1], t1, os.Args[2], t2) os.Exit(1) } -} \ No newline at end of file +} diff --git a/src/cmd/go/testdata/script/build_link_x_import_path_escape.txt b/src/cmd/go/testdata/script/build_link_x_import_path_escape.txt index daa544d3f0..f1de1e4c71 100644 --- a/src/cmd/go/testdata/script/build_link_x_import_path_escape.txt +++ b/src/cmd/go/testdata/script/build_link_x_import_path_escape.txt @@ -4,7 +4,11 @@ go build -o linkx$GOEXE -ldflags -X=my.pkg.Text=linkXworked my.pkg/main exec ./linkx$GOEXE stderr '^linkXworked$' --- my.pkg/main/main.go -- +-- go.mod -- +module my.pkg + +go 1.16 +-- main/main.go -- package main import "my.pkg" @@ -12,7 +16,7 @@ import "my.pkg" func main() { println(pkg.Text) } --- my.pkg/pkg.go -- +-- pkg.go -- package pkg var Text = "unset" diff --git a/src/cmd/go/testdata/script/build_n_cgo.txt b/src/cmd/go/testdata/script/build_n_cgo.txt index 200d13760e..7aa77aea42 100644 --- a/src/cmd/go/testdata/script/build_n_cgo.txt +++ b/src/cmd/go/testdata/script/build_n_cgo.txt @@ -5,6 +5,10 @@ go build -n ! stderr '[/\\]\$WORK' +-- go.mod -- +module m + +go 1.16 -- main.go -- package main diff --git a/src/cmd/go/testdata/script/build_no_go.txt b/src/cmd/go/testdata/script/build_no_go.txt index 3fd7739fbb..b61d752274 100644 --- a/src/cmd/go/testdata/script/build_no_go.txt +++ b/src/cmd/go/testdata/script/build_no_go.txt @@ -16,6 +16,10 @@ stderr 'no Go files in ' ! go build ./exclude/empty stderr 'no Go files in ' +-- go.mod -- +module m + +go 1.16 -- empty/test/test_test.go -- package p -- empty/testxtest/test_test.go -- diff --git a/src/cmd/go/testdata/script/build_output.txt b/src/cmd/go/testdata/script/build_output.txt index e5a4852346..ced7cf82a6 100644 --- a/src/cmd/go/testdata/script/build_output.txt +++ b/src/cmd/go/testdata/script/build_output.txt @@ -5,7 +5,7 @@ [windows] env NONEXE='' env GOBIN=$WORK/tmp/bin -go install isarchive & +go install m/isarchive & go build x.go exists -exec x$GOEXE @@ -55,6 +55,10 @@ exec $GOBIN/isarchive myatomic.a ! go build -o whatever cmd/gofmt sync/atomic stderr 'multiple packages' +-- go.mod -- +module m + +go 1.16 -- x.go -- package main @@ -84,4 +88,4 @@ func main() { fmt.Fprintf(os.Stderr, "file %s exists but is not an archive\n", os.Args[1]) os.Exit(1) } -} \ No newline at end of file +} diff --git a/src/cmd/go/testdata/script/build_patterns_outside_gopath.txt b/src/cmd/go/testdata/script/build_patterns_outside_gopath.txt index f36e90fea6..6a600cfb0a 100644 --- a/src/cmd/go/testdata/script/build_patterns_outside_gopath.txt +++ b/src/cmd/go/testdata/script/build_patterns_outside_gopath.txt @@ -1,6 +1,9 @@ # Tests issue #18778 +[short] skip cd pkgs + +env GO111MODULE=off go build ./... ! stdout . go test ./... @@ -9,6 +12,10 @@ go list ./... stdout 'pkgs$' stdout 'pkgs/a' +-- pkgs/go.mod -- +module pkgs + +go 1.16 -- pkgs/a.go -- package x -- pkgs/a_test.go -- @@ -26,4 +33,4 @@ package a_test import "testing" func TestA(t *testing.T) { -} \ No newline at end of file +} diff --git a/src/cmd/go/testdata/script/build_plugin_non_main.txt b/src/cmd/go/testdata/script/build_plugin_non_main.txt index 996d87d961..dba80c29ad 100644 --- a/src/cmd/go/testdata/script/build_plugin_non_main.txt +++ b/src/cmd/go/testdata/script/build_plugin_non_main.txt @@ -2,16 +2,13 @@ [!linux] [!darwin] skip [!cgo] skip -go build -n testdep/p2 -! go build -buildmode=plugin testdep/p2 +go build -n testdep +! go build -buildmode=plugin testdep stderr '-buildmode=plugin requires exactly one main package' --- testdep/p1/p1.go -- -package p1 --- testdep/p1/p1_test.go -- -package p1 - -import _ "testdep/p2" --- testdep/p2/p2.go -- -package p2 +-- go.mod -- +module testdep +go 1.16 +-- testdep.go -- +package p diff --git a/src/cmd/go/testdata/script/build_test_only.txt b/src/cmd/go/testdata/script/build_test_only.txt index 54dd59772a..8693a80a08 100644 --- a/src/cmd/go/testdata/script/build_test_only.txt +++ b/src/cmd/go/testdata/script/build_test_only.txt @@ -1,14 +1,18 @@ # Named explicitly, test-only packages should be reported as # unbuildable/uninstallable, even if there is a wildcard also matching. -! go build testonly testonly... +! go build m/testonly m/testonly... stderr 'no non-test Go files in' ! go install ./testonly stderr 'no non-test Go files in' # Named through a wildcard, the test-only packages should be silently ignored. -go build testonly... +go build m/testonly... go install ./testonly... +-- go.mod -- +module m + +go 1.16 -- testonly/t_test.go -- package testonly -- testonly2/t.go -- diff --git a/src/cmd/go/testdata/script/build_vendor.txt b/src/cmd/go/testdata/script/build_vendor.txt index 726ecd75b9..f430ff2c3e 100644 --- a/src/cmd/go/testdata/script/build_vendor.txt +++ b/src/cmd/go/testdata/script/build_vendor.txt @@ -1,4 +1,5 @@ # Build +env GO111MODULE=off go build vend/x ! stdout . ! stderr . diff --git a/src/cmd/go/testdata/script/cgo_asm_error.txt b/src/cmd/go/testdata/script/cgo_asm_error.txt index e656106940..7aaa713e24 100644 --- a/src/cmd/go/testdata/script/cgo_asm_error.txt +++ b/src/cmd/go/testdata/script/cgo_asm_error.txt @@ -6,7 +6,11 @@ ! go build cgoasm stderr 'package using cgo has Go assembly file' --- cgoasm/p.go -- +-- go.mod -- +module cgoasm + +go 1.16 +-- p.go -- package p /* @@ -15,7 +19,7 @@ package p import "C" func F() {} --- cgoasm/p.s -- +-- p.s -- TEXT asm(SB),$0 RET diff --git a/src/cmd/go/testdata/script/cgo_bad_directives.txt b/src/cmd/go/testdata/script/cgo_bad_directives.txt index 358284ffec..6bf3beb8e4 100644 --- a/src/cmd/go/testdata/script/cgo_bad_directives.txt +++ b/src/cmd/go/testdata/script/cgo_bad_directives.txt @@ -1,58 +1,57 @@ [!cgo] skip [short] skip -mkdir x -cp x.go.txt x/x.go +cp x.go.txt x.go # Only allow //go:cgo_ldflag .* in cgo-generated code -[gc] cp x_gc.go.txt x/x.go +[gc] cp x_gc.go.txt x.go [gc] ! go build x [gc] stderr '//go:cgo_ldflag .* only allowed in cgo-generated code' # Ignore _* files -rm x/x.go -! go build x +rm x.go +! go build . stderr 'no Go files' -cp cgo_yy.go.txt x/_cgo_yy.go -! go build x +cp cgo_yy.go.txt _cgo_yy.go +! go build . stderr 'no Go files' #_* files are ignored... -[gc] ! go build x/_cgo_yy.go # ... but if forced, the comment is rejected +[gc] ! go build _cgo_yy.go # ... but if forced, the comment is rejected # Actually, today there is a separate issue that _ files named # on the command line are ignored. Once that is fixed, # we want to see the cgo_ldflag error. [gc] stderr '//go:cgo_ldflag only allowed in cgo-generated code|no Go files' -rm x/_cgo_yy.go +rm _cgo_yy.go # Reject #cgo CFLAGS: -fplugin=foo.so -cp x.go.txt x/x.go -cp y_fplugin.go.txt x/y.go +cp x.go.txt x.go +cp y_fplugin.go.txt y.go ! go build x stderr 'invalid flag in #cgo CFLAGS: -fplugin=foo.so' # Reject #cgo CFLAGS: -lbar -fplugin=foo.so -cp y_lbar_fplugin.go.txt x/y.go +cp y_lbar_fplugin.go.txt y.go ! go build x stderr 'invalid flag in #cgo CFLAGS: -fplugin=foo.so' # Reject #cgo pkg-config: -foo -cp y_pkgconfig_dash_foo.txt x/y.go +cp y_pkgconfig_dash_foo.txt y.go ! go build x stderr 'invalid pkg-config package name: -foo' # Reject #cgo pkg-config: @foo -cp y_pkgconfig_at_foo.txt x/y.go +cp y_pkgconfig_at_foo.txt y.go ! go build x stderr 'invalid pkg-config package name: @foo' # Reject #cgo CFLAGS: @foo -cp y_cflags_at_foo.txt x/y.go +cp y_cflags_at_foo.txt y.go ! go build x stderr 'invalid flag in #cgo CFLAGS: @foo' # Reject #cgo CFLAGS: -D -cp y_cflags_dash_d.txt x/y.go +cp y_cflags_dash_d.txt y.go ! go build x stderr 'invalid flag in #cgo CFLAGS: -D without argument' @@ -60,21 +59,25 @@ stderr 'invalid flag in #cgo CFLAGS: -D without argument' # before the check is applied. There's no such rewrite for -D. # Reject #cgo CFLAGS: -D @foo -cp y_cflags_dash_d_space_at_foo.txt x/y.go +cp y_cflags_dash_d_space_at_foo.txt y.go ! go build x stderr 'invalid flag in #cgo CFLAGS: -D @foo' # Reject #cgo CFLAGS -D@foo -cp y_cflags_dash_d_at_foo.txt x/y.go +cp y_cflags_dash_d_at_foo.txt y.go ! go build x stderr 'invalid flag in #cgo CFLAGS: -D@foo' # Check for CFLAGS in commands env CGO_CFLAGS=-D@foo -cp y_no_cflags.txt x/y.go +cp y_no_cflags.txt y.go go build -n x stderr '-D@foo' +-- go.mod -- +module x + +go 1.16 -- x_gc.go.txt -- package x @@ -123,4 +126,4 @@ package x import "C" -- y_no_cflags.txt -- package x -import "C" \ No newline at end of file +import "C" diff --git a/src/cmd/go/testdata/script/cgo_depends_on_syscall.txt b/src/cmd/go/testdata/script/cgo_depends_on_syscall.txt index e5fa84fdbb..bd4777c821 100644 --- a/src/cmd/go/testdata/script/cgo_depends_on_syscall.txt +++ b/src/cmd/go/testdata/script/cgo_depends_on_syscall.txt @@ -4,7 +4,11 @@ go list -race -deps foo stdout syscall --- foo/foo.go -- +-- go.mod -- +module foo + +go 1.16 +-- foo.go -- package foo // #include diff --git a/src/cmd/go/testdata/script/cover_asm.txt b/src/cmd/go/testdata/script/cover_asm.txt index 5241c7f0df..57f76d6c02 100644 --- a/src/cmd/go/testdata/script/cover_asm.txt +++ b/src/cmd/go/testdata/script/cover_asm.txt @@ -8,7 +8,11 @@ go tool cover -func=$WORK/cover.out stdout '\tg\t*100.0%' # Check g is 100% covered. ! stdout '\tf\t*[0-9]' # Check for no coverage on the assembly function --- coverasm/p.go -- +-- go.mod -- +module coverasm + +go 1.16 +-- p.go -- package p func f() @@ -16,10 +20,10 @@ func f() func g() { println("g") } --- coverasm/p.s -- +-- p.s -- // empty asm file, // so go test doesn't complain about declaration of f in p.go. --- coverasm/p_test.go -- +-- p_test.go -- package p import "testing" diff --git a/src/cmd/go/testdata/script/cover_blank_func_decl.txt b/src/cmd/go/testdata/script/cover_blank_func_decl.txt index 6fac4f87ea..e7d5250468 100644 --- a/src/cmd/go/testdata/script/cover_blank_func_decl.txt +++ b/src/cmd/go/testdata/script/cover_blank_func_decl.txt @@ -1,9 +1,13 @@ [short] skip -go test -cover ./coverblank +go test -cover coverblank stdout 'coverage: 100.0% of statements' --- coverblank/a.go -- +-- go.mod -- +module coverblank + +go 1.16 +-- a.go -- package coverblank func _() { @@ -20,7 +24,7 @@ func (x X) _() { println("unreachable") } --- coverblank/a_test.go -- +-- a_test.go -- package coverblank import "testing" diff --git a/src/cmd/go/testdata/script/cover_cgo.txt b/src/cmd/go/testdata/script/cover_cgo.txt index fdd0191ee0..9cf78f71e9 100644 --- a/src/cmd/go/testdata/script/cover_cgo.txt +++ b/src/cmd/go/testdata/script/cover_cgo.txt @@ -8,7 +8,11 @@ go test -short -cover cgocover stdout 'coverage:.*[1-9][0-9.]+%' ! stderr '[^0-9]0\.0%' --- cgocover/p.go -- +-- go.mod -- +module cgocover + +go 1.16 +-- p.go -- package p /* @@ -28,7 +32,7 @@ func F() { } C.f() } --- cgocover/p_test.go -- +-- p_test.go -- package p import "testing" diff --git a/src/cmd/go/testdata/script/cover_cgo_extra_file.txt b/src/cmd/go/testdata/script/cover_cgo_extra_file.txt index 483813bd6a..c53b979f9f 100644 --- a/src/cmd/go/testdata/script/cover_cgo_extra_file.txt +++ b/src/cmd/go/testdata/script/cover_cgo_extra_file.txt @@ -9,9 +9,13 @@ go test -short -cover cgocover4 stdout 'coverage:.*[1-9][0-9.]+%' ! stderr '[^0-9]0\.0%' --- cgocover4/notcgo.go -- +-- go.mod -- +module cgocover4 + +go 1.16 +-- notcgo.go -- package p --- cgocover4/p.go -- +-- p.go -- package p /* @@ -31,7 +35,7 @@ func F() { } C.f() } --- cgocover4/x_test.go -- +-- x_test.go -- package p_test import ( @@ -41,4 +45,4 @@ import ( func TestF(t *testing.T) { F() -} \ No newline at end of file +} diff --git a/src/cmd/go/testdata/script/cover_cgo_extra_test.txt b/src/cmd/go/testdata/script/cover_cgo_extra_test.txt index 92fc1ebdda..b501ab02a5 100644 --- a/src/cmd/go/testdata/script/cover_cgo_extra_test.txt +++ b/src/cmd/go/testdata/script/cover_cgo_extra_test.txt @@ -10,7 +10,11 @@ go test -short -cover cgocover3 stdout 'coverage:.*[1-9][0-9.]+%' ! stderr '[^0-9]0\.0%' --- cgocover3/p.go -- +-- go.mod -- +module cgocover3 + +go 1.16 +-- p.go -- package p /* @@ -30,9 +34,9 @@ func F() { } C.f() } --- cgocover3/p_test.go -- +-- p_test.go -- package p --- cgocover3/x_test.go -- +-- x_test.go -- package p_test import ( @@ -42,4 +46,4 @@ import ( func TestF(t *testing.T) { F() -} \ No newline at end of file +} diff --git a/src/cmd/go/testdata/script/cover_cgo_xtest.txt b/src/cmd/go/testdata/script/cover_cgo_xtest.txt index edf8112728..79cc08c481 100644 --- a/src/cmd/go/testdata/script/cover_cgo_xtest.txt +++ b/src/cmd/go/testdata/script/cover_cgo_xtest.txt @@ -8,7 +8,11 @@ go test -short -cover cgocover2 stdout 'coverage:.*[1-9][0-9.]+%' ! stderr '[^0-9]0\.0%' --- cgocover2/p.go -- +-- go.mod -- +module cgocover2 + +go 1.16 +-- p.go -- package p /* @@ -28,7 +32,7 @@ func F() { } C.f() } --- cgocover2/x_test.go -- +-- x_test.go -- package p_test import ( @@ -38,4 +42,4 @@ import ( func TestF(t *testing.T) { F() -} \ No newline at end of file +} diff --git a/src/cmd/go/testdata/script/cover_dash_c.txt b/src/cmd/go/testdata/script/cover_dash_c.txt index 61793cec49..8950f8d088 100644 --- a/src/cmd/go/testdata/script/cover_dash_c.txt +++ b/src/cmd/go/testdata/script/cover_dash_c.txt @@ -6,18 +6,22 @@ go test -c -o $WORK/coverdep -coverprofile=$WORK/no/such/dir/cover.out coverdep exists -exec $WORK/coverdep --- coverdep/p.go -- +-- go.mod -- +module coverdep + +go 1.16 +-- p.go -- package p import _ "coverdep/p1" func F() { } --- coverdep/p1/p1.go -- +-- p1/p1.go -- package p1 import _ "errors" --- coverdep/p_test.go -- +-- p_test.go -- package p import "testing" diff --git a/src/cmd/go/testdata/script/cover_dep_loop.txt b/src/cmd/go/testdata/script/cover_dep_loop.txt index 20b0c15d18..36ea6e00b3 100644 --- a/src/cmd/go/testdata/script/cover_dep_loop.txt +++ b/src/cmd/go/testdata/script/cover_dep_loop.txt @@ -7,11 +7,15 @@ go test -short -cover coverdep2/p1 stdout 'coverage: 100.0% of statements' # expect 100.0% coverage --- coverdep2/p1/p.go -- +-- go.mod -- +module coverdep2 + +go 1.16 +-- p1/p.go -- package p1 func F() int { return 1 } --- coverdep2/p1/p_test.go -- +-- p1/p_test.go -- package p1_test import ( @@ -22,7 +26,7 @@ import ( func Test(t *testing.T) { p2.F() } --- coverdep2/p2/p2.go -- +-- p2/p2.go -- package p2 import "coverdep2/p1" diff --git a/src/cmd/go/testdata/script/cover_dot_import.txt b/src/cmd/go/testdata/script/cover_dot_import.txt index e07be22d6c..d492e42e2a 100644 --- a/src/cmd/go/testdata/script/cover_dot_import.txt +++ b/src/cmd/go/testdata/script/cover_dot_import.txt @@ -1,22 +1,26 @@ [short] skip [gccgo] skip # gccgo has no cover tool -go test -coverpkg=coverdot1,coverdot2 coverdot2 +go test -coverpkg=coverdot/a,coverdot/b coverdot/b ! stderr '[^0-9]0\.0%' ! stdout '[^0-9]0\.0%' --- coverdot1/p.go -- -package coverdot1 +-- go.mod -- +module coverdot + +go 1.16 +-- a/a.go -- +package a func F() {} --- coverdot2/p.go -- -package coverdot2 +-- b/b.go -- +package b -import . "coverdot1" +import . "coverdot/a" func G() { F() } --- coverdot2/p_test.go -- -package coverdot2 +-- b/b_test.go -- +package b import "testing" diff --git a/src/cmd/go/testdata/script/cover_error.txt b/src/cmd/go/testdata/script/cover_error.txt index 6ba0f08a2b..4abdf1137a 100644 --- a/src/cmd/go/testdata/script/cover_error.txt +++ b/src/cmd/go/testdata/script/cover_error.txt @@ -5,8 +5,8 @@ # Get errors from a go test into stderr.txt ! go test coverbad -stderr 'coverbad[\\/]p\.go:4' # look for error at coverbad/p.go:4 -[cgo] stderr 'coverbad[\\/]p1\.go:6' # look for error at coverbad/p.go:6 +stderr 'p\.go:4' # look for error at coverbad/p.go:4 +[cgo] stderr 'p1\.go:6' # look for error at coverbad/p.go:6 ! stderr $WORK # make sure temporary directory isn't in error cp stderr $WORK/stderr.txt @@ -24,13 +24,17 @@ wait # for go run above cmp $WORK/stderr.txt $WORK/stderr2.txt --- coverbad/p.go -- +-- go.mod -- +module coverbad + +go 1.16 +-- p.go -- package p func f() { g() } --- coverbad/p1.go -- +-- p1.go -- package p import "C" @@ -38,13 +42,15 @@ import "C" func h() { j() } --- coverbad/p_test.go -- +-- p_test.go -- package p import "testing" func Test(t *testing.T) {} -- clean_charpos.go -- +// +build ignore + package main import ( @@ -66,4 +72,4 @@ func main() { if err != nil { log.Fatal(err) } -} \ No newline at end of file +} diff --git a/src/cmd/go/testdata/script/cover_import_main_loop.txt b/src/cmd/go/testdata/script/cover_import_main_loop.txt index 83eef0c8a8..eb6de6778a 100644 --- a/src/cmd/go/testdata/script/cover_import_main_loop.txt +++ b/src/cmd/go/testdata/script/cover_import_main_loop.txt @@ -5,15 +5,19 @@ stderr 'not an importable package' # check that import main was detected ! go test -n -cover importmain/test stderr 'not an importable package' # check that import main was detected --- importmain/ismain/main.go -- +-- go.mod -- +module importmain + +go 1.16 +-- ismain/main.go -- package main import _ "importmain/test" func main() {} --- importmain/test/test.go -- +-- test/test.go -- package test --- importmain/test/test_test.go -- +-- test/test_test.go -- package test_test import "testing" diff --git a/src/cmd/go/testdata/script/cover_pattern.txt b/src/cmd/go/testdata/script/cover_pattern.txt index 0b7f2d70a2..ec0850c003 100644 --- a/src/cmd/go/testdata/script/cover_pattern.txt +++ b/src/cmd/go/testdata/script/cover_pattern.txt @@ -1,12 +1,16 @@ [gccgo] skip -# If coverpkg=sleepy... expands by package loading +# If coverpkg=m/sleepy... expands by package loading # (as opposed to pattern matching on deps) # then it will try to load sleepybad, which does not compile, # and the test command will fail. -! go list sleepy... -go test -c -n -coverprofile=$TMPDIR/cover.out -coverpkg=sleepy... -run=^$ sleepy1 +! go list m/sleepy... +go test -c -n -coverprofile=$TMPDIR/cover.out -coverpkg=m/sleepy... -run=^$ m/sleepy1 +-- go.mod -- +module m + +go 1.16 -- sleepy1/p_test.go -- package p diff --git a/src/cmd/go/testdata/script/cover_statements.txt b/src/cmd/go/testdata/script/cover_statements.txt index 314ea6bead..4f3c9ca2f2 100644 --- a/src/cmd/go/testdata/script/cover_statements.txt +++ b/src/cmd/go/testdata/script/cover_statements.txt @@ -5,6 +5,10 @@ stdout 'pkg2 \S+ coverage: 0.0% of statements \[no tests to run\]' stdout 'pkg3 \S+ coverage: 100.0% of statements' stdout 'pkg4 \S+ coverage: \[no statements\]' +-- go.mod -- +module m + +go 1.16 -- pkg1/a.go -- package pkg1 diff --git a/src/cmd/go/testdata/script/cover_sync_atomic_import.txt b/src/cmd/go/testdata/script/cover_sync_atomic_import.txt index 769c03ea83..433af9ab73 100644 --- a/src/cmd/go/testdata/script/cover_sync_atomic_import.txt +++ b/src/cmd/go/testdata/script/cover_sync_atomic_import.txt @@ -3,22 +3,26 @@ go test -short -cover -covermode=atomic -coverpkg=coverdep/p1 coverdep --- coverdep/p.go -- +-- go.mod -- +module coverdep + +go 1.16 +-- p.go -- package p import _ "coverdep/p1" func F() { } --- coverdep/p1/p1.go -- +-- p1/p1.go -- package p1 import _ "errors" --- coverdep/p_test.go -- +-- p_test.go -- package p import "testing" func Test(t *testing.T) { F() -} \ No newline at end of file +} diff --git a/src/cmd/go/testdata/script/generate_bad_imports.txt b/src/cmd/go/testdata/script/generate_bad_imports.txt index 59a2f5786b..4d31573d56 100644 --- a/src/cmd/go/testdata/script/generate_bad_imports.txt +++ b/src/cmd/go/testdata/script/generate_bad_imports.txt @@ -3,7 +3,11 @@ go generate gencycle stdout 'hello world' # check go generate gencycle ran the generator --- gencycle/gencycle.go -- +-- go.mod -- +module gencycle + +go 1.16 +-- gencycle.go -- //go:generate echo hello world package gencycle diff --git a/src/cmd/go/testdata/script/generate_invalid.txt b/src/cmd/go/testdata/script/generate_invalid.txt index 62aa9dd9ba..e18e62ccf3 100644 --- a/src/cmd/go/testdata/script/generate_invalid.txt +++ b/src/cmd/go/testdata/script/generate_invalid.txt @@ -54,6 +54,10 @@ func main() { fmt.Println() } +-- go.mod -- +module m + +go 1.16 -- nogo/foo.txt -- Text file in a directory without go files. Go generate should ignore this directory. @@ -196,4 +200,4 @@ import "bar" package importerr import "moo" -//go:generate echo Success c \ No newline at end of file +//go:generate echo Success c diff --git a/src/cmd/go/testdata/script/get_custom_domain_wildcard.txt b/src/cmd/go/testdata/script/get_custom_domain_wildcard.txt index 743fbb3ea4..cda25e12b0 100644 --- a/src/cmd/go/testdata/script/get_custom_domain_wildcard.txt +++ b/src/cmd/go/testdata/script/get_custom_domain_wildcard.txt @@ -1,5 +1,6 @@ [!net] skip [!exec:git] skip +env GO111MODULE=off go get -u rsc.io/pdf/... -exists $GOPATH/bin/pdfpasswd$GOEXE \ No newline at end of file +exists $GOPATH/bin/pdfpasswd$GOEXE diff --git a/src/cmd/go/testdata/script/get_dash_t.txt b/src/cmd/go/testdata/script/get_dash_t.txt index be5c8dd5ca..baac916868 100644 --- a/src/cmd/go/testdata/script/get_dash_t.txt +++ b/src/cmd/go/testdata/script/get_dash_t.txt @@ -2,7 +2,8 @@ [!net] skip [!exec:git] skip +env GO111MODULE=off go get -v -t github.com/rsc/go-get-issue-8181/a github.com/rsc/go-get-issue-8181/b go list ... -stdout 'x/build/gerrit' \ No newline at end of file +stdout 'x/build/gerrit' diff --git a/src/cmd/go/testdata/script/get_domain_root.txt b/src/cmd/go/testdata/script/get_domain_root.txt index c2e9db35ec..918784869b 100644 --- a/src/cmd/go/testdata/script/get_domain_root.txt +++ b/src/cmd/go/testdata/script/get_domain_root.txt @@ -3,6 +3,7 @@ [!net] skip [!exec:git] skip +env GO111MODULE=off # go-get-issue-9357.appspot.com is running # the code at github.com/rsc/go-get-issue-9357, @@ -16,4 +17,4 @@ rm $GOPATH/src/go-get-issue-9357.appspot.com go get go-get-issue-9357.appspot.com rm $GOPATH/src/go-get-issue-9357.appspot.com -go get -u go-get-issue-9357.appspot.com \ No newline at end of file +go get -u go-get-issue-9357.appspot.com diff --git a/src/cmd/go/testdata/script/get_dot_slash_download.txt b/src/cmd/go/testdata/script/get_dot_slash_download.txt index 0396e1b278..dbaf46ced3 100644 --- a/src/cmd/go/testdata/script/get_dot_slash_download.txt +++ b/src/cmd/go/testdata/script/get_dot_slash_download.txt @@ -1,9 +1,10 @@ [!net] skip [!exec:git] skip +env GO111MODULE=off # Tests Issues #9797 and #19769 mkdir $WORK/tmp/src/rsc.io env GOPATH=$WORK/tmp cd $WORK/tmp/src/rsc.io -go get ./pprof_mac_fix \ No newline at end of file +go get ./pprof_mac_fix diff --git a/src/cmd/go/testdata/script/get_goroot.txt b/src/cmd/go/testdata/script/get_goroot.txt index 49f1a174d0..929435ad70 100644 --- a/src/cmd/go/testdata/script/get_goroot.txt +++ b/src/cmd/go/testdata/script/get_goroot.txt @@ -1,4 +1,5 @@ [!net] skip +env GO111MODULE=off # Issue 4186. go get cannot be used to download packages to $GOROOT. # Test that without GOPATH set, go get should fail. @@ -49,4 +50,4 @@ stderr '\$GOPATH not set' env GOPATH= env GOROOT=$WORK/home/go/ ! go get -d github.com/golang/example/hello -stderr '\$GOPATH not set' \ No newline at end of file +stderr '\$GOPATH not set' diff --git a/src/cmd/go/testdata/script/get_insecure_custom_domain.txt b/src/cmd/go/testdata/script/get_insecure_custom_domain.txt index c0439fb037..a4a6fd428f 100644 --- a/src/cmd/go/testdata/script/get_insecure_custom_domain.txt +++ b/src/cmd/go/testdata/script/get_insecure_custom_domain.txt @@ -1,5 +1,6 @@ [!net] skip [!exec:git] skip +env GO111MODULE=off ! go get -d insecure.go-get-issue-15410.appspot.com/pkg/p -go get -d -insecure insecure.go-get-issue-15410.appspot.com/pkg/p \ No newline at end of file +go get -d -insecure insecure.go-get-issue-15410.appspot.com/pkg/p diff --git a/src/cmd/go/testdata/script/get_insecure_update.txt b/src/cmd/go/testdata/script/get_insecure_update.txt index 792c868151..4511c98c56 100644 --- a/src/cmd/go/testdata/script/get_insecure_update.txt +++ b/src/cmd/go/testdata/script/get_insecure_update.txt @@ -1,5 +1,6 @@ [!net] skip [!exec:git] skip +env GO111MODULE=off # Clone the repo via HTTP manually. exec git clone -q http://github.com/golang/example github.com/golang/example @@ -8,4 +9,4 @@ exec git clone -q http://github.com/golang/example github.com/golang/example # Update with -insecure should succeed. # We need -f to ignore import comments. ! go get -d -u -f github.com/golang/example/hello -go get -d -u -f -insecure github.com/golang/example/hello \ No newline at end of file +go get -d -u -f -insecure github.com/golang/example/hello diff --git a/src/cmd/go/testdata/script/get_internal_wildcard.txt b/src/cmd/go/testdata/script/get_internal_wildcard.txt index 82bb0d5ba5..ff20d4ba04 100644 --- a/src/cmd/go/testdata/script/get_internal_wildcard.txt +++ b/src/cmd/go/testdata/script/get_internal_wildcard.txt @@ -1,5 +1,6 @@ [!net] skip [!exec:git] skip +env GO111MODULE=off # This used to fail with errors about internal packages -go get github.com/rsc/go-get-issue-11960/... \ No newline at end of file +go get github.com/rsc/go-get-issue-11960/... diff --git a/src/cmd/go/testdata/script/get_issue11307.txt b/src/cmd/go/testdata/script/get_issue11307.txt index da7704dee5..9d6b7dde01 100644 --- a/src/cmd/go/testdata/script/get_issue11307.txt +++ b/src/cmd/go/testdata/script/get_issue11307.txt @@ -2,7 +2,8 @@ [!net] skip [!exec:git] skip +env GO111MODULE=off env GOPATH=$WORK/tmp/gopath go get github.com/rsc/go-get-issue-11307 -go get -u github.com/rsc/go-get-issue-11307 # was failing \ No newline at end of file +go get -u github.com/rsc/go-get-issue-11307 # was failing diff --git a/src/cmd/go/testdata/script/get_legacy.txt b/src/cmd/go/testdata/script/get_legacy.txt new file mode 100644 index 0000000000..938d42868a --- /dev/null +++ b/src/cmd/go/testdata/script/get_legacy.txt @@ -0,0 +1,58 @@ +# This test was converted from a test in vendor_test.go (which no longer exists). +# That seems to imply that it's about vendoring semantics, but the test doesn't +# use 'go -mod=vendor' (and none of the fetched repos have vendor folders). +# The test still seems to be useful as a test of direct-mode go get. + +[short] skip +[!exec:git] skip +env GO111MODULE=off + +env GOPATH=$WORK/tmp/d1 +go get vcs-test.golang.org/git/modlegacy1-old.git/p1 +go list -f '{{.Deps}}' vcs-test.golang.org/git/modlegacy1-old.git/p1 +stdout 'new.git/p2' # old/p1 should depend on new/p2 +! stdout new.git/v2/p2 # old/p1 should NOT depend on new/v2/p2 +go build vcs-test.golang.org/git/modlegacy1-old.git/p1 vcs-test.golang.org/git/modlegacy1-new.git/p1 +! stdout . + +env GOPATH=$WORK/tmp/d2 + +rm $GOPATH +go get github.com/rsc/vgotest5 +go get github.com/rsc/vgotest4 +go get github.com/myitcv/vgo_example_compat + +rm $GOPATH +go get github.com/rsc/vgotest4 +go get github.com/rsc/vgotest5 +go get github.com/myitcv/vgo_example_compat + +rm $GOPATH +go get github.com/rsc/vgotest4 github.com/rsc/vgotest5 +go get github.com/myitcv/vgo_example_compat + +rm $GOPATH +go get github.com/rsc/vgotest5 github.com/rsc/vgotest4 +go get github.com/myitcv/vgo_example_compat + +rm $GOPATH +go get github.com/myitcv/vgo_example_compat +go get github.com/rsc/vgotest5 github.com/rsc/vgotest4 + +rm $GOPATH +go get github.com/myitcv/vgo_example_compat github.com/rsc/vgotest4 github.com/rsc/vgotest5 + +rm $GOPATH +go get github.com/myitcv/vgo_example_compat github.com/rsc/vgotest5 github.com/rsc/vgotest4 + +rm $GOPATH +go get github.com/rsc/vgotest4 github.com/myitcv/vgo_example_compat github.com/rsc/vgotest5 + +rm $GOPATH +go get github.com/rsc/vgotest4 github.com/rsc/vgotest5 github.com/myitcv/vgo_example_compat + +rm $GOPATH +go get github.com/rsc/vgotest5 github.com/myitcv/vgo_example_compat github.com/rsc/vgotest4 + +rm $GOPATH +go get github.com/rsc/vgotest5 github.com/rsc/vgotest4 github.com/myitcv/vgo_example_compat diff --git a/src/cmd/go/testdata/script/get_race.txt b/src/cmd/go/testdata/script/get_race.txt index 8b34c9596c..16a560afca 100644 --- a/src/cmd/go/testdata/script/get_race.txt +++ b/src/cmd/go/testdata/script/get_race.txt @@ -3,5 +3,6 @@ [!net] skip [!exec:git] skip [!race] skip +env GO111MODULE=off -go get -race github.com/rsc/go-get-issue-9224-cmd \ No newline at end of file +go get -race github.com/rsc/go-get-issue-9224-cmd diff --git a/src/cmd/go/testdata/script/get_test_only.txt b/src/cmd/go/testdata/script/get_test_only.txt index 7437c30e77..a3f38ddbab 100644 --- a/src/cmd/go/testdata/script/get_test_only.txt +++ b/src/cmd/go/testdata/script/get_test_only.txt @@ -1,5 +1,6 @@ [!net] skip [!exec:git] skip +env GO111MODULE=off go get golang.org/x/tour/content... -go get -t golang.org/x/tour/content... \ No newline at end of file +go get -t golang.org/x/tour/content... diff --git a/src/cmd/go/testdata/script/get_update.txt b/src/cmd/go/testdata/script/get_update.txt index df889c49b0..9afce6a443 100644 --- a/src/cmd/go/testdata/script/get_update.txt +++ b/src/cmd/go/testdata/script/get_update.txt @@ -4,6 +4,7 @@ [!net] skip [!exec:git] skip +env GO111MODULE=off # Rewind go get github.com/rsc/go-get-issue-9224-cmd @@ -21,4 +22,4 @@ exec git reset --hard HEAD~ cd $GOPATH/src # (Again with -d -u) Run get -go get -d -u 'github.com/rsc/go-get-issue-9224-cmd' \ No newline at end of file +go get -d -u 'github.com/rsc/go-get-issue-9224-cmd' diff --git a/src/cmd/go/testdata/script/get_update_unknown_protocol.txt b/src/cmd/go/testdata/script/get_update_unknown_protocol.txt index 85c2e24bc8..b00adea70b 100644 --- a/src/cmd/go/testdata/script/get_update_unknown_protocol.txt +++ b/src/cmd/go/testdata/script/get_update_unknown_protocol.txt @@ -1,5 +1,6 @@ [!net] skip [!exec:git] skip +env GO111MODULE=off # Clone the repo via HTTPS manually. exec git clone -q https://github.com/golang/example github.com/golang/example @@ -10,4 +11,4 @@ cd github.com/golang/example exec git remote set-url origin xyz://github.com/golang/example exec git config --local url.https://github.com/.insteadOf xyz://github.com/ -go get -d -u -f github.com/golang/example/hello \ No newline at end of file +go get -d -u -f github.com/golang/example/hello diff --git a/src/cmd/go/testdata/script/get_update_wildcard.txt b/src/cmd/go/testdata/script/get_update_wildcard.txt index bfa47a2a4c..4e66004014 100644 --- a/src/cmd/go/testdata/script/get_update_wildcard.txt +++ b/src/cmd/go/testdata/script/get_update_wildcard.txt @@ -2,6 +2,7 @@ [!net] skip [!exec:git] skip +env GO111MODULE=off go get github.com/tmwh/go-get-issue-14450/a ! go get -u .../ @@ -12,4 +13,4 @@ exists github.com/tmwh/go-get-issue-14450/b exists github.com/tmwh/go-get-issue-14450-b-dependency/c exists github.com/tmwh/go-get-issue-14450-b-dependency/d -! exists github.com/tmwh/go-get-issue-14450-c-dependency/e \ No newline at end of file +! exists github.com/tmwh/go-get-issue-14450-c-dependency/e diff --git a/src/cmd/go/testdata/script/get_vcs_error_message.txt b/src/cmd/go/testdata/script/get_vcs_error_message.txt index e2404cc8d9..8dc84fc727 100644 --- a/src/cmd/go/testdata/script/get_vcs_error_message.txt +++ b/src/cmd/go/testdata/script/get_vcs_error_message.txt @@ -1,4 +1,5 @@ # Test that the Version Control error message includes the correct directory +env GO111MODULE=off ! go get -u foo stderr gopath(\\\\|/)src(\\\\|/)foo diff --git a/src/cmd/go/testdata/script/get_vendor.txt b/src/cmd/go/testdata/script/get_vendor.txt index a6f0a70c48..4ebb8a26b6 100644 --- a/src/cmd/go/testdata/script/get_vendor.txt +++ b/src/cmd/go/testdata/script/get_vendor.txt @@ -1,4 +1,5 @@ [short] skip +env GO111MODULE=off cd $GOPATH/src/v go run m.go @@ -91,4 +92,4 @@ func TestNothing(t *testing.T) { } -- v/vendor/vendor.org/p/p.go -- package p -const C = 1 \ No newline at end of file +const C = 1 diff --git a/src/cmd/go/testdata/script/gopath_vendor_dup_err.txt b/src/cmd/go/testdata/script/gopath_vendor_dup_err.txt index 5096195c70..22e6048e96 100644 --- a/src/cmd/go/testdata/script/gopath_vendor_dup_err.txt +++ b/src/cmd/go/testdata/script/gopath_vendor_dup_err.txt @@ -1,4 +1,5 @@ [!net] skip +env GO111MODULE=off # Issue 17119: Test more duplicate load errors. ! go build dupload diff --git a/src/cmd/go/testdata/script/install_cgo_excluded.txt b/src/cmd/go/testdata/script/install_cgo_excluded.txt index fa1fcd67a4..5a2b46030f 100644 --- a/src/cmd/go/testdata/script/install_cgo_excluded.txt +++ b/src/cmd/go/testdata/script/install_cgo_excluded.txt @@ -3,7 +3,11 @@ env CGO_ENABLED=0 ! go install cgotest stderr 'build constraints exclude all Go files' --- cgotest/m.go -- +-- go.mod -- +module cgotest + +go 1.16 +-- m.go -- package cgotest import "C" diff --git a/src/cmd/go/testdata/script/install_relative_gobin_fail.txt b/src/cmd/go/testdata/script/install_relative_gobin_fail.txt index e1e9ec7bdf..aa145249c5 100644 --- a/src/cmd/go/testdata/script/install_relative_gobin_fail.txt +++ b/src/cmd/go/testdata/script/install_relative_gobin_fail.txt @@ -2,7 +2,11 @@ env GOBIN=. ! go install stderr 'cannot install, GOBIN must be an absolute path' +-- go.mod -- +module triv + +go 1.16 -- triv.go -- package main -func main() {} \ No newline at end of file +func main() {} diff --git a/src/cmd/go/testdata/script/install_shadow_gopath.txt b/src/cmd/go/testdata/script/install_shadow_gopath.txt index 995162172e..2039d9e7f2 100644 --- a/src/cmd/go/testdata/script/install_shadow_gopath.txt +++ b/src/cmd/go/testdata/script/install_shadow_gopath.txt @@ -3,6 +3,7 @@ [!net] skip +env GO111MODULE=off env GOPATH=$WORK/gopath1${:}$WORK/gopath2 mkdir $WORK/gopath1/src/test @@ -16,4 +17,4 @@ stderr 'no install location for.*gopath2.src.test: hidden by .*gopath1.src.test' -- main.go -- package main -func main() {} \ No newline at end of file +func main() {} diff --git a/src/cmd/go/testdata/script/link_syso_issue33139.txt b/src/cmd/go/testdata/script/link_syso_issue33139.txt index 46b0ef4200..d4f0b87537 100644 --- a/src/cmd/go/testdata/script/link_syso_issue33139.txt +++ b/src/cmd/go/testdata/script/link_syso_issue33139.txt @@ -15,6 +15,10 @@ cc -c -o syso/objTestImpl.syso syso/src/objTestImpl.c go build -ldflags='-linkmode=external' ./cmd/main.go +-- go.mod -- +module m + +go 1.16 -- syso/objTest.s -- #include "textflag.h" @@ -36,7 +40,7 @@ void objTestImpl() { /* Empty */ } -- cmd/main.go -- package main -import "syso" +import "m/syso" func main() { syso.ObjTest() diff --git a/src/cmd/go/testdata/script/list_case_collision.txt b/src/cmd/go/testdata/script/list_case_collision.txt index 73f44b63a0..181a202989 100644 --- a/src/cmd/go/testdata/script/list_case_collision.txt +++ b/src/cmd/go/testdata/script/list_case_collision.txt @@ -8,30 +8,34 @@ stderr 'case-insensitive import collision' # List files explicitly on command line, to encounter case-checking # logic even on case-insensitive filesystems. -cp example/b/file.go example/b/FILE.go # no-op on case-insensitive filesystems -! go list example/b/file.go example/b/FILE.go +cp b/file.go b/FILE.go # no-op on case-insensitive filesystems +! go list b/file.go b/FILE.go stderr 'case-insensitive file name collision' -mkdir example/a/Pkg # no-op on case-insensitive filesystems -cp example/a/pkg/pkg.go example/a/Pkg/pkg.go # no-op on case-insensitive filesystems +mkdir a/Pkg # no-op on case-insensitive filesystems +cp a/pkg/pkg.go a/Pkg/pkg.go # no-op on case-insensitive filesystems ! go list example/a/pkg example/a/Pkg # Test that the path reported with an indirect import is correct. -cp example/b/file.go example/b/FILE.go +cp b/file.go b/FILE.go [case-sensitive] ! go build example/c [case-sensitive] stderr '^package example/c\n\timports example/b: case-insensitive file name collision: "FILE.go" and "file.go"$' --- example/a/a.go -- +-- go.mod -- +module example + +go 1.16 +-- a/a.go -- package p import ( _ "example/a/pkg" _ "example/a/Pkg" ) --- example/a/pkg/pkg.go -- +-- a/pkg/pkg.go -- package pkg --- example/b/file.go -- +-- b/file.go -- package b --- example/c/c.go -- +-- c/c.go -- package c import _ "example/b" diff --git a/src/cmd/go/testdata/script/list_dedup_packages.txt b/src/cmd/go/testdata/script/list_dedup_packages.txt index ebd497b7e5..30c68dd77f 100644 --- a/src/cmd/go/testdata/script/list_dedup_packages.txt +++ b/src/cmd/go/testdata/script/list_dedup_packages.txt @@ -1,4 +1,5 @@ # Setup +env GO111MODULE=off mkdir $WORK/tmp/testdata/src/xtestonly cp f.go $WORK/tmp/testdata/src/xtestonly/f.go cp f_test.go $WORK/tmp/testdata/src/xtestonly/f_test.go diff --git a/src/cmd/go/testdata/script/list_symlink.txt b/src/cmd/go/testdata/script/list_symlink.txt index 20c85b6453..f74ca86f37 100644 --- a/src/cmd/go/testdata/script/list_symlink.txt +++ b/src/cmd/go/testdata/script/list_symlink.txt @@ -1,4 +1,5 @@ [!symlink] skip +env GO111MODULE=off mkdir $WORK/tmp/src symlink $WORK/tmp/src/dir1 -> $WORK/tmp diff --git a/src/cmd/go/testdata/script/list_symlink_internal.txt b/src/cmd/go/testdata/script/list_symlink_internal.txt index e538072b33..f756a56a3f 100644 --- a/src/cmd/go/testdata/script/list_symlink_internal.txt +++ b/src/cmd/go/testdata/script/list_symlink_internal.txt @@ -1,4 +1,5 @@ [!symlink] skip +env GO111MODULE=off mkdir $WORK/tmp/gopath/src/dir1/internal/v cp p.go $WORK/tmp/gopath/src/dir1/p.go @@ -23,4 +24,4 @@ import _ `dir1/internal/v` func main() {} -- v.go -- -package v \ No newline at end of file +package v diff --git a/src/cmd/go/testdata/script/list_symlink_vendor_issue14054.txt b/src/cmd/go/testdata/script/list_symlink_vendor_issue14054.txt index 68b7fd948b..8e63a5a259 100644 --- a/src/cmd/go/testdata/script/list_symlink_vendor_issue14054.txt +++ b/src/cmd/go/testdata/script/list_symlink_vendor_issue14054.txt @@ -1,4 +1,5 @@ [!symlink] skip +env GO111MODULE=off mkdir $WORK/tmp/gopath/src/dir1/vendor/v cp p.go $WORK/tmp/gopath/src/dir1/p.go @@ -24,4 +25,4 @@ import _ `v` func main () {} -- v.go -- -package v \ No newline at end of file +package v diff --git a/src/cmd/go/testdata/script/list_symlink_vendor_issue15201.txt b/src/cmd/go/testdata/script/list_symlink_vendor_issue15201.txt index 98921614a9..19f2138250 100644 --- a/src/cmd/go/testdata/script/list_symlink_vendor_issue15201.txt +++ b/src/cmd/go/testdata/script/list_symlink_vendor_issue15201.txt @@ -1,4 +1,5 @@ [!symlink] skip +env GO111MODULE=off mkdir $WORK/tmp/gopath/src/x/y/_vendor/src/x symlink $WORK/tmp/gopath/src/x/y/_vendor/src/x/y -> ../../.. @@ -17,4 +18,4 @@ package w import "x/y/z" -- z.go -- -package z \ No newline at end of file +package z diff --git a/src/cmd/go/testdata/script/list_test_simple.txt b/src/cmd/go/testdata/script/list_test_simple.txt index 862b7a8fbb..954897c663 100644 --- a/src/cmd/go/testdata/script/list_test_simple.txt +++ b/src/cmd/go/testdata/script/list_test_simple.txt @@ -1,21 +1,23 @@ [short] skip -cd $WORK - # Test -go test './gopath/src/testlist/...' -list=Test +go test -list=Test stdout TestSimple # Benchmark -go test './gopath/src/testlist/...' -list=Benchmark +go test -list=Benchmark stdout BenchmarkSimple # Examples -go test './gopath/src/testlist/...' -list=Example +go test -list=Example stdout ExampleSimple stdout ExampleWithEmptyOutput --- testlist/bench_test.go -- +-- go.mod -- +module m + +go 1.16 +-- bench_test.go -- package testlist import ( @@ -30,7 +32,7 @@ func BenchmarkSimplefunc(b *testing.B) { _ = fmt.Sprint("Test for bench") } } --- testlist/example_test.go -- +-- example_test.go -- package testlist import ( @@ -52,7 +54,7 @@ func ExampleWithEmptyOutput() { func ExampleNoOutput() { _ = fmt.Sprint("Test with no output") } --- testlist/test_test.go -- +-- test_test.go -- package testlist import ( @@ -62,4 +64,4 @@ import ( func TestSimple(t *testing.T) { _ = fmt.Sprint("Test simple") -} \ No newline at end of file +} diff --git a/src/cmd/go/testdata/script/list_wildcard_skip_nonmatching.txt b/src/cmd/go/testdata/script/list_wildcard_skip_nonmatching.txt index 74ca315a72..02b1088297 100644 --- a/src/cmd/go/testdata/script/list_wildcard_skip_nonmatching.txt +++ b/src/cmd/go/testdata/script/list_wildcard_skip_nonmatching.txt @@ -1,13 +1,17 @@ # Test that wildcards don't look in useless directories. # First make sure that badpkg fails the list of '...'. -! go list ... +! go list ./... stderr badpkg -# Check that the list of 'm...' succeeds. That implies badpkg was skipped. -go list m... +# Check that the list of './goodpkg...' succeeds. That implies badpkg was skipped. +go list ./goodpkg... --- m/x.go -- -package m +-- go.mod -- +module m + +go 1.16 +-- goodpkg/x.go -- +package goodpkg -- badpkg/x.go -- -pkg badpkg \ No newline at end of file +pkg badpkg diff --git a/src/cmd/go/testdata/script/load_test_pkg_err.txt b/src/cmd/go/testdata/script/load_test_pkg_err.txt index b3065490de..d088ee40f3 100644 --- a/src/cmd/go/testdata/script/load_test_pkg_err.txt +++ b/src/cmd/go/testdata/script/load_test_pkg_err.txt @@ -10,17 +10,21 @@ stdout golang.org/fake/d d d.test d_test [d.test] --- d/d.go -- +-- go.mod -- +module d + +go 1.16 +-- d.go -- package d import "net/http" const d = http.MethodGet func Get() string { return d; } --- d/d2.go -- --- d/d_test.go -- +-- d2.go -- +-- d_test.go -- package d_test import "testing" import "golang.org/fake/d" -func TestD(t *testing.T) { d.Get(); } \ No newline at end of file +func TestD(t *testing.T) { d.Get(); } diff --git a/src/cmd/go/testdata/script/mod_get_legacy.txt b/src/cmd/go/testdata/script/mod_get_legacy.txt deleted file mode 100644 index 28a820e97b..0000000000 --- a/src/cmd/go/testdata/script/mod_get_legacy.txt +++ /dev/null @@ -1,57 +0,0 @@ -# This test was converted from a test in vendor_test.go (which no longer exists). -# That seems to imply that it's about vendoring semantics, but the test doesn't -# use 'go -mod=vendor' (and none of the fetched repos have vendor folders). -# The test still seems to be useful as a test of direct-mode go get. - -[short] skip -[!exec:git] skip - -env GOPATH=$WORK/tmp/d1 -go get vcs-test.golang.org/git/modlegacy1-old.git/p1 -go list -f '{{.Deps}}' vcs-test.golang.org/git/modlegacy1-old.git/p1 -stdout 'new.git/p2' # old/p1 should depend on new/p2 -! stdout new.git/v2/p2 # old/p1 should NOT depend on new/v2/p2 -go build vcs-test.golang.org/git/modlegacy1-old.git/p1 vcs-test.golang.org/git/modlegacy1-new.git/p1 -! stdout . - -env GOPATH=$WORK/tmp/d2 - -rm $GOPATH -go get github.com/rsc/vgotest5 -go get github.com/rsc/vgotest4 -go get github.com/myitcv/vgo_example_compat - -rm $GOPATH -go get github.com/rsc/vgotest4 -go get github.com/rsc/vgotest5 -go get github.com/myitcv/vgo_example_compat - -rm $GOPATH -go get github.com/rsc/vgotest4 github.com/rsc/vgotest5 -go get github.com/myitcv/vgo_example_compat - -rm $GOPATH -go get github.com/rsc/vgotest5 github.com/rsc/vgotest4 -go get github.com/myitcv/vgo_example_compat - -rm $GOPATH -go get github.com/myitcv/vgo_example_compat -go get github.com/rsc/vgotest5 github.com/rsc/vgotest4 - -rm $GOPATH -go get github.com/myitcv/vgo_example_compat github.com/rsc/vgotest4 github.com/rsc/vgotest5 - -rm $GOPATH -go get github.com/myitcv/vgo_example_compat github.com/rsc/vgotest5 github.com/rsc/vgotest4 - -rm $GOPATH -go get github.com/rsc/vgotest4 github.com/myitcv/vgo_example_compat github.com/rsc/vgotest5 - -rm $GOPATH -go get github.com/rsc/vgotest4 github.com/rsc/vgotest5 github.com/myitcv/vgo_example_compat - -rm $GOPATH -go get github.com/rsc/vgotest5 github.com/myitcv/vgo_example_compat github.com/rsc/vgotest4 - -rm $GOPATH -go get github.com/rsc/vgotest5 github.com/rsc/vgotest4 github.com/myitcv/vgo_example_compat \ No newline at end of file diff --git a/src/cmd/go/testdata/script/run_hello_pkg.txt b/src/cmd/go/testdata/script/run_hello_pkg.txt index 03fba13c77..ea2b4d7cde 100644 --- a/src/cmd/go/testdata/script/run_hello_pkg.txt +++ b/src/cmd/go/testdata/script/run_hello_pkg.txt @@ -1,11 +1,14 @@ -cd $GOPATH -go run hello +go run m/hello stderr 'hello, world' -cd src/hello +cd hello go run . stderr 'hello, world' +-- go.mod -- +module m + +go 1.16 -- hello/hello.go -- package main diff --git a/src/cmd/go/testdata/script/run_vendor.txt b/src/cmd/go/testdata/script/run_vendor.txt index 8544281db9..46cac06bf4 100644 --- a/src/cmd/go/testdata/script/run_vendor.txt +++ b/src/cmd/go/testdata/script/run_vendor.txt @@ -1,4 +1,5 @@ # Run +env GO111MODULE=off cd vend/hello go run hello.go stdout 'hello, world' diff --git a/src/cmd/go/testdata/script/test_benchmark_fatal.txt b/src/cmd/go/testdata/script/test_benchmark_fatal.txt index 1e20c4eb61..e281379ebd 100644 --- a/src/cmd/go/testdata/script/test_benchmark_fatal.txt +++ b/src/cmd/go/testdata/script/test_benchmark_fatal.txt @@ -5,7 +5,11 @@ ! stderr ^ok stdout FAIL.*benchfatal --- benchfatal/x_test.go -- +-- go.mod -- +module benchfatal + +go 1.16 +-- x_test.go -- package benchfatal import "testing" diff --git a/src/cmd/go/testdata/script/test_benchmark_labels.txt b/src/cmd/go/testdata/script/test_benchmark_labels.txt index affab6b806..6b424c1bd8 100644 --- a/src/cmd/go/testdata/script/test_benchmark_labels.txt +++ b/src/cmd/go/testdata/script/test_benchmark_labels.txt @@ -10,7 +10,11 @@ stdout '^pkg: bench' ! stdout 'pkg:.*pkg: ' ! stderr 'pkg:.*pkg:' --- bench/x_test.go -- +-- go.mod -- +module bench + +go 1.16 +-- x_test.go -- package bench import "testing" diff --git a/src/cmd/go/testdata/script/test_build_failure.txt b/src/cmd/go/testdata/script/test_build_failure.txt index 2ae448a566..8d13634c8c 100644 --- a/src/cmd/go/testdata/script/test_build_failure.txt +++ b/src/cmd/go/testdata/script/test_build_failure.txt @@ -5,13 +5,17 @@ stderr 'undefined: g' stderr 'undefined: j' --- coverbad/p.go -- +-- go.mod -- +module coverbad + +go 1.16 +-- p.go -- package p func f() { g() } --- coverbad/p1.go -- +-- p1.go -- package p import "C" @@ -19,7 +23,7 @@ import "C" func h() { j() } --- coverbad/p_test.go -- +-- p_test.go -- package p import "testing" diff --git a/src/cmd/go/testdata/script/test_deadline.txt b/src/cmd/go/testdata/script/test_deadline.txt index 5a19f6590f..06ae16ffd2 100644 --- a/src/cmd/go/testdata/script/test_deadline.txt +++ b/src/cmd/go/testdata/script/test_deadline.txt @@ -4,6 +4,10 @@ go test -timeout=0 -run=TestNoDeadline go test -timeout=1m -run=TestDeadlineWithinMinute go test -timeout=1m -run=TestSubtestDeadlineWithinMinute +-- go.mod -- +module m + +go 1.16 -- deadline_test.go -- package testing_test diff --git a/src/cmd/go/testdata/script/test_empty.txt b/src/cmd/go/testdata/script/test_empty.txt index f2c512e791..5ebbecd53d 100644 --- a/src/cmd/go/testdata/script/test_empty.txt +++ b/src/cmd/go/testdata/script/test_empty.txt @@ -23,6 +23,10 @@ go test -cover -coverpkg=. -race cd $GOPATH/src/empty/testxtest go test -cover -coverpkg=. -race +-- empty/go.mod -- +module empty + +go 1.16 -- empty/pkg/pkg.go -- package p -- empty/pkgtest/pkg.go -- diff --git a/src/cmd/go/testdata/script/test_example_goexit.txt b/src/cmd/go/testdata/script/test_example_goexit.txt index 59219e3366..984f4349f5 100644 --- a/src/cmd/go/testdata/script/test_example_goexit.txt +++ b/src/cmd/go/testdata/script/test_example_goexit.txt @@ -5,7 +5,11 @@ stdout '(?s)--- PASS.*--- FAIL.*' stdout 'panic: test executed panic\(nil\) or runtime\.Goexit' --- examplegoexit/example_test.go -- +-- go.mod -- +module examplegoexit + +go 1.16 +-- example_test.go -- package main import ( diff --git a/src/cmd/go/testdata/script/test_import_error_stack.txt b/src/cmd/go/testdata/script/test_import_error_stack.txt index c66c1213a4..6c60f3d2ab 100644 --- a/src/cmd/go/testdata/script/test_import_error_stack.txt +++ b/src/cmd/go/testdata/script/test_import_error_stack.txt @@ -1,9 +1,20 @@ +env GO111MODULE=off ! go test testdep/p1 stderr 'package testdep/p1 \(test\)\n\timports testdep/p2\n\timports testdep/p3: build constraints exclude all Go files ' # check for full import stack +! go vet testdep/p1 +stderr 'package testdep/p1 \(test\)\n\timports testdep/p2\n\timports testdep/p3: build constraints exclude all Go files ' # check for full import stack +env GO111MODULE=on +cd testdep +! go test testdep/p1 +stderr 'package testdep/p1 \(test\)\n\timports testdep/p2\n\timports testdep/p3: build constraints exclude all Go files ' # check for full import stack ! go vet testdep/p1 stderr 'package testdep/p1 \(test\)\n\timports testdep/p2\n\timports testdep/p3: build constraints exclude all Go files ' # check for full import stack +-- testdep/go.mod -- +module testdep + +go 1.16 -- testdep/p1/p1.go -- package p1 -- testdep/p1/p1_test.go -- diff --git a/src/cmd/go/testdata/script/test_json.txt b/src/cmd/go/testdata/script/test_json.txt index 1bd530514c..cd5b0b9d7a 100644 --- a/src/cmd/go/testdata/script/test_json.txt +++ b/src/cmd/go/testdata/script/test_json.txt @@ -3,23 +3,23 @@ env GOCACHE=$WORK/tmp -# Run go test -json on errors empty/pkg and skipper +# Run go test -json on errors m/empty/pkg and m/skipper # It would be nice to test that the output is interlaced # but it seems to be impossible to do that in a short test # that isn't also flaky. Just check that we get JSON output. -go test -json -short -v errors empty/pkg skipper +go test -json -short -v errors m/empty/pkg m/skipper # Check errors for run action stdout '"Package":"errors"' stdout '"Action":"run","Package":"errors"' -# Check empty/pkg for output and skip actions -stdout '"Action":"output","Package":"empty/pkg","Output":".*no test files' -stdout '"Action":"skip","Package":"empty/pkg"' +# Check m/empty/pkg for output and skip actions +stdout '"Action":"output","Package":"m/empty/pkg","Output":".*no test files' +stdout '"Action":"skip","Package":"m/empty/pkg"' # Check skipper for output and skip actions -stdout '"Action":"output","Package":"skipper","Test":"Test","Output":"--- SKIP:' -stdout '"Action":"skip","Package":"skipper","Test":"Test"' +stdout '"Action":"output","Package":"m/skipper","Test":"Test","Output":"--- SKIP:' +stdout '"Action":"skip","Package":"m/skipper","Test":"Test"' # Run go test -json on errors and check it's cached go test -json -short -v errors @@ -36,6 +36,10 @@ stdout '"Package":"errors"' stdout '"Action":"run"' stdout '\{"Action":"pass","Package":"errors"\}' +-- go.mod -- +module m + +go 1.16 -- skipper/skip_test.go -- package skipper diff --git a/src/cmd/go/testdata/script/test_main_twice.txt b/src/cmd/go/testdata/script/test_main_twice.txt index 1e68dabec0..f32d4fc3b5 100644 --- a/src/cmd/go/testdata/script/test_main_twice.txt +++ b/src/cmd/go/testdata/script/test_main_twice.txt @@ -4,7 +4,11 @@ env GOCACHE=$WORK/tmp go test -v multimain stdout -count=2 notwithstanding # check tests ran twice --- multimain/multimain_test.go -- +-- go.mod -- +module multimain + +go 1.16 +-- multimain_test.go -- package multimain_test import "testing" diff --git a/src/cmd/go/testdata/script/test_match_no_tests_build_failure.txt b/src/cmd/go/testdata/script/test_match_no_tests_build_failure.txt index 92cb690dcc..e1c96438c8 100644 --- a/src/cmd/go/testdata/script/test_match_no_tests_build_failure.txt +++ b/src/cmd/go/testdata/script/test_match_no_tests_build_failure.txt @@ -6,9 +6,13 @@ ! stderr '(?m)^ok.*\[no tests to run\]' stdout 'FAIL' --- syntaxerror/x.go -- +-- go.mod -- +module syntaxerror + +go 1.16 +-- x.go -- package p --- syntaxerror/x_test.go -- +-- x_test.go -- package p func f() (x.y, z int) { diff --git a/src/cmd/go/testdata/script/test_no_run_example.txt b/src/cmd/go/testdata/script/test_no_run_example.txt index 66daa310fa..53ac755902 100644 --- a/src/cmd/go/testdata/script/test_no_run_example.txt +++ b/src/cmd/go/testdata/script/test_no_run_example.txt @@ -1,7 +1,11 @@ go test -v norunexample stdout 'File with non-runnable example was built.' --- norunexample/example_test.go -- +-- go.mod -- +module norunexample + +go 1.16 +-- example_test.go -- package pkg_test import "os" @@ -13,7 +17,7 @@ func init() { func Example_test() { // This test will not be run, it has no "Output:" comment. } --- norunexample/test_test.go -- +-- test_test.go -- package pkg import ( diff --git a/src/cmd/go/testdata/script/test_no_tests.txt b/src/cmd/go/testdata/script/test_no_tests.txt index d75bcff934..2d624d1da6 100644 --- a/src/cmd/go/testdata/script/test_no_tests.txt +++ b/src/cmd/go/testdata/script/test_no_tests.txt @@ -3,7 +3,11 @@ go test testnorun stdout 'testnorun\t\[no test files\]' --- testnorun/p.go -- +-- go.mod -- +module testnorun + +go 1.16 +-- p.go -- package p func init() { diff --git a/src/cmd/go/testdata/script/test_race.txt b/src/cmd/go/testdata/script/test_race.txt index 5d15189e19..2ffea46092 100644 --- a/src/cmd/go/testdata/script/test_race.txt +++ b/src/cmd/go/testdata/script/test_race.txt @@ -13,7 +13,11 @@ stdout 'FAIL: BenchmarkRace' ! stdout 'PASS' ! stderr 'PASS' --- testrace/race_test.go -- +-- go.mod -- +module testrace + +go 1.16 +-- race_test.go -- package testrace import "testing" diff --git a/src/cmd/go/testdata/script/test_race_cover_mode_issue20435.txt b/src/cmd/go/testdata/script/test_race_cover_mode_issue20435.txt index bff9502ac7..eacc882091 100644 --- a/src/cmd/go/testdata/script/test_race_cover_mode_issue20435.txt +++ b/src/cmd/go/testdata/script/test_race_cover_mode_issue20435.txt @@ -10,7 +10,11 @@ stderr '-covermode must be "atomic", not "set", when -race is enabled' ! stdout PASS ! stderr PASS --- testrace/race_test.go -- +-- go.mod -- +module testrace + +go 1.16 +-- race_test.go -- package testrace import "testing" diff --git a/src/cmd/go/testdata/script/test_race_install.txt b/src/cmd/go/testdata/script/test_race_install.txt index 66dc19ebb6..d28809bfdc 100644 --- a/src/cmd/go/testdata/script/test_race_install.txt +++ b/src/cmd/go/testdata/script/test_race_install.txt @@ -8,6 +8,10 @@ go install -race -pkgdir=$WORKDIR/tmp/pkg std go test -race -pkgdir=$WORKDIR/tmp/pkg -i -v empty/pkg ! stderr . --- empty/pkg/pkg.go -- +-- go.mod -- +module empty + +go 1.16 +-- pkg/pkg.go -- package p diff --git a/src/cmd/go/testdata/script/test_race_install_cgo.txt b/src/cmd/go/testdata/script/test_race_install_cgo.txt index feddc8f922..82f00f2086 100644 --- a/src/cmd/go/testdata/script/test_race_install_cgo.txt +++ b/src/cmd/go/testdata/script/test_race_install_cgo.txt @@ -5,7 +5,7 @@ [!darwin] ! stale cmd/cgo # The darwin builders are spuriously stale; see #33598. env GOBIN=$WORK/bin -go install mtime sametime +go install m/mtime m/sametime go tool -n cgo cp stdout cgopath.txt @@ -21,6 +21,10 @@ exec $GOBIN/mtime cgopath.txt # get the mtime of the file whose name is in cgopa cp stdout cgotime_after.txt exec $GOBIN/sametime cgotime_before.txt cgotime_after.txt +-- go.mod -- +module m + +go 1.16 -- mtime/mtime.go -- package main diff --git a/src/cmd/go/testdata/script/test_regexps.txt b/src/cmd/go/testdata/script/test_regexps.txt index a616195cab..2f33080a00 100644 --- a/src/cmd/go/testdata/script/test_regexps.txt +++ b/src/cmd/go/testdata/script/test_regexps.txt @@ -35,7 +35,11 @@ stdout -count=1 '^ z_test.go:18: LOG: XX running N=1$' # a large number, and the last iteration count prints right before the results. stdout -count=2 '^ x_test.go:15: LOG: Y running N=[1-9]\d{4,}\nBenchmarkX/Y\s+\d+' --- testregexp/x_test.go -- +-- go.mod -- +module testregexp + +go 1.16 +-- x_test.go -- package x import "testing" @@ -53,7 +57,7 @@ func BenchmarkX(b *testing.B) { b.Logf("LOG: Y running N=%d", b.N) }) } --- testregexp/z_test.go -- +-- z_test.go -- package x import "testing" diff --git a/src/cmd/go/testdata/script/test_relative_import.txt b/src/cmd/go/testdata/script/test_relative_import.txt index 0d212b4924..938a875b55 100644 --- a/src/cmd/go/testdata/script/test_relative_import.txt +++ b/src/cmd/go/testdata/script/test_relative_import.txt @@ -1,4 +1,5 @@ # Relative imports in go test +env GO111MODULE=off # relative import not supported in module mode # Run tests outside GOPATH. env GOPATH=$WORK/tmp @@ -27,4 +28,4 @@ func TestF(t *testing.T) { if F() != p1.F() { t.Fatal(F()) } -} \ No newline at end of file +} diff --git a/src/cmd/go/testdata/script/test_relative_import_dash_i.txt b/src/cmd/go/testdata/script/test_relative_import_dash_i.txt index dafa04ef02..b2716d8403 100644 --- a/src/cmd/go/testdata/script/test_relative_import_dash_i.txt +++ b/src/cmd/go/testdata/script/test_relative_import_dash_i.txt @@ -1,4 +1,5 @@ # Relative imports in go test -i +env GO111MODULE=off # relative import not supported in module mode # Run tests outside GOPATH. env GOPATH=$WORK/tmp @@ -28,4 +29,4 @@ func TestF(t *testing.T) { if F() != p1.F() { t.Fatal(F()) } -} \ No newline at end of file +} diff --git a/src/cmd/go/testdata/script/test_syntax_error_says_fail.txt b/src/cmd/go/testdata/script/test_syntax_error_says_fail.txt index 29fa805b43..44ff6e2b39 100644 --- a/src/cmd/go/testdata/script/test_syntax_error_says_fail.txt +++ b/src/cmd/go/testdata/script/test_syntax_error_says_fail.txt @@ -1,10 +1,21 @@ # Test that the error message for a syntax error in a test go file # says FAIL. +env GO111MODULE=off ! go test syntaxerror stderr 'x_test.go:' # check that the error is diagnosed stdout 'FAIL' # check that go test says FAIL +env GO111MODULE=on +cd syntaxerror +! go test syntaxerror +stderr 'x_test.go:' # check that the error is diagnosed +stdout 'FAIL' # check that go test says FAIL + +-- syntaxerror/go.mod -- +module syntaxerror + +go 1.16 -- syntaxerror/x.go -- package p -- syntaxerror/x_test.go -- diff --git a/src/cmd/go/testdata/script/test_vendor.txt b/src/cmd/go/testdata/script/test_vendor.txt index d72d672827..c6a88b6fed 100644 --- a/src/cmd/go/testdata/script/test_vendor.txt +++ b/src/cmd/go/testdata/script/test_vendor.txt @@ -1,9 +1,19 @@ -# Test +# In GOPATH mode, vendored packages can replace std packages. +env GO111MODULE=off cd vend/hello go test -v stdout TestMsgInternal stdout TestMsgExternal +# In module mode, they cannot. +env GO111MODULE=on +! go test -mod=vendor +stderr 'undefined: strings.Msg' + +-- vend/hello/go.mod -- +module vend/hello + +go 1.16 -- vend/hello/hello.go -- package main diff --git a/src/cmd/go/testdata/script/test_vet.txt b/src/cmd/go/testdata/script/test_vet.txt index af26b4de79..5af26b54f9 100644 --- a/src/cmd/go/testdata/script/test_vet.txt +++ b/src/cmd/go/testdata/script/test_vet.txt @@ -17,20 +17,24 @@ go test -vet=off p1.go stdout '\[no test files\]' # Test issue #22890 -go test vetcycle -stdout 'vetcycle.*\[no test files\]' +go test m/vetcycle +stdout 'm/vetcycle.*\[no test files\]' # Test with ... -! go test vetfail/... +! go test ./vetfail/... stderr 'Printf format %d' -stdout 'ok\s+vetfail/p2' +stdout 'ok\s+m/vetfail/p2' # Check there's no diagnosis of a bad build constraint in vetxonly mode. # Use -a so that we need to recompute the vet-specific export data for # vetfail/p1. -go test -a vetfail/p2 +go test -a m/vetfail/p2 ! stderr 'invalid.*constraint' +-- go.mod -- +module m + +go 1.16 -- p1_test.go -- package p @@ -74,7 +78,7 @@ func F() { -- vetfail/p2/p2.go -- package p2 -import _ "vetfail/p1" +import _ "m/vetfail/p1" func F() { } diff --git a/src/cmd/go/testdata/script/test_write_profiles_on_timeout.txt b/src/cmd/go/testdata/script/test_write_profiles_on_timeout.txt index a6cb934709..08e67a429e 100644 --- a/src/cmd/go/testdata/script/test_write_profiles_on_timeout.txt +++ b/src/cmd/go/testdata/script/test_write_profiles_on_timeout.txt @@ -2,13 +2,16 @@ [short] skip -cd profiling ! go test -cpuprofile cpu.pprof -memprofile mem.pprof -timeout 1ms grep . cpu.pprof grep . mem.pprof --- profiling/timeout_test.go -- +-- go.mod -- +module profiling + +go 1.16 +-- timeout_test.go -- package timeouttest_test import "testing" import "time" -func TestSleep(t *testing.T) { time.Sleep(time.Second) } \ No newline at end of file +func TestSleep(t *testing.T) { time.Sleep(time.Second) } diff --git a/src/cmd/go/testdata/script/test_xtestonly_works.txt b/src/cmd/go/testdata/script/test_xtestonly_works.txt index 01bafb733b..8e150dbfc2 100644 --- a/src/cmd/go/testdata/script/test_xtestonly_works.txt +++ b/src/cmd/go/testdata/script/test_xtestonly_works.txt @@ -4,11 +4,15 @@ go test xtestonly ! stdout '^ok.*\[no tests to run\]' stdout '^ok' --- xtestonly/f.go -- +-- go.mod -- +module xtestonly + +go 1.16 +-- f.go -- package xtestonly func F() int { return 42 } --- xtestonly/f_test.go -- +-- f_test.go -- package xtestonly_test import ( diff --git a/src/cmd/go/testdata/script/testing_issue40908.txt b/src/cmd/go/testdata/script/testing_issue40908.txt index 4939de080c..839320e4e8 100644 --- a/src/cmd/go/testdata/script/testing_issue40908.txt +++ b/src/cmd/go/testdata/script/testing_issue40908.txt @@ -3,7 +3,11 @@ go test -race testrace --- testrace/race_test.go -- +-- go.mod -- +module testrace + +go 1.16 +-- race_test.go -- package testrace import "testing" diff --git a/src/cmd/go/testdata/script/vendor_gopath_issue11409.txt b/src/cmd/go/testdata/script/vendor_gopath_issue11409.txt index 746a34a744..da52f9acf2 100644 --- a/src/cmd/go/testdata/script/vendor_gopath_issue11409.txt +++ b/src/cmd/go/testdata/script/vendor_gopath_issue11409.txt @@ -1,4 +1,5 @@ [!windows] [short] stop 'this test only applies to Windows' +env GO111MODULE=off go build run_go.go exec ./run_go$GOEXE $GOPATH $GOPATH/src/vend/hello diff --git a/src/cmd/go/testdata/script/vendor_import.txt b/src/cmd/go/testdata/script/vendor_import.txt index 35419f36f1..df4c27df81 100644 --- a/src/cmd/go/testdata/script/vendor_import.txt +++ b/src/cmd/go/testdata/script/vendor_import.txt @@ -1,4 +1,5 @@ # Imports +env GO111MODULE=off go list -f '{{.ImportPath}} {{.Imports}}' 'vend/...' 'vend/vendor/...' 'vend/x/vendor/...' cmp stdout want_vendor_imports.txt diff --git a/src/cmd/go/testdata/script/vendor_import_wrong.txt b/src/cmd/go/testdata/script/vendor_import_wrong.txt index aba6269784..73bf595640 100644 --- a/src/cmd/go/testdata/script/vendor_import_wrong.txt +++ b/src/cmd/go/testdata/script/vendor_import_wrong.txt @@ -1,7 +1,18 @@ # Wrong import path +env GO111MODULE=off ! go build vend/x/invalid stderr 'must be imported as foo' +env GO111MODULE= +cd vend/x/invalid +! go build vend/x/invalid +stderr 'must be imported as foo' + +-- vend/x/invalid/go.mod -- +module vend/x/invalid + +go 1.16 + -- vend/x/invalid/invalid.go -- package invalid diff --git a/src/cmd/go/testdata/script/vendor_issue12156.txt b/src/cmd/go/testdata/script/vendor_issue12156.txt index 49eb235ba5..ac95c6d3da 100644 --- a/src/cmd/go/testdata/script/vendor_issue12156.txt +++ b/src/cmd/go/testdata/script/vendor_issue12156.txt @@ -1,5 +1,6 @@ # Tests issue #12156, a former index out of range panic. +env GO111MODULE=off env GOPATH=$WORK/gopath/src/testvendor2 # vendor/x is directly in $GOPATH, not in $GOPATH/src cd $WORK/gopath/src/testvendor2/src/p diff --git a/src/cmd/go/testdata/script/vendor_list_issue11977.txt b/src/cmd/go/testdata/script/vendor_list_issue11977.txt index d97c6518b4..cdab33c089 100644 --- a/src/cmd/go/testdata/script/vendor_list_issue11977.txt +++ b/src/cmd/go/testdata/script/vendor_list_issue11977.txt @@ -1,5 +1,6 @@ [!net] skip [!exec:git] skip +env GO111MODULE=off go get github.com/rsc/go-get-issue-11864 @@ -13,4 +14,4 @@ go list -f '{{join .XTestImports "\n"}}' github.com/rsc/go-get-issue-11864/vendo stdout 'go-get-issue-11864/vendor/vendor.org/tx2' go list -f '{{join .XTestImports "\n"}}' github.com/rsc/go-get-issue-11864/vendor/vendor.org/tx3 -stdout 'go-get-issue-11864/vendor/vendor.org/tx3' \ No newline at end of file +stdout 'go-get-issue-11864/vendor/vendor.org/tx3' diff --git a/src/cmd/go/testdata/script/vendor_resolve.txt b/src/cmd/go/testdata/script/vendor_resolve.txt index 220b92f80b..bc8cf0ab38 100644 --- a/src/cmd/go/testdata/script/vendor_resolve.txt +++ b/src/cmd/go/testdata/script/vendor_resolve.txt @@ -1,3 +1,4 @@ +env GO111MODULE=off ! go build p stderr 'must be imported as x' diff --git a/src/cmd/go/testdata/script/vendor_test_issue11864.txt b/src/cmd/go/testdata/script/vendor_test_issue11864.txt index f11d790e6f..cfb43bf343 100644 --- a/src/cmd/go/testdata/script/vendor_test_issue11864.txt +++ b/src/cmd/go/testdata/script/vendor_test_issue11864.txt @@ -1,5 +1,6 @@ [!net] skip [!exec:git] skip +env GO111MODULE=off go get github.com/rsc/go-get-issue-11864 @@ -16,4 +17,4 @@ go test github.com/rsc/go-get-issue-11864 go test github.com/rsc/go-get-issue-11864/t # external tests should observe internal test exports (golang.org/issue/11977) -go test github.com/rsc/go-get-issue-11864/vendor/vendor.org/tx2 \ No newline at end of file +go test github.com/rsc/go-get-issue-11864/vendor/vendor.org/tx2 diff --git a/src/cmd/go/testdata/script/vendor_test_issue14613.txt b/src/cmd/go/testdata/script/vendor_test_issue14613.txt index 4e5e066d6b..7801e6944d 100644 --- a/src/cmd/go/testdata/script/vendor_test_issue14613.txt +++ b/src/cmd/go/testdata/script/vendor_test_issue14613.txt @@ -1,5 +1,6 @@ [!net] skip [!exec:git] skip +env GO111MODULE=off cd $GOPATH diff --git a/src/cmd/go/testdata/script/vet.txt b/src/cmd/go/testdata/script/vet.txt index 73fe2958fc..6573ae3ebd 100644 --- a/src/cmd/go/testdata/script/vet.txt +++ b/src/cmd/go/testdata/script/vet.txt @@ -1,29 +1,33 @@ # Package with external tests -! go vet vetpkg +! go vet m/vetpkg stderr 'Printf' # With tags -! go vet -tags tagtest vetpkg +! go vet -tags tagtest m/vetpkg stderr 'c\.go.*Printf' # With flags on -! go vet -printf vetpkg +! go vet -printf m/vetpkg stderr 'Printf' # With flags off -go vet -printf=false vetpkg +go vet -printf=false m/vetpkg ! stderr . # With only test files (tests issue #23395) -go vet onlytest +go vet m/onlytest ! stderr . # With only cgo files (tests issue #24193) [!cgo] skip [short] skip -go vet onlycgo +go vet m/onlycgo ! stderr . +-- go.mod -- +module m + +go 1.16 -- vetpkg/a_test.go -- package p_test -- vetpkg/b.go -- @@ -55,4 +59,4 @@ package p import "C" -func F() {} \ No newline at end of file +func F() {} -- cgit v1.2.3-54-g00ecf From 0f7ac9b4f5f6bc20344feb8a2c32b8126df80baa Mon Sep 17 00:00:00 2001 From: Alex Opie Date: Thu, 17 Sep 2020 04:31:50 +0000 Subject: cmd/go: use the correct linker config in the buildID hash The linker config is hashed into the buildID; however, the GOROOT_FINAL environment variable that is actually used when -trimpath is specified was not reflected in that hash. This change fixes that. Fixes #38989 Change-Id: I418a21a9f6293ca63c101d22b501dfdba8e91ac6 GitHub-Last-Rev: 4cf82920e4a76173c5cb5359b059e87ee7fc7f51 GitHub-Pull-Request: golang/go#40296 Reviewed-on: https://go-review.googlesource.com/c/go/+/243557 Run-TryBot: Jay Conrod Reviewed-by: Jay Conrod Trust: Jay Conrod Trust: Bryan C. Mills --- src/cmd/go/internal/work/exec.go | 9 +++-- src/cmd/go/internal/work/gc.go | 5 ++- .../go/testdata/script/link_matching_actionid.txt | 38 ++++++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 src/cmd/go/testdata/script/link_matching_actionid.txt diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index d975c36306..9da5a44e17 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -1178,8 +1178,13 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) { key, val := cfg.GetArchEnv() fmt.Fprintf(h, "%s=%s\n", key, val) - // The linker writes source file paths that say GOROOT_FINAL. - fmt.Fprintf(h, "GOROOT=%s\n", cfg.GOROOT_FINAL) + // The linker writes source file paths that say GOROOT_FINAL, but + // only if -trimpath is not specified (see ld() in gc.go). + gorootFinal := cfg.GOROOT_FINAL + if cfg.BuildTrimpath { + gorootFinal = trimPathGoRootFinal + } + fmt.Fprintf(h, "GOROOT=%s\n", gorootFinal) // GO_EXTLINK_ENABLED controls whether the external linker is used. fmt.Fprintf(h, "GO_EXTLINK_ENABLED=%s\n", cfg.Getenv("GO_EXTLINK_ENABLED")) diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go index 6031897f88..d76574932e 100644 --- a/src/cmd/go/internal/work/gc.go +++ b/src/cmd/go/internal/work/gc.go @@ -25,6 +25,9 @@ import ( "crypto/sha1" ) +// The 'path' used for GOROOT_FINAL when -trimpath is specified +const trimPathGoRootFinal = "go" + // The Go toolchain. type gcToolchain struct{} @@ -569,7 +572,7 @@ func (gcToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) env := []string{} if cfg.BuildTrimpath { - env = append(env, "GOROOT_FINAL=go") + env = append(env, "GOROOT_FINAL="+trimPathGoRootFinal) } return b.run(root, dir, root.Package.ImportPath, env, cfg.BuildToolexec, base.Tool("link"), "-o", out, "-importcfg", importcfg, ldflags, mainpkg) } diff --git a/src/cmd/go/testdata/script/link_matching_actionid.txt b/src/cmd/go/testdata/script/link_matching_actionid.txt new file mode 100644 index 0000000000..b8d423d027 --- /dev/null +++ b/src/cmd/go/testdata/script/link_matching_actionid.txt @@ -0,0 +1,38 @@ +# Checks that an identical binary is built with -trimpath from the same +# source files, with GOROOT in two different locations. +# Verifies golang.org/issue/38989 + +[short] skip +[!symlink] skip + +# Symlink the compiler to a local path +env GOROOT=$WORK/goroot1 +symlink $GOROOT -> $TESTGO_GOROOT + +# Set up fresh GOCACHE +env GOCACHE=$WORK/gocache1 +mkdir $GOCACHE + +# Build a simple binary +go build -o binary1 -trimpath -x main.go + +# Now repeat the same process with the compiler at a different local path +env GOROOT=$WORK/goroot2 +symlink $GOROOT -> $TESTGO_GOROOT + +env GOCACHE=$WORK/gocache2 +mkdir $GOCACHE + +go build -o binary2 -trimpath -x main.go + +# Check that the binaries match exactly +go tool buildid binary1 +cp stdout buildid1 +go tool buildid binary2 +cp stdout buildid2 +cmp buildid1 buildid2 + + +-- main.go -- +package main +func main() {} -- cgit v1.2.3-54-g00ecf From 07d5eb075b6f270ae4443e9689821d2e403b72b5 Mon Sep 17 00:00:00 2001 From: Sam Xie Date: Thu, 17 Sep 2020 02:59:28 +0000 Subject: cmd/go: allow output in non-existent directory When 'go build' is given an output path with -o, if the output path ends with a path separator, always treat it as a directory. Fixes #41313 Change-Id: I9a9c25448abfcd6297ad973f5ed2025b2568a4a7 GitHub-Last-Rev: 20a19bd63a2779a2c94b0efdf86146ffd551293c GitHub-Pull-Request: golang/go#41314 Reviewed-on: https://go-review.googlesource.com/c/go/+/253821 Run-TryBot: Jay Conrod Run-TryBot: Bryan C. Mills TryBot-Result: Go Bot Reviewed-by: Jay Conrod Trust: Bryan C. Mills Trust: Jay Conrod --- src/cmd/go/alldocs.go | 5 +++-- src/cmd/go/internal/work/build.go | 12 ++++++++---- src/cmd/go/testdata/script/build_output.txt | 26 ++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index b7e5bbed2d..5f1c7aaecb 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -93,8 +93,9 @@ // // The -o flag forces build to write the resulting executable or object // to the named output file or directory, instead of the default behavior described -// in the last two paragraphs. If the named output is a directory that exists, -// then any resulting executables will be written to that directory. +// in the last two paragraphs. If the named output is an existing directory or +// ends with a slash or backslash, then any resulting executables +// will be written to that directory. // // The -i flag installs the packages that are dependencies of the target. // diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go index 990e5d9ecd..86423f118c 100644 --- a/src/cmd/go/internal/work/build.go +++ b/src/cmd/go/internal/work/build.go @@ -53,8 +53,9 @@ serving only as a check that the packages can be built. The -o flag forces build to write the resulting executable or object to the named output file or directory, instead of the default behavior described -in the last two paragraphs. If the named output is a directory that exists, -then any resulting executables will be written to that directory. +in the last two paragraphs. If the named output is an existing directory or +ends with a slash or backslash, then any resulting executables +will be written to that directory. The -i flag installs the packages that are dependencies of the target. @@ -387,10 +388,13 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) { } if cfg.BuildO != "" { - // If the -o name exists and is a directory, then + // If the -o name exists and is a directory or + // ends with a slash or backslash, then // write all main packages to that directory. // Otherwise require only a single package be built. - if fi, err := os.Stat(cfg.BuildO); err == nil && fi.IsDir() { + if fi, err := os.Stat(cfg.BuildO); (err == nil && fi.IsDir()) || + strings.HasSuffix(cfg.BuildO, "/") || + strings.HasSuffix(cfg.BuildO, string(os.PathSeparator)) { if !explicitO { base.Fatalf("go build: build output %q already exists and is a directory", cfg.BuildO) } diff --git a/src/cmd/go/testdata/script/build_output.txt b/src/cmd/go/testdata/script/build_output.txt index ced7cf82a6..1e82950dbc 100644 --- a/src/cmd/go/testdata/script/build_output.txt +++ b/src/cmd/go/testdata/script/build_output.txt @@ -18,6 +18,32 @@ go build -o myprog x.go exists -exec myprog ! exists myprogr.exe +! exists bin +go build -o bin/x x.go +exists -exec bin/x +rm bin + +! exists bin +go build -o bin/ x.go +exists -exec bin/x$GOEXE +rm bin + +[windows] ! exists bin +[windows] go build -o bin\x x.go +[windows] exists -exec bin\x +[windows] rm bin + +[windows] ! exists bin +[windows] go build -o bin\ x.go +[windows] exists -exec bin\x.exe +[windows] rm bin + +! exists bin +mkdir bin +go build -o bin x.go +exists -exec bin/x$GOEXE +rm bin + go build p.go ! exists p ! exists p.a -- cgit v1.2.3-54-g00ecf From f554eb7bc332cc265e6cc0490a7595b2f5873cba Mon Sep 17 00:00:00 2001 From: David Chase Date: Mon, 13 Jul 2020 14:44:14 -0400 Subject: cmd/compile: add variable length TRESULTS type for SSA use. This type is very much like TTUPLE, but not just for pairs. Used to describe results of a pre-expansion function call. (will later probably also be used to describe the incoming args). Change-Id: I811850cfcc2b3de85085eb4c2eca217c04c330b3 Reviewed-on: https://go-review.googlesource.com/c/go/+/242360 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Than McIntosh Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/fmt.go | 11 +++++ src/cmd/compile/internal/types/etype_string.go | 7 +-- src/cmd/compile/internal/types/type.go | 66 +++++++++++++++++++++++--- 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 43e501deaf..d4af451506 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -711,6 +711,17 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited return } + if t.Etype == types.TRESULTS { + tys := t.Extra.(*types.Results).Types + for i, et := range tys { + if i > 0 { + b.WriteByte(',') + } + b.WriteString(et.String()) + } + return + } + flag, mode = flag.update(mode) if mode == FTypeIdName { flag |= FmtUnsigned diff --git a/src/cmd/compile/internal/types/etype_string.go b/src/cmd/compile/internal/types/etype_string.go index 0ff05a8c2a..14fd5b71df 100644 --- a/src/cmd/compile/internal/types/etype_string.go +++ b/src/cmd/compile/internal/types/etype_string.go @@ -44,12 +44,13 @@ func _() { _ = x[TCHANARGS-33] _ = x[TSSA-34] _ = x[TTUPLE-35] - _ = x[NTYPE-36] + _ = x[TRESULTS-36] + _ = x[NTYPE-37] } -const _EType_name = "xxxINT8UINT8INT16UINT16INT32UINT32INT64UINT64INTUINTUINTPTRCOMPLEX64COMPLEX128FLOAT32FLOAT64BOOLPTRFUNCSLICEARRAYSTRUCTCHANMAPINTERFORWANYSTRINGUNSAFEPTRIDEALNILBLANKFUNCARGSCHANARGSSSATUPLENTYPE" +const _EType_name = "xxxINT8UINT8INT16UINT16INT32UINT32INT64UINT64INTUINTUINTPTRCOMPLEX64COMPLEX128FLOAT32FLOAT64BOOLPTRFUNCSLICEARRAYSTRUCTCHANMAPINTERFORWANYSTRINGUNSAFEPTRIDEALNILBLANKFUNCARGSCHANARGSSSATUPLERESULTSNTYPE" -var _EType_index = [...]uint8{0, 3, 7, 12, 17, 23, 28, 34, 39, 45, 48, 52, 59, 68, 78, 85, 92, 96, 99, 103, 108, 113, 119, 123, 126, 131, 135, 138, 144, 153, 158, 161, 166, 174, 182, 185, 190, 195} +var _EType_index = [...]uint8{0, 3, 7, 12, 17, 23, 28, 34, 39, 45, 48, 52, 59, 68, 78, 85, 92, 96, 99, 103, 108, 113, 119, 123, 126, 131, 135, 138, 144, 153, 158, 161, 166, 174, 182, 185, 190, 197, 202} func (i EType) String() string { if i >= EType(len(_EType_index)-1) { diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index a777a5fd90..9b05aef429 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -66,8 +66,9 @@ const ( TCHANARGS // SSA backend types - TSSA // internal types used by SSA backend (flags, memory, etc.) - TTUPLE // a pair of types, used by SSA backend + TSSA // internal types used by SSA backend (flags, memory, etc.) + TTUPLE // a pair of types, used by SSA backend + TRESULTS // multiuple types; the resulting of calling a function or method, plus a memory at the end. NTYPE ) @@ -330,6 +331,11 @@ type Tuple struct { // Any tuple with a memory type must put that memory type second. } +type Results struct { + Types []*Type + // Any Results with a memory type must put that memory type last. +} + // Array contains Type fields specific to array types. type Array struct { Elem *Type // element type @@ -466,6 +472,8 @@ func New(et EType) *Type { t.Extra = new(Chan) case TTUPLE: t.Extra = new(Tuple) + case TRESULTS: + t.Extra = new(Results) } return t } @@ -512,6 +520,12 @@ func NewTuple(t1, t2 *Type) *Type { return t } +func NewResults(types []*Type) *Type { + t := New(TRESULTS) + t.Extra.(*Results).Types = types + return t +} + func newSSA(name string) *Type { t := New(TSSA) t.Extra = name @@ -688,7 +702,7 @@ func (t *Type) copy() *Type { case TARRAY: x := *t.Extra.(*Array) nt.Extra = &x - case TTUPLE, TSSA: + case TTUPLE, TSSA, TRESULTS: Fatalf("ssa types cannot be copied") } // TODO(mdempsky): Find out why this is necessary and explain. @@ -1051,6 +1065,23 @@ func (t *Type) cmp(x *Type) Cmp { } return ttup.second.Compare(xtup.second) + case TRESULTS: + xResults := x.Extra.(*Results) + tResults := t.Extra.(*Results) + xl, tl := len(xResults.Types), len(tResults.Types) + if tl != xl { + if tl < xl { + return CMPlt + } + return CMPgt + } + for i := 0; i < tl; i++ { + if c := tResults.Types[i].Compare(xResults.Types[i]); c != CMPeq { + return c + } + } + return CMPeq + case TMAP: if c := t.Key().cmp(x.Key()); c != CMPeq { return c @@ -1305,6 +1336,9 @@ func (t *Type) FieldType(i int) *Type { panic("bad tuple index") } } + if t.Etype == TRESULTS { + return t.Extra.(*Results).Types[i] + } return t.Field(i).Type } func (t *Type) FieldOff(i int) int64 { @@ -1382,11 +1416,20 @@ func (t *Type) ChanDir() ChanDir { } func (t *Type) IsMemory() bool { - return t == TypeMem || t.Etype == TTUPLE && t.Extra.(*Tuple).second == TypeMem + if t == TypeMem || t.Etype == TTUPLE && t.Extra.(*Tuple).second == TypeMem { + return true + } + if t.Etype == TRESULTS { + if types := t.Extra.(*Results).Types; len(types) > 0 && types[len(types)-1] == TypeMem { + return true + } + } + return false } -func (t *Type) IsFlags() bool { return t == TypeFlags } -func (t *Type) IsVoid() bool { return t == TypeVoid } -func (t *Type) IsTuple() bool { return t.Etype == TTUPLE } +func (t *Type) IsFlags() bool { return t == TypeFlags } +func (t *Type) IsVoid() bool { return t == TypeVoid } +func (t *Type) IsTuple() bool { return t.Etype == TTUPLE } +func (t *Type) IsResults() bool { return t.Etype == TRESULTS } // IsUntyped reports whether t is an untyped type. func (t *Type) IsUntyped() bool { @@ -1431,6 +1474,15 @@ func (t *Type) HasPointers() bool { case TTUPLE: ttup := t.Extra.(*Tuple) return ttup.first.HasPointers() || ttup.second.HasPointers() + + case TRESULTS: + types := t.Extra.(*Results).Types + for _, et := range types { + if et.HasPointers() { + return true + } + } + return false } return true -- cgit v1.2.3-54-g00ecf From 35e413c5373538ca6e0516f454f78c74477eff2c Mon Sep 17 00:00:00 2001 From: David Chase Date: Mon, 15 Jun 2020 14:20:36 -0400 Subject: cmd/compile: add new ops for experiment with late call expansion Added Dereference, StaticLECall, SelectN, SelectNAddr Change-Id: I5426ae488e83956539aa07f7415b8acc26c933e6 Reviewed-on: https://go-review.googlesource.com/c/go/+/239082 Trust: David Chase Run-TryBot: David Chase Reviewed-by: Cherry Zhang TryBot-Result: Go Bot --- src/cmd/compile/internal/ssa/gen/genericOps.go | 15 +++++++++----- src/cmd/compile/internal/ssa/opGen.go | 28 ++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index 145ba2d50c..95edff4c8c 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -346,6 +346,7 @@ var genericOps = []opData{ // Memory operations {name: "Load", argLength: 2}, // Load from arg0. arg1=memory + {name: "Dereference", argLength: 2}, // Load from arg0. arg1=memory. Helper op for arg/result passing, result is an otherwise not-SSA-able "value". {name: "Store", argLength: 3, typ: "Mem", aux: "Typ"}, // Store arg1 to arg0. arg2=memory, aux=type. Returns memory. // The source and destination of Move may overlap in some cases. See e.g. // memmove inlining in generic.rules. When inlineablememmovesize (in ../rewrite.go) @@ -387,9 +388,11 @@ var genericOps = []opData{ // as a phantom first argument. // TODO(josharian): ClosureCall and InterCall should have Int32 aux // to match StaticCall's 32 bit arg size limit. - {name: "ClosureCall", argLength: 3, aux: "CallOff", call: true}, // arg0=code pointer, arg1=context ptr, arg2=memory. auxint=arg size. Returns memory. - {name: "StaticCall", argLength: 1, aux: "CallOff", call: true}, // call function aux.(*obj.LSym), arg0=memory. auxint=arg size. Returns memory. - {name: "InterCall", argLength: 2, aux: "CallOff", call: true}, // interface call. arg0=code pointer, arg1=memory, auxint=arg size. Returns memory. + // TODO(drchase,josharian): could the arg size limit be bundled into the rules for CallOff? + {name: "ClosureCall", argLength: 3, aux: "CallOff", call: true}, // arg0=code pointer, arg1=context ptr, arg2=memory. auxint=arg size. Returns memory. + {name: "StaticCall", argLength: 1, aux: "CallOff", call: true}, // call function aux.(*obj.LSym), arg0=memory. auxint=arg size. Returns memory. + {name: "InterCall", argLength: 2, aux: "CallOff", call: true}, // interface call. arg0=code pointer, arg1=memory, auxint=arg size. Returns memory. + {name: "StaticLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded static call function aux.(*ssa.AuxCall.Fn). arg0..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem. // Conversions: signed extensions, zero (unsigned) extensions, truncations {name: "SignExt8to16", argLength: 1, typ: "Int16"}, @@ -531,8 +534,10 @@ var genericOps = []opData{ {name: "Cvt64Fto64U", argLength: 1}, // float64 -> uint64, only used on archs that has the instruction // pseudo-ops for breaking Tuple - {name: "Select0", argLength: 1, zeroWidth: true}, // the first component of a tuple - {name: "Select1", argLength: 1, zeroWidth: true}, // the second component of a tuple + {name: "Select0", argLength: 1, zeroWidth: true}, // the first component of a tuple + {name: "Select1", argLength: 1, zeroWidth: true}, // the second component of a tuple + {name: "SelectN", argLength: 1, aux: "Int64"}, // arg0=tuple, auxint=field index. Returns the auxint'th member. + {name: "SelectNAddr", argLength: 1, aux: "Int64"}, // arg0=tuple, auxint=field index. Returns the address of auxint'th member. Used for un-SSA-able result types. // Atomic operations used for semantically inlining runtime/internal/atomic. // Atomic loads return a new memory so that the loads are properly ordered diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index f00dc3f7f5..1fc0f7ea79 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -2717,6 +2717,7 @@ const ( OpSP OpSB OpLoad + OpDereference OpStore OpMove OpZero @@ -2730,6 +2731,7 @@ const ( OpClosureCall OpStaticCall OpInterCall + OpStaticLECall OpSignExt8to16 OpSignExt8to32 OpSignExt8to64 @@ -2826,6 +2828,8 @@ const ( OpCvt64Fto64U OpSelect0 OpSelect1 + OpSelectN + OpSelectNAddr OpAtomicLoad8 OpAtomicLoad32 OpAtomicLoad64 @@ -34742,6 +34746,11 @@ var opcodeTable = [...]opInfo{ argLen: 2, generic: true, }, + { + name: "Dereference", + argLen: 2, + generic: true, + }, { name: "Store", auxType: auxTyp, @@ -34827,6 +34836,13 @@ var opcodeTable = [...]opInfo{ call: true, generic: true, }, + { + name: "StaticLECall", + auxType: auxCallOff, + argLen: -1, + call: true, + generic: true, + }, { name: "SignExt8to16", argLen: 1, @@ -35328,6 +35344,18 @@ var opcodeTable = [...]opInfo{ zeroWidth: true, generic: true, }, + { + name: "SelectN", + auxType: auxInt64, + argLen: 1, + generic: true, + }, + { + name: "SelectNAddr", + auxType: auxInt64, + argLen: 1, + generic: true, + }, { name: "AtomicLoad8", argLen: 2, -- cgit v1.2.3-54-g00ecf From 6796a7fb127676b61375339076ae1c982a721dde Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Thu, 17 Sep 2020 09:09:39 -0400 Subject: cmd/addr2line: don't assume that GOROOT_FINAL is clean Fixes #41447 Change-Id: I4460c1c7962d02c41622a5ea1a3c4bc3714a1873 Reviewed-on: https://go-review.googlesource.com/c/go/+/255477 Trust: Bryan C. Mills Run-TryBot: Bryan C. Mills TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor --- src/cmd/addr2line/addr2line_test.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/cmd/addr2line/addr2line_test.go b/src/cmd/addr2line/addr2line_test.go index 578d88e432..7973aa2fe1 100644 --- a/src/cmd/addr2line/addr2line_test.go +++ b/src/cmd/addr2line/addr2line_test.go @@ -73,29 +73,37 @@ func testAddr2Line(t *testing.T, exepath, addr string) { if err != nil { t.Fatalf("Stat failed: %v", err) } + // Debug paths are stored slash-separated, so convert to system-native. srcPath = filepath.FromSlash(srcPath) fi2, err := os.Stat(srcPath) - if gorootFinal := os.Getenv("GOROOT_FINAL"); gorootFinal != "" && strings.HasPrefix(srcPath, gorootFinal) { - if os.IsNotExist(err) || (err == nil && !os.SameFile(fi1, fi2)) { - // srcPath has had GOROOT_FINAL substituted for GOROOT, and it doesn't - // match the actual file. GOROOT probably hasn't been moved to its final - // location yet, so try the original location instead. + + // If GOROOT_FINAL is set and srcPath is not the file we expect, perhaps + // srcPath has had GOROOT_FINAL substituted for GOROOT and GOROOT hasn't been + // moved to its final location yet. If so, try the original location instead. + if gorootFinal := os.Getenv("GOROOT_FINAL"); gorootFinal != "" && + (os.IsNotExist(err) || (err == nil && !os.SameFile(fi1, fi2))) { + // srcPath is clean, but GOROOT_FINAL itself might not be. + // (See https://golang.org/issue/41447.) + gorootFinal = filepath.Clean(gorootFinal) + + if strings.HasPrefix(srcPath, gorootFinal) { fi2, err = os.Stat(runtime.GOROOT() + strings.TrimPrefix(srcPath, gorootFinal)) } } + if err != nil { t.Fatalf("Stat failed: %v", err) } if !os.SameFile(fi1, fi2) { t.Fatalf("addr2line_test.go and %s are not same file", srcPath) } - if srcLineNo != "99" { - t.Fatalf("line number = %v; want 99", srcLineNo) + if srcLineNo != "107" { + t.Fatalf("line number = %v; want 107", srcLineNo) } } -// This is line 98. The test depends on that. +// This is line 106. The test depends on that. func TestAddr2Line(t *testing.T) { testenv.MustHaveGoBuild(t) -- cgit v1.2.3-54-g00ecf From 22053790fa2c0944df53ea95df476ad2f855424f Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 17 Sep 2020 09:55:23 -0700 Subject: cmd/compile: propagate go:notinheap implicitly //go:notinheap type T int type U T We already correctly propagate the notinheap-ness of T to U. But we have an assertion in the typechecker that if there's no explicit //go:notinheap associated with U, then report an error. Get rid of that error so that implicit propagation is allowed. Adjust the tests so that we make sure that uses of types like U do correctly report an error when U is used in a context that might cause a Go heap allocation. Fixes #41451 Update #40954 Update #41432 Change-Id: I1692bc7cceff21ebb3f557f3748812a40887118d Reviewed-on: https://go-review.googlesource.com/c/go/+/255637 Run-TryBot: Keith Randall Reviewed-by: Cuong Manh Le Reviewed-by: Ian Lance Taylor Trust: Cuong Manh Le TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/typecheck.go | 6 ------ test/notinheap.go | 20 -------------------- test/notinheap2.go | 26 ++++++++++++++++++++++++++ 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 55773641ed..834c1a8ee6 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -2068,12 +2068,6 @@ func typecheck1(n *Node, top int) (res *Node) { ok |= ctxStmt n.Left = typecheck(n.Left, ctxType) checkwidth(n.Left.Type) - if n.Left.Type != nil && n.Left.Type.NotInHeap() && !n.Left.Name.Param.Alias && n.Left.Name.Param.Pragma&NotInHeap == 0 { - // The type contains go:notinheap types, so it - // must be marked as such (alternatively, we - // could silently propagate go:notinheap). - yyerror("type %v must be go:notinheap", n.Left.Type) - } } t := n.Type diff --git a/test/notinheap.go b/test/notinheap.go index 5dd4997a65..2188a38a14 100644 --- a/test/notinheap.go +++ b/test/notinheap.go @@ -11,18 +11,6 @@ package p //go:notinheap type nih struct{} -// Types embedding notinheap types must be notinheap. - -type embed1 struct { // ERROR "must be go:notinheap" - x nih -} - -type embed2 [1]nih // ERROR "must be go:notinheap" - -type embed3 struct { // ERROR "must be go:notinheap" - x [1]nih -} - type embed4 map[nih]int // ERROR "incomplete \(or unallocatable\) map key not allowed" type embed5 map[int]nih // ERROR "incomplete \(or unallocatable\) map value not allowed" @@ -52,14 +40,6 @@ type t3 byte //go:notinheap type t4 rune -// Type aliases inherit the go:notinheap-ness of the type they alias. -type nihAlias = nih - -type embedAlias1 struct { // ERROR "must be go:notinheap" - x nihAlias -} -type embedAlias2 [1]nihAlias // ERROR "must be go:notinheap" - var sink interface{} func i() { diff --git a/test/notinheap2.go b/test/notinheap2.go index 23d4b0ae77..100ed37b72 100644 --- a/test/notinheap2.go +++ b/test/notinheap2.go @@ -32,6 +32,25 @@ var y3 *[1]nih var z []nih var w []nih var n int +var sink interface{} + +type embed1 struct { // implicitly notinheap + x nih +} + +type embed2 [1]nih // implicitly notinheap + +type embed3 struct { // implicitly notinheap + x [1]nih +} + +// Type aliases inherit the go:notinheap-ness of the type they alias. +type nihAlias = nih + +type embedAlias1 struct { // implicitly notinheap + x nihAlias +} +type embedAlias2 [1]nihAlias // implicitly notinheap func g() { y = new(nih) // ERROR "can't be allocated in Go" @@ -39,6 +58,13 @@ func g() { y3 = new([1]nih) // ERROR "can't be allocated in Go" z = make([]nih, 1) // ERROR "can't be allocated in Go" z = append(z, x) // ERROR "can't be allocated in Go" + + sink = new(embed1) // ERROR "can't be allocated in Go" + sink = new(embed2) // ERROR "can't be allocated in Go" + sink = new(embed3) // ERROR "can't be allocated in Go" + sink = new(embedAlias1) // ERROR "can't be allocated in Go" + sink = new(embedAlias2) // ERROR "can't be allocated in Go" + // Test for special case of OMAKESLICECOPY x := make([]nih, n) // ERROR "can't be allocated in Go" copy(x, z) -- cgit v1.2.3-54-g00ecf From e6426dfd6dbc47ba23b8a91003b8f947c5afa692 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Thu, 17 Sep 2020 16:05:52 +0200 Subject: net: use IFF_* consts from package syscall on solaris All necessary IFF_* consts are available in the syscall package. Use them in linkFlags instead of duplicating them. Change-Id: Ibd2b0f6f39f98bfad2a0c8c55d1eb64167aeee03 Reviewed-on: https://go-review.googlesource.com/c/go/+/255497 Trust: Tobias Klauser Run-TryBot: Tobias Klauser Reviewed-by: Brad Fitzpatrick --- src/net/interface_solaris.go | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/src/net/interface_solaris.go b/src/net/interface_solaris.go index 5f9367f996..f8d1571b90 100644 --- a/src/net/interface_solaris.go +++ b/src/net/interface_solaris.go @@ -32,39 +32,21 @@ func interfaceTable(ifindex int) ([]Interface, error) { return ift, nil } -const ( - sysIFF_UP = 0x1 - sysIFF_BROADCAST = 0x2 - sysIFF_DEBUG = 0x4 - sysIFF_LOOPBACK = 0x8 - sysIFF_POINTOPOINT = 0x10 - sysIFF_NOTRAILERS = 0x20 - sysIFF_RUNNING = 0x40 - sysIFF_NOARP = 0x80 - sysIFF_PROMISC = 0x100 - sysIFF_ALLMULTI = 0x200 - sysIFF_INTELLIGENT = 0x400 - sysIFF_MULTICAST = 0x800 - sysIFF_MULTI_BCAST = 0x1000 - sysIFF_UNNUMBERED = 0x2000 - sysIFF_PRIVATE = 0x8000 -) - func linkFlags(rawFlags int) Flags { var f Flags - if rawFlags&sysIFF_UP != 0 { + if rawFlags&syscall.IFF_UP != 0 { f |= FlagUp } - if rawFlags&sysIFF_BROADCAST != 0 { + if rawFlags&syscall.IFF_BROADCAST != 0 { f |= FlagBroadcast } - if rawFlags&sysIFF_LOOPBACK != 0 { + if rawFlags&syscall.IFF_LOOPBACK != 0 { f |= FlagLoopback } - if rawFlags&sysIFF_POINTOPOINT != 0 { + if rawFlags&syscall.IFF_POINTOPOINT != 0 { f |= FlagPointToPoint } - if rawFlags&sysIFF_MULTICAST != 0 { + if rawFlags&syscall.IFF_MULTICAST != 0 { f |= FlagMulticast } return f -- cgit v1.2.3-54-g00ecf From 9a702fd427645e4bcd42a68f9676bc1ab2adb6e4 Mon Sep 17 00:00:00 2001 From: Jay Conrod Date: Thu, 17 Sep 2020 15:54:13 -0400 Subject: cmd/go: flip relationship between load and modload Previously, modload imported load, but it mainly just did so in order to install callbacks to the modload API. This was important during vgo development, but there's no longer a strong reason to do this. Nothing modload imports strongly depends on load, so there's little danger of a dependency cycle. This change deletes the callbacks in load and instead, makes load call exported functions in modload directly. In the future, these functions may have different signatures than their GOPATH counterparts. Change-Id: Ifde5c3ffebd190b5bd184924ec447d272b936f27 Reviewed-on: https://go-review.googlesource.com/c/go/+/255719 Trust: Jay Conrod Run-TryBot: Jay Conrod TryBot-Result: Go Bot Reviewed-by: Bryan C. Mills --- src/cmd/go/internal/load/pkg.go | 46 ++++++++++++----------------------- src/cmd/go/internal/modload/import.go | 7 ------ src/cmd/go/internal/modload/init.go | 14 +---------- src/cmd/go/internal/vcs/vcs.go | 36 ++++++++++++++++++++++++--- src/cmd/go/internal/work/exec.go | 3 ++- src/cmd/go/internal/work/init.go | 4 +-- 6 files changed, 53 insertions(+), 57 deletions(-) diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go index 71fd9b5538..d06e65737d 100644 --- a/src/cmd/go/internal/load/pkg.go +++ b/src/cmd/go/internal/load/pkg.go @@ -28,27 +28,13 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/modinfo" + "cmd/go/internal/modload" "cmd/go/internal/par" "cmd/go/internal/search" "cmd/go/internal/str" "cmd/go/internal/trace" ) -var ( - // module initialization hook; never nil, no-op if module use is disabled - ModInit func() - - // module hooks; nil if module use is disabled - ModBinDir func() string // return effective bin directory - ModLookup func(parentPath string, parentIsStd bool, path string) (dir, realPath string, err error) // lookup effective meaning of import - ModPackageModuleInfo func(path string) *modinfo.ModulePublic // return module info for Package struct - ModImportPaths func(ctx context.Context, args []string) []*search.Match // expand import paths - ModPackageBuildInfo func(main string, deps []string) string // return module info to embed in binary - ModInfoProg func(info string, isgccgo bool) []byte // wrap module info in .go code for binary - ModImportFromFiles func(context.Context, []string) // update go.mod to add modules for imports in these files - ModDirImportPath func(string) string // return effective import path for directory -) - var IgnoreImports bool // control whether we ignore imports in packages // A Package describes a single package found in a directory. @@ -770,7 +756,7 @@ func loadPackageData(path, parentPath, parentDir, parentRoot string, parentIsStd r.dir = filepath.Join(parentDir, path) r.path = dirToImportPath(r.dir) } else if cfg.ModulesEnabled { - r.dir, r.path, r.err = ModLookup(parentPath, parentIsStd, path) + r.dir, r.path, r.err = modload.Lookup(parentPath, parentIsStd, path) } else if mode&ResolveImport != 0 { // We do our own path resolution, because we want to // find out the key to use in packageCache without the @@ -801,7 +787,7 @@ func loadPackageData(path, parentPath, parentDir, parentRoot string, parentIsStd } data.p, data.err = cfg.BuildContext.ImportDir(r.dir, buildMode) if data.p.Root == "" && cfg.ModulesEnabled { - if info := ModPackageModuleInfo(path); info != nil { + if info := modload.PackageModuleInfo(path); info != nil { data.p.Root = info.Dir } } @@ -827,7 +813,7 @@ func loadPackageData(path, parentPath, parentDir, parentRoot string, parentIsStd if cfg.GOBIN != "" { data.p.BinDir = cfg.GOBIN } else if cfg.ModulesEnabled { - data.p.BinDir = ModBinDir() + data.p.BinDir = modload.BinDir() } } @@ -895,8 +881,8 @@ var preloadWorkerCount = runtime.GOMAXPROCS(0) // to ensure preload goroutines are no longer active. This is necessary // because of global mutable state that cannot safely be read and written // concurrently. In particular, packageDataCache may be cleared by "go get" -// in GOPATH mode, and modload.loaded (accessed via ModLookup) may be -// modified by modload.ImportPaths (ModImportPaths). +// in GOPATH mode, and modload.loaded (accessed via modload.Lookup) may be +// modified by modload.ImportPaths (modload.ImportPaths). type preload struct { cancel chan struct{} sema chan struct{} @@ -1006,7 +992,7 @@ func ResolveImportPath(parent *Package, path string) (found string) { func resolveImportPath(path, parentPath, parentDir, parentRoot string, parentIsStd bool) (found string) { if cfg.ModulesEnabled { - if _, p, e := ModLookup(parentPath, parentIsStd, path); e == nil { + if _, p, e := modload.Lookup(parentPath, parentIsStd, path); e == nil { return p } return path @@ -1369,7 +1355,7 @@ func disallowInternal(srcDir string, importer *Package, importerPath string, p * // directory containing them. // If the directory is outside the main module, this will resolve to ".", // which is not a prefix of any valid module. - importerPath = ModDirImportPath(importer.Dir) + importerPath = modload.DirImportPath(importer.Dir) } parentOfInternal := p.ImportPath[:i] if str.HasPathPrefix(importerPath, parentOfInternal) { @@ -1652,7 +1638,7 @@ func (p *Package) load(ctx context.Context, path string, stk *ImportStack, impor elem = full } if p.Internal.Build.BinDir == "" && cfg.ModulesEnabled { - p.Internal.Build.BinDir = ModBinDir() + p.Internal.Build.BinDir = modload.BinDir() } if p.Internal.Build.BinDir != "" { // Install to GOBIN or bin of GOPATH entry. @@ -1861,9 +1847,9 @@ func (p *Package) load(ctx context.Context, path string, stk *ImportStack, impor if p.Internal.CmdlineFiles { mainPath = "command-line-arguments" } - p.Module = ModPackageModuleInfo(mainPath) + p.Module = modload.PackageModuleInfo(mainPath) if p.Name == "main" && len(p.DepsErrors) == 0 { - p.Internal.BuildInfo = ModPackageBuildInfo(mainPath, p.Deps) + p.Internal.BuildInfo = modload.PackageBuildInfo(mainPath, p.Deps) } } } @@ -2229,8 +2215,8 @@ func setToolFlags(pkgs ...*Package) { } func ImportPaths(ctx context.Context, args []string) []*search.Match { - if ModInit(); cfg.ModulesEnabled { - return ModImportPaths(ctx, args) + if modload.Init(); cfg.ModulesEnabled { + return modload.ImportPaths(ctx, args) } return search.ImportPaths(args) } @@ -2282,7 +2268,7 @@ func PackagesForBuild(ctx context.Context, args []string) []*Package { // (typically named on the command line). The target is named p.a for // package p or named after the first Go file for package main. func GoFilesPackage(ctx context.Context, gofiles []string) *Package { - ModInit() + modload.Init() for _, f := range gofiles { if !strings.HasSuffix(f, ".go") { @@ -2329,7 +2315,7 @@ func GoFilesPackage(ctx context.Context, gofiles []string) *Package { ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dirent, nil } if cfg.ModulesEnabled { - ModImportFromFiles(ctx, gofiles) + modload.ImportFromFiles(ctx, gofiles) } var err error @@ -2357,7 +2343,7 @@ func GoFilesPackage(ctx context.Context, gofiles []string) *Package { if cfg.GOBIN != "" { pkg.Target = filepath.Join(cfg.GOBIN, exe) } else if cfg.ModulesEnabled { - pkg.Target = filepath.Join(ModBinDir(), exe) + pkg.Target = filepath.Join(modload.BinDir(), exe) } } diff --git a/src/cmd/go/internal/modload/import.go b/src/cmd/go/internal/modload/import.go index 10b1e7f4b8..e93eebcb81 100644 --- a/src/cmd/go/internal/modload/import.go +++ b/src/cmd/go/internal/modload/import.go @@ -17,7 +17,6 @@ import ( "time" "cmd/go/internal/cfg" - "cmd/go/internal/load" "cmd/go/internal/modfetch" "cmd/go/internal/par" "cmd/go/internal/search" @@ -38,8 +37,6 @@ type ImportMissingError struct { newMissingVersion string } -var _ load.ImportPathError = (*ImportMissingError)(nil) - func (e *ImportMissingError) Error() string { if e.Module.Path == "" { if search.IsStandardImportPath(e.Path) { @@ -105,8 +102,6 @@ func (e *AmbiguousImportError) Error() string { return buf.String() } -var _ load.ImportPathError = &AmbiguousImportError{} - type invalidImportError struct { importPath string err error @@ -124,8 +119,6 @@ func (e *invalidImportError) Unwrap() error { return e.err } -var _ load.ImportPathError = &invalidImportError{} - // importFromBuildList finds the module and directory in the build list // containing the package with the given import path. The answer must be unique: // importFromBuildList returns an error if multiple modules attempt to provide diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go index 60aadf23ea..2c95fa4263 100644 --- a/src/cmd/go/internal/modload/init.go +++ b/src/cmd/go/internal/modload/init.go @@ -22,7 +22,6 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" - "cmd/go/internal/load" "cmd/go/internal/lockedfile" "cmd/go/internal/modconv" "cmd/go/internal/modfetch" @@ -210,14 +209,7 @@ func Init() { } cfg.ModulesEnabled = true - load.ModBinDir = BinDir - load.ModLookup = Lookup - load.ModPackageModuleInfo = PackageModuleInfo - load.ModImportPaths = ImportPaths - load.ModPackageBuildInfo = PackageBuildInfo - load.ModInfoProg = ModInfoProg - load.ModImportFromFiles = ImportFromFiles - load.ModDirImportPath = DirImportPath + // load.ModDirImportPath = DirImportPath if modRoot == "" { // We're in module mode, but not inside a module. @@ -243,10 +235,6 @@ func Init() { } } -func init() { - load.ModInit = Init -} - // WillBeEnabled checks whether modules should be enabled but does not // initialize modules by installing hooks. If Init has already been called, // WillBeEnabled returns the same result as Enabled. diff --git a/src/cmd/go/internal/vcs/vcs.go b/src/cmd/go/internal/vcs/vcs.go index e535998d89..90bf10244d 100644 --- a/src/cmd/go/internal/vcs/vcs.go +++ b/src/cmd/go/internal/vcs/vcs.go @@ -21,7 +21,6 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" - "cmd/go/internal/load" "cmd/go/internal/web" ) @@ -664,7 +663,7 @@ func RepoRootForImportPath(importPath string, mod ModuleMode, security web.Secur if err == errUnknownSite { rr, err = repoRootForImportDynamic(importPath, mod, security) if err != nil { - err = load.ImportErrorf(importPath, "unrecognized import path %q: %v", importPath, err) + err = importErrorf(importPath, "unrecognized import path %q: %v", importPath, err) } } if err != nil { @@ -679,7 +678,7 @@ func RepoRootForImportPath(importPath string, mod ModuleMode, security web.Secur if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.Root, "...") { // Do not allow wildcards in the repo root. rr = nil - err = load.ImportErrorf(importPath, "cannot expand ... in %q", importPath) + err = importErrorf(importPath, "cannot expand ... in %q", importPath) } return rr, err } @@ -703,7 +702,7 @@ func repoRootFromVCSPaths(importPath string, security web.SecurityMode, vcsPaths m := srv.regexp.FindStringSubmatch(importPath) if m == nil { if srv.prefix != "" { - return nil, load.ImportErrorf(importPath, "invalid %s import path %q", srv.prefix, importPath) + return nil, importErrorf(importPath, "invalid %s import path %q", srv.prefix, importPath) } continue } @@ -1185,3 +1184,32 @@ func launchpadVCS(match map[string]string) error { } return nil } + +// importError is a copy of load.importError, made to avoid a dependency cycle +// on cmd/go/internal/load. It just needs to satisfy load.ImportPathError. +type importError struct { + importPath string + err error +} + +func importErrorf(path, format string, args ...interface{}) error { + err := &importError{importPath: path, err: fmt.Errorf(format, args...)} + if errStr := err.Error(); !strings.Contains(errStr, path) { + panic(fmt.Sprintf("path %q not in error %q", path, errStr)) + } + return err +} + +func (e *importError) Error() string { + return e.err.Error() +} + +func (e *importError) Unwrap() error { + // Don't return e.err directly, since we're only wrapping an error if %w + // was passed to ImportErrorf. + return errors.Unwrap(e.err) +} + +func (e *importError) ImportPath() string { + return e.importPath +} diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index 9da5a44e17..afd6fd6d3f 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -31,6 +31,7 @@ import ( "cmd/go/internal/cache" "cmd/go/internal/cfg" "cmd/go/internal/load" + "cmd/go/internal/modload" "cmd/go/internal/str" "cmd/go/internal/trace" ) @@ -692,7 +693,7 @@ func (b *Builder) build(ctx context.Context, a *Action) (err error) { } if p.Internal.BuildInfo != "" && cfg.ModulesEnabled { - if err := b.writeFile(objdir+"_gomod_.go", load.ModInfoProg(p.Internal.BuildInfo, cfg.BuildToolchainName == "gccgo")); err != nil { + if err := b.writeFile(objdir+"_gomod_.go", modload.ModInfoProg(p.Internal.BuildInfo, cfg.BuildToolchainName == "gccgo")); err != nil { return err } gofiles = append(gofiles, objdir+"_gomod_.go") diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go index d71387d323..42692acd3b 100644 --- a/src/cmd/go/internal/work/init.go +++ b/src/cmd/go/internal/work/init.go @@ -9,7 +9,7 @@ package work import ( "cmd/go/internal/base" "cmd/go/internal/cfg" - "cmd/go/internal/load" + "cmd/go/internal/modload" "cmd/internal/objabi" "cmd/internal/sys" "flag" @@ -21,7 +21,7 @@ import ( ) func BuildInit() { - load.ModInit() + modload.Init() instrumentInit() buildModeInit() -- cgit v1.2.3-54-g00ecf From 7a095c32366593f637a95bc927c63454125e3015 Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Tue, 15 Sep 2020 15:14:55 -0400 Subject: cmd/go/internal/modload: avoid a network fetch when querying a valid semantic version Test this behavior incidentally in a test for ambiguous import errors. (I rediscovered the error when writing the new test.) For #32567 Updates #28806 Change-Id: I323f05145734e5cf99818b9f04d65075f7c0f787 Reviewed-on: https://go-review.googlesource.com/c/go/+/255046 Trust: Bryan C. Mills Run-TryBot: Bryan C. Mills TryBot-Result: Go Bot Reviewed-by: Michael Matloob Reviewed-by: Jay Conrod --- src/cmd/go/internal/modload/query.go | 21 ++++++-- .../testdata/script/mod_get_ambiguous_import.txt | 60 ++++++++++++++++++++++ 2 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 src/cmd/go/testdata/script/mod_get_ambiguous_import.txt diff --git a/src/cmd/go/internal/modload/query.go b/src/cmd/go/internal/modload/query.go index f67a738677..5ddb4e6565 100644 --- a/src/cmd/go/internal/modload/query.go +++ b/src/cmd/go/internal/modload/query.go @@ -212,7 +212,20 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed default: // Direct lookup of semantic version or commit identifier. - // + + // If the query is a valid semantic version and that version is replaced, + // use the replacement module without searching the proxy. + canonicalQuery := module.CanonicalVersion(query) + if canonicalQuery != "" { + m := module.Version{Path: path, Version: query} + if r := Replacement(m); r.Path != "" { + if err := allowed(ctx, m); errors.Is(err, ErrDisallowed) { + return nil, err + } + return &modfetch.RevInfo{Version: query}, nil + } + } + // If the identifier is not a canonical semver tag — including if it's a // semver tag with a +metadata suffix — then modfetch.Stat will populate // info.Version with a suitable pseudo-version. @@ -222,9 +235,9 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed // The full query doesn't correspond to a tag. If it is a semantic version // with a +metadata suffix, see if there is a tag without that suffix: // semantic versioning defines them to be equivalent. - if vers := module.CanonicalVersion(query); vers != "" && vers != query { - info, err = modfetch.Stat(proxy, path, vers) - if !errors.Is(err, os.ErrNotExist) { + if canonicalQuery != "" && query != canonicalQuery { + info, err = modfetch.Stat(proxy, path, canonicalQuery) + if err != nil && !errors.Is(err, os.ErrNotExist) { return info, err } } diff --git a/src/cmd/go/testdata/script/mod_get_ambiguous_import.txt b/src/cmd/go/testdata/script/mod_get_ambiguous_import.txt new file mode 100644 index 0000000000..8f5bf20636 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_get_ambiguous_import.txt @@ -0,0 +1,60 @@ +go list -m all +stdout '^example.net/m v0.1.0 ' +! stdout '^example.net/m/p ' +cp go.mod go.mod.orig + +# Upgrading example.net/m/p without also upgrading example.net/m +# causes the import of package example.net/m/p to be ambiguous. +# +# TODO(#27899): Should we automatically upgrade example.net/m to v0.2.0 +# to resolve the conflict? +! go get -d example.net/m/p@v1.0.0 +stderr '^go get example.net/m/p@v1.0.0: ambiguous import: found package example.net/m/p in multiple modules:\n\texample.net/m v0.1.0 \(.*[/\\]m1[/\\]p\)\n\texample.net/m/p v1.0.0 \(.*[/\\]p0\)\n\z' +cmp go.mod go.mod.orig + +# Upgrading both modules simultaneously resolves the ambiguous upgrade. +# Note that this command line mixes a module path (example.net/m) +# and a package path (example.net/m/p) in the same command. +go get -d example.net/m@v0.2.0 example.net/m/p@v1.0.0 + +go list -m all +stdout '^example.net/m v0.2.0 ' +stdout '^example.net/m/p v1.0.0 ' + +-- go.mod -- +module example.net/importer + +go 1.16 + +require ( + example.net/m v0.1.0 +) + +replace ( + example.net/m v0.1.0 => ./m1 + example.net/m v0.2.0 => ./m2 + example.net/m/p v1.0.0 => ./p0 +) +-- importer.go -- +package importer +import _ "example.net/m/p" +-- m1/go.mod -- +module example.net/m + +go 1.16 +-- m1/p/p.go -- +package p +-- m2/go.mod -- +module example.net/m + +go 1.16 +-- m2/README.txt -- +Package p has been moved to module …/m/p. +Module …/m/p does not require any version of module …/m. + +-- p0/go.mod -- +module example.net/m/p + +go 1.16 +-- p0/p.go -- +package p -- cgit v1.2.3-54-g00ecf From 75fab04b83a832eb84bec9e1f23d395a342c865c Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Thu, 17 Sep 2020 15:31:07 -0400 Subject: cmd/asm: make asm -S flag consistent with compile -S flag Change things so that the -S command line option for the assembler works the same as -S in the compiler, e.g. you can use -S=2 to get additional detail. Change-Id: I7bdfba39a98e67c7ae4b93019e171b188bb99a2d Reviewed-on: https://go-review.googlesource.com/c/go/+/255717 Trust: Than McIntosh Run-TryBot: Than McIntosh Reviewed-by: Cherry Zhang Reviewed-by: David Chase TryBot-Result: Go Bot --- src/cmd/asm/internal/flags/flags.go | 7 ++++--- src/cmd/asm/main.go | 4 +--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/cmd/asm/internal/flags/flags.go b/src/cmd/asm/internal/flags/flags.go index 1df9df9563..64024cc97d 100644 --- a/src/cmd/asm/internal/flags/flags.go +++ b/src/cmd/asm/internal/flags/flags.go @@ -17,7 +17,6 @@ import ( var ( Debug = flag.Bool("debug", false, "dump instructions as they are parsed") OutputFile = flag.String("o", "", "output file; default foo.o for /a/b/c/foo.s as first argument") - PrintOut = flag.Bool("S", false, "print assembly and machine code") TrimPath = flag.String("trimpath", "", "remove prefix from recorded source file paths") Shared = flag.Bool("shared", false, "generate code that can be linked into a shared library") Dynlink = flag.Bool("dynlink", false, "support references to Go symbols defined in other shared libraries") @@ -28,14 +27,16 @@ var ( ) var ( - D MultiFlag - I MultiFlag + D MultiFlag + I MultiFlag + PrintOut int ) func init() { flag.Var(&D, "D", "predefined symbol with optional simple value -D=identifier=value; can be set multiple times") flag.Var(&I, "I", "include directory; can be set multiple times") objabi.AddVersionFlag() // -V + objabi.Flagcount("S", "print assembly and machine code", &PrintOut) } // MultiFlag allows setting a value multiple times to collect a list, as in -I=dir1 -I=dir2. diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go index a6eb44de73..fd079a2ccd 100644 --- a/src/cmd/asm/main.go +++ b/src/cmd/asm/main.go @@ -35,9 +35,7 @@ func main() { flags.Parse() ctxt := obj.Linknew(architecture.LinkArch) - if *flags.PrintOut { - ctxt.Debugasm = 1 - } + ctxt.Debugasm = flags.PrintOut ctxt.Flag_dynlink = *flags.Dynlink ctxt.Flag_shared = *flags.Shared || *flags.Dynlink ctxt.IsAsm = true -- cgit v1.2.3-54-g00ecf From 982ac06f3df4ea08ae0506e6f9fb53eb27ddb140 Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Thu, 17 Sep 2020 15:35:31 -0400 Subject: cmd/compile,cmd/asm: dump sym ABI versions for -S=2 When -S=2 is in effect for the compiler/assembler, include symbol ABI values for defined symbols and relocations. This is intended to help make it easier to distinguish between a symbol and its ABI wrapper. Change-Id: Ifbf71372392075f15363b40e882b2132406b7d6d Reviewed-on: https://go-review.googlesource.com/c/go/+/255718 Trust: Than McIntosh Run-TryBot: Than McIntosh TryBot-Result: Go Bot Reviewed-by: Cherry Zhang Reviewed-by: David Chase --- src/cmd/internal/obj/objfile.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index 7bc4f4992e..aede5fe71c 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -663,7 +663,11 @@ func (ctxt *Link) writeSymDebug(s *LSym) { } func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) { - fmt.Fprintf(ctxt.Bso, "%s ", name) + ver := "" + if ctxt.Debugasm > 1 { + ver = fmt.Sprintf("<%d>", s.ABI()) + } + fmt.Fprintf(ctxt.Bso, "%s%s ", name, ver) if s.Type != 0 { fmt.Fprintf(ctxt.Bso, "%v ", s.Type) } @@ -726,15 +730,19 @@ func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) { sort.Sort(relocByOff(s.R)) // generate stable output for _, r := range s.R { name := "" + ver := "" if r.Sym != nil { name = r.Sym.Name + if ctxt.Debugasm > 1 { + ver = fmt.Sprintf("<%d>", s.ABI()) + } } else if r.Type == objabi.R_TLS_LE { name = "TLS" } if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) { - fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%x\n", int(r.Off), r.Siz, r.Type, name, uint64(r.Add)) + fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s%s+%x\n", int(r.Off), r.Siz, r.Type, name, ver, uint64(r.Add)) } else { - fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, name, r.Add) + fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s%s+%d\n", int(r.Off), r.Siz, r.Type, name, ver, r.Add) } } } -- cgit v1.2.3-54-g00ecf From 9e9c030083491aa485152601ebb3b96faa6dec4c Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 15 Sep 2020 21:20:22 -0700 Subject: debug/elf: add many PT_ and DT_ constants Change-Id: Icbb5a0f0ff4aa0a425aa4a15477da7bd0d58339c Reviewed-on: https://go-review.googlesource.com/c/go/+/255138 Trust: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Go Bot Reviewed-by: Tobias Klauser Reviewed-by: Cherry Zhang --- src/debug/elf/elf.go | 223 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 201 insertions(+), 22 deletions(-) diff --git a/src/debug/elf/elf.go b/src/debug/elf/elf.go index 96a67ce732..2b777eabac 100644 --- a/src/debug/elf/elf.go +++ b/src/debug/elf/elf.go @@ -745,18 +745,51 @@ func (i CompressionType) GoString() string { return stringName(uint32(i), compre type ProgType int const ( - PT_NULL ProgType = 0 /* Unused entry. */ - PT_LOAD ProgType = 1 /* Loadable segment. */ - PT_DYNAMIC ProgType = 2 /* Dynamic linking information segment. */ - PT_INTERP ProgType = 3 /* Pathname of interpreter. */ - PT_NOTE ProgType = 4 /* Auxiliary information. */ - PT_SHLIB ProgType = 5 /* Reserved (not used). */ - PT_PHDR ProgType = 6 /* Location of program header itself. */ - PT_TLS ProgType = 7 /* Thread local storage segment */ - PT_LOOS ProgType = 0x60000000 /* First OS-specific. */ - PT_HIOS ProgType = 0x6fffffff /* Last OS-specific. */ - PT_LOPROC ProgType = 0x70000000 /* First processor-specific type. */ - PT_HIPROC ProgType = 0x7fffffff /* Last processor-specific type. */ + PT_NULL ProgType = 0 /* Unused entry. */ + PT_LOAD ProgType = 1 /* Loadable segment. */ + PT_DYNAMIC ProgType = 2 /* Dynamic linking information segment. */ + PT_INTERP ProgType = 3 /* Pathname of interpreter. */ + PT_NOTE ProgType = 4 /* Auxiliary information. */ + PT_SHLIB ProgType = 5 /* Reserved (not used). */ + PT_PHDR ProgType = 6 /* Location of program header itself. */ + PT_TLS ProgType = 7 /* Thread local storage segment */ + + PT_LOOS ProgType = 0x60000000 /* First OS-specific. */ + + PT_GNU_EH_FRAME ProgType = 0x6474e550 /* Frame unwind information */ + PT_GNU_STACK ProgType = 0x6474e551 /* Stack flags */ + PT_GNU_RELRO ProgType = 0x6474e552 /* Read only after relocs */ + PT_GNU_PROPERTY ProgType = 0x6474e553 /* GNU property */ + PT_GNU_MBIND_LO ProgType = 0x6474e555 /* Mbind segments start */ + PT_GNU_MBIND_HI ProgType = 0x6474f554 /* Mbind segments finish */ + + PT_PAX_FLAGS ProgType = 0x65041580 /* PAX flags */ + + PT_OPENBSD_RANDOMIZE ProgType = 0x65a3dbe6 /* Random data */ + PT_OPENBSD_WXNEEDED ProgType = 0x65a3dbe7 /* W^X violations */ + PT_OPENBSD_BOOTDATA ProgType = 0x65a41be6 /* Boot arguments */ + + PT_SUNW_EH_FRAME ProgType = 0x6474e550 /* Frame unwind information */ + PT_SUNWSTACK ProgType = 0x6ffffffb /* Stack segment */ + + PT_HIOS ProgType = 0x6fffffff /* Last OS-specific. */ + + PT_LOPROC ProgType = 0x70000000 /* First processor-specific type. */ + + PT_ARM_ARCHEXT ProgType = 0x70000000 /* Architecture compatibility */ + PT_ARM_EXIDX ProgType = 0x70000001 /* Exception unwind tables */ + + PT_AARCH64_ARCHEXT ProgType = 0x70000000 /* Architecture compatibility */ + PT_AARCH64_UNWIND ProgType = 0x70000001 /* Exception unwind tables */ + + PT_MIPS_REGINFO ProgType = 0x70000000 /* Register usage */ + PT_MIPS_RTPROC ProgType = 0x70000001 /* Runtime procedures */ + PT_MIPS_OPTIONS ProgType = 0x70000002 /* Options */ + PT_MIPS_ABIFLAGS ProgType = 0x70000003 /* ABI flags */ + + PT_S390_PGSTE ProgType = 0x70000000 /* 4k page table size */ + + PT_HIPROC ProgType = 0x7fffffff /* Last processor-specific type. */ ) var ptStrings = []intName{ @@ -769,8 +802,19 @@ var ptStrings = []intName{ {6, "PT_PHDR"}, {7, "PT_TLS"}, {0x60000000, "PT_LOOS"}, + {0x6474e550, "PT_GNU_EH_FRAME"}, + {0x6474e551, "PT_GNU_STACK"}, + {0x6474e552, "PT_GNU_RELRO"}, + {0x6474e553, "PT_GNU_PROPERTY"}, + {0x65041580, "PT_PAX_FLAGS"}, + {0x65a3dbe6, "PT_OPENBSD_RANDOMIZE"}, + {0x65a3dbe7, "PT_OPENBSD_WXNEEDED"}, + {0x65a41be6, "PT_OPENBSD_BOOTDATA"}, + {0x6ffffffb, "PT_SUNWSTACK"}, {0x6fffffff, "PT_HIOS"}, {0x70000000, "PT_LOPROC"}, + // We don't list the processor-dependent ProgTypes, + // as the values overlap. {0x7fffffff, "PT_HIPROC"}, } @@ -837,15 +881,114 @@ const ( the interpretation of the d_un union as follows: even == 'd_ptr', even == 'd_val' or none */ - DT_PREINIT_ARRAY DynTag = 32 /* Address of the array of pointers to pre-initialization functions. */ - DT_PREINIT_ARRAYSZ DynTag = 33 /* Size in bytes of the array of pre-initialization functions. */ - DT_LOOS DynTag = 0x6000000d /* First OS-specific */ - DT_HIOS DynTag = 0x6ffff000 /* Last OS-specific */ - DT_VERSYM DynTag = 0x6ffffff0 - DT_VERNEED DynTag = 0x6ffffffe - DT_VERNEEDNUM DynTag = 0x6fffffff - DT_LOPROC DynTag = 0x70000000 /* First processor-specific type. */ - DT_HIPROC DynTag = 0x7fffffff /* Last processor-specific type. */ + DT_PREINIT_ARRAY DynTag = 32 /* Address of the array of pointers to pre-initialization functions. */ + DT_PREINIT_ARRAYSZ DynTag = 33 /* Size in bytes of the array of pre-initialization functions. */ + DT_SYMTAB_SHNDX DynTag = 34 /* Address of SHT_SYMTAB_SHNDX section. */ + + DT_LOOS DynTag = 0x6000000d /* First OS-specific */ + DT_HIOS DynTag = 0x6ffff000 /* Last OS-specific */ + + DT_VALRNGLO DynTag = 0x6ffffd00 + DT_GNU_PRELINKED DynTag = 0x6ffffdf5 + DT_GNU_CONFLICTSZ DynTag = 0x6ffffdf6 + DT_GNU_LIBLISTSZ DynTag = 0x6ffffdf7 + DT_CHECKSUM DynTag = 0x6ffffdf8 + DT_PLTPADSZ DynTag = 0x6ffffdf9 + DT_MOVEENT DynTag = 0x6ffffdfa + DT_MOVESZ DynTag = 0x6ffffdfb + DT_FEATURE DynTag = 0x6ffffdfc + DT_POSFLAG_1 DynTag = 0x6ffffdfd + DT_SYMINSZ DynTag = 0x6ffffdfe + DT_SYMINENT DynTag = 0x6ffffdff + DT_VALRNGHI DynTag = 0x6ffffdff + + DT_ADDRRNGLO DynTag = 0x6ffffe00 + DT_GNU_HASH DynTag = 0x6ffffef5 + DT_TLSDESC_PLT DynTag = 0x6ffffef6 + DT_TLSDESC_GOT DynTag = 0x6ffffef7 + DT_GNU_CONFLICT DynTag = 0x6ffffef8 + DT_GNU_LIBLIST DynTag = 0x6ffffef9 + DT_CONFIG DynTag = 0x6ffffefa + DT_DEPAUDIT DynTag = 0x6ffffefb + DT_AUDIT DynTag = 0x6ffffefc + DT_PLTPAD DynTag = 0x6ffffefd + DT_MOVETAB DynTag = 0x6ffffefe + DT_SYMINFO DynTag = 0x6ffffeff + DT_ADDRRNGHI DynTag = 0x6ffffeff + + DT_VERSYM DynTag = 0x6ffffff0 + DT_RELACOUNT DynTag = 0x6ffffff9 + DT_RELCOUNT DynTag = 0x6ffffffa + DT_FLAGS_1 DynTag = 0x6ffffffb + DT_VERDEF DynTag = 0x6ffffffc + DT_VERDEFNUM DynTag = 0x6ffffffd + DT_VERNEED DynTag = 0x6ffffffe + DT_VERNEEDNUM DynTag = 0x6fffffff + + DT_LOPROC DynTag = 0x70000000 /* First processor-specific type. */ + + DT_MIPS_RLD_VERSION DynTag = 0x70000001 + DT_MIPS_TIME_STAMP DynTag = 0x70000002 + DT_MIPS_ICHECKSUM DynTag = 0x70000003 + DT_MIPS_IVERSION DynTag = 0x70000004 + DT_MIPS_FLAGS DynTag = 0x70000005 + DT_MIPS_BASE_ADDRESS DynTag = 0x70000006 + DT_MIPS_MSYM DynTag = 0x70000007 + DT_MIPS_CONFLICT DynTag = 0x70000008 + DT_MIPS_LIBLIST DynTag = 0x70000009 + DT_MIPS_LOCAL_GOTNO DynTag = 0x7000000a + DT_MIPS_CONFLICTNO DynTag = 0x7000000b + DT_MIPS_LIBLISTNO DynTag = 0x70000010 + DT_MIPS_SYMTABNO DynTag = 0x70000011 + DT_MIPS_UNREFEXTNO DynTag = 0x70000012 + DT_MIPS_GOTSYM DynTag = 0x70000013 + DT_MIPS_HIPAGENO DynTag = 0x70000014 + DT_MIPS_RLD_MAP DynTag = 0x70000016 + DT_MIPS_DELTA_CLASS DynTag = 0x70000017 + DT_MIPS_DELTA_CLASS_NO DynTag = 0x70000018 + DT_MIPS_DELTA_INSTANCE DynTag = 0x70000019 + DT_MIPS_DELTA_INSTANCE_NO DynTag = 0x7000001a + DT_MIPS_DELTA_RELOC DynTag = 0x7000001b + DT_MIPS_DELTA_RELOC_NO DynTag = 0x7000001c + DT_MIPS_DELTA_SYM DynTag = 0x7000001d + DT_MIPS_DELTA_SYM_NO DynTag = 0x7000001e + DT_MIPS_DELTA_CLASSSYM DynTag = 0x70000020 + DT_MIPS_DELTA_CLASSSYM_NO DynTag = 0x70000021 + DT_MIPS_CXX_FLAGS DynTag = 0x70000022 + DT_MIPS_PIXIE_INIT DynTag = 0x70000023 + DT_MIPS_SYMBOL_LIB DynTag = 0x70000024 + DT_MIPS_LOCALPAGE_GOTIDX DynTag = 0x70000025 + DT_MIPS_LOCAL_GOTIDX DynTag = 0x70000026 + DT_MIPS_HIDDEN_GOTIDX DynTag = 0x70000027 + DT_MIPS_PROTECTED_GOTIDX DynTag = 0x70000028 + DT_MIPS_OPTIONS DynTag = 0x70000029 + DT_MIPS_INTERFACE DynTag = 0x7000002a + DT_MIPS_DYNSTR_ALIGN DynTag = 0x7000002b + DT_MIPS_INTERFACE_SIZE DynTag = 0x7000002c + DT_MIPS_RLD_TEXT_RESOLVE_ADDR DynTag = 0x7000002d + DT_MIPS_PERF_SUFFIX DynTag = 0x7000002e + DT_MIPS_COMPACT_SIZE DynTag = 0x7000002f + DT_MIPS_GP_VALUE DynTag = 0x70000030 + DT_MIPS_AUX_DYNAMIC DynTag = 0x70000031 + DT_MIPS_PLTGOT DynTag = 0x70000032 + DT_MIPS_RWPLT DynTag = 0x70000034 + DT_MIPS_RLD_MAP_REL DynTag = 0x70000035 + + DT_PPC_GOT DynTag = 0x70000000 + DT_PPC_OPT DynTag = 0x70000001 + + DT_PPC64_GLINK DynTag = 0x70000000 + DT_PPC64_OPD DynTag = 0x70000001 + DT_PPC64_OPDSZ DynTag = 0x70000002 + DT_PPC64_OPT DynTag = 0x70000003 + + DT_SPARC_REGISTER DynTag = 0x70000001 + + DT_AUXILIARY DynTag = 0x7ffffffd + DT_USED DynTag = 0x7ffffffe + DT_FILTER DynTag = 0x7fffffff + + DT_HIPROC DynTag = 0x7fffffff /* Last processor-specific type. */ ) var dtStrings = []intName{ @@ -883,13 +1026,49 @@ var dtStrings = []intName{ {32, "DT_ENCODING"}, {32, "DT_PREINIT_ARRAY"}, {33, "DT_PREINIT_ARRAYSZ"}, + {34, "DT_SYMTAB_SHNDX"}, {0x6000000d, "DT_LOOS"}, {0x6ffff000, "DT_HIOS"}, + {0x6ffffd00, "DT_VALRNGLO"}, + {0x6ffffdf5, "DT_GNU_PRELINKED"}, + {0x6ffffdf6, "DT_GNU_CONFLICTSZ"}, + {0x6ffffdf7, "DT_GNU_LIBLISTSZ"}, + {0x6ffffdf8, "DT_CHECKSUM"}, + {0x6ffffdf9, "DT_PLTPADSZ"}, + {0x6ffffdfa, "DT_MOVEENT"}, + {0x6ffffdfb, "DT_MOVESZ"}, + {0x6ffffdfc, "DT_FEATURE"}, + {0x6ffffdfd, "DT_POSFLAG_1"}, + {0x6ffffdfe, "DT_SYMINSZ"}, + {0x6ffffdff, "DT_SYMINENT"}, + {0x6ffffdff, "DT_VALRNGHI"}, + {0x6ffffe00, "DT_ADDRRNGLO"}, + {0x6ffffef5, "DT_GNU_HASH"}, + {0x6ffffef6, "DT_TLSDESC_PLT"}, + {0x6ffffef7, "DT_TLSDESC_GOT"}, + {0x6ffffef8, "DT_GNU_CONFLICT"}, + {0x6ffffef9, "DT_GNU_LIBLIST"}, + {0x6ffffefa, "DT_CONFIG"}, + {0x6ffffefb, "DT_DEPAUDIT"}, + {0x6ffffefc, "DT_AUDIT"}, + {0x6ffffefd, "DT_PLTPAD"}, + {0x6ffffefe, "DT_MOVETAB"}, + {0x6ffffeff, "DT_SYMINFO"}, + {0x6ffffeff, "DT_ADDRRNGHI"}, {0x6ffffff0, "DT_VERSYM"}, + {0x6ffffff9, "DT_RELACOUNT"}, + {0x6ffffffa, "DT_RELCOUNT"}, + {0x6ffffffb, "DT_FLAGS_1"}, + {0x6ffffffc, "DT_VERDEF"}, + {0x6ffffffd, "DT_VERDEFNUM"}, {0x6ffffffe, "DT_VERNEED"}, {0x6fffffff, "DT_VERNEEDNUM"}, {0x70000000, "DT_LOPROC"}, - {0x7fffffff, "DT_HIPROC"}, + // We don't list the processor-dependent DynTags, + // as the values overlap. + {0x7ffffffd, "DT_AUXILIARY"}, + {0x7ffffffe, "DT_USED"}, + {0x7fffffff, "DT_FILTER"}, } func (i DynTag) String() string { return stringName(uint32(i), dtStrings, false) } -- cgit v1.2.3-54-g00ecf From 1ee30d25c517cfa5674a35754602f1a9ba3562f4 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Thu, 17 Sep 2020 16:22:14 -0400 Subject: runtime: correctly log stderr in TestFakeTime Change-Id: Iaf122ce7a8b8fb431199399aeed67b128a34d20b Reviewed-on: https://go-review.googlesource.com/c/go/+/255720 Trust: Cherry Zhang Run-TryBot: Cherry Zhang TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor --- src/runtime/time_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/time_test.go b/src/runtime/time_test.go index bf29561144..a8dab7db8e 100644 --- a/src/runtime/time_test.go +++ b/src/runtime/time_test.go @@ -38,7 +38,7 @@ func TestFakeTime(t *testing.T) { } t.Logf("raw stdout: %q", stdout.String()) - t.Logf("raw stderr: %q", stdout.String()) + t.Logf("raw stderr: %q", stderr.String()) f1, err1 := parseFakeTime(stdout.Bytes()) if err1 != nil { -- cgit v1.2.3-54-g00ecf From 0dc369b127651830edef453938dfb5c149aa37cf Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Wed, 16 Sep 2020 15:42:00 +0700 Subject: cmd/compile: make typecheck set correct untyped type Passes toolstash-check. Change-Id: Ie631d8dacb1cc76613e1f50da8422850ac7119a1 Reviewed-on: https://go-review.googlesource.com/c/go/+/255217 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/const.go | 122 +++++++++++-------------------- src/cmd/compile/internal/gc/typecheck.go | 19 ++++- 2 files changed, 60 insertions(+), 81 deletions(-) diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go index fe73df9d57..59b2c56051 100644 --- a/src/cmd/compile/internal/gc/const.go +++ b/src/cmd/compile/internal/gc/const.go @@ -44,7 +44,7 @@ func (v Val) Ctype() Ctype { Fatalf("unexpected Ctype for %T", v.U) panic("unreachable") case nil: - return 0 + return CTxxx case *NilVal: return CTNIL case bool: @@ -261,7 +261,7 @@ func convlit1(n *Node, t *types.Type, explicit bool, context func() string) *Nod } if t == nil || !okforconst[t.Etype] { - t = defaultType(idealkind(n)) + t = defaultType(n.Type) } switch n.Op { @@ -994,10 +994,8 @@ func setconst(n *Node, v Val) { Xoffset: BADWIDTH, } n.SetVal(v) - if n.Type.IsUntyped() { - // TODO(mdempsky): Make typecheck responsible for setting - // the correct untyped type. - n.Type = idealType(v.Ctype()) + if vt := idealType(v.Ctype()); n.Type.IsUntyped() && n.Type != vt { + Fatalf("untyped type mismatch, have: %v, want: %v", n.Type, vt) } // Check range. @@ -1056,67 +1054,6 @@ func idealType(ct Ctype) *types.Type { return nil } -// idealkind returns a constant kind like consttype -// but for an arbitrary "ideal" (untyped constant) expression. -func idealkind(n *Node) Ctype { - if n == nil || !n.Type.IsUntyped() { - return CTxxx - } - - switch n.Op { - default: - return CTxxx - - case OLITERAL: - return n.Val().Ctype() - - // numeric kinds. - case OADD, - OAND, - OANDNOT, - OBITNOT, - ODIV, - ONEG, - OMOD, - OMUL, - OSUB, - OXOR, - OOR, - OPLUS: - k1 := idealkind(n.Left) - k2 := idealkind(n.Right) - if k1 > k2 { - return k1 - } else { - return k2 - } - - case OREAL, OIMAG: - return CTFLT - - case OCOMPLEX: - return CTCPLX - - case OADDSTR: - return CTSTR - - case OANDAND, - OEQ, - OGE, - OGT, - OLE, - OLT, - ONE, - ONOT, - OOROR: - return CTBOOL - - // shifts (beware!). - case OLSH, ORSH: - return idealkind(n.Left) - } -} - // defaultlit on both nodes simultaneously; // if they're both ideal going in they better // get the same type going out. @@ -1152,32 +1089,57 @@ func defaultlit2(l *Node, r *Node, force bool) (*Node, *Node) { return l, r } - k := idealkind(l) - if rk := idealkind(r); rk > k { - k = rk + nn := l + if ctype(r.Type) > ctype(l.Type) { + nn = r } - t := defaultType(k) + + t := defaultType(nn.Type) l = convlit(l, t) r = convlit(r, t) return l, r } -func defaultType(k Ctype) *types.Type { - switch k { - case CTBOOL: +func ctype(t *types.Type) Ctype { + switch t { + case types.Idealbool: + return CTBOOL + case types.Idealstring: + return CTSTR + case types.Idealint: + return CTINT + case types.Idealrune: + return CTRUNE + case types.Idealfloat: + return CTFLT + case types.Idealcomplex: + return CTCPLX + } + Fatalf("bad type %v", t) + panic("unreachable") +} + +func defaultType(t *types.Type) *types.Type { + if !t.IsUntyped() { + return t + } + + switch t { + case types.Idealbool: return types.Types[TBOOL] - case CTSTR: + case types.Idealstring: return types.Types[TSTRING] - case CTINT: + case types.Idealint: return types.Types[TINT] - case CTRUNE: + case types.Idealrune: return types.Runetype - case CTFLT: + case types.Idealfloat: return types.Types[TFLOAT64] - case CTCPLX: + case types.Idealcomplex: return types.Types[TCOMPLEX128] } - Fatalf("bad idealkind: %v", k) + + Fatalf("bad type %v", t) return nil } diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 834c1a8ee6..274787a22b 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -623,6 +623,9 @@ func typecheck1(n *Node, top int) (res *Node) { // no defaultlit for left // the outer context gives the type n.Type = l.Type + if (l.Type == types.Idealfloat || l.Type == types.Idealcomplex) && r.Op == OLITERAL { + n.Type = types.Idealint + } break } @@ -798,6 +801,20 @@ func typecheck1(n *Node, top int) (res *Node) { } n.Type = t + if t.Etype == TIDEAL { + switch { + case l.Type == types.Idealcomplex || r.Type == types.Idealcomplex: + n.Type = types.Idealcomplex + case l.Type == types.Idealfloat || r.Type == types.Idealfloat: + n.Type = types.Idealfloat + case l.Type == types.Idealrune || r.Type == types.Idealrune: + n.Type = types.Idealrune + case l.Type == types.Idealint || r.Type == types.Idealint: + n.Type = types.Idealint + default: + Fatalf("bad untyped type: %v", t) + } + } case OBITNOT, ONEG, ONOT, OPLUS: ok |= ctxExpr @@ -1678,7 +1695,7 @@ func typecheck1(n *Node, top int) (res *Node) { } var why string n.Op = convertop(n.Left.Op == OLITERAL, t, n.Type, &why) - if n.Op == 0 { + if n.Op == OXXX { if !n.Diag() && !n.Type.Broke() && !n.Left.Diag() { yyerror("cannot convert %L to type %v%s", n.Left, n.Type, why) n.SetDiag(true) -- cgit v1.2.3-54-g00ecf From dc59469f5178a0715a582cbfcc4cf9c06a2c9e82 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Thu, 17 Sep 2020 22:53:39 +0700 Subject: cmd/compile: move validation from unary/binaryOp to typecheck CL 254400 makes typecheck set untyped type correctly. We now have enough information to check valid operators for a type in typecheck. Passes toolstash-check. Change-Id: I01a7606ee6ce9964ec52430d53eaa886442bd17f Reviewed-on: https://go-review.googlesource.com/c/go/+/255617 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/const.go | 19 +------------- src/cmd/compile/internal/gc/typecheck.go | 44 +++++++++++++------------------- 2 files changed, 19 insertions(+), 44 deletions(-) diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go index 59b2c56051..399d0148bb 100644 --- a/src/cmd/compile/internal/gc/const.go +++ b/src/cmd/compile/internal/gc/const.go @@ -838,10 +838,6 @@ Outer: return Val{} } u.Quo(y) - case OMOD, OOR, OAND, OANDNOT, OXOR: - // TODO(mdempsky): Move to typecheck; see #31060. - yyerror("invalid operation: operator %v not defined on untyped float", op) - return Val{} default: break Outer } @@ -867,10 +863,6 @@ Outer: yyerror("complex division by zero") return Val{} } - case OMOD, OOR, OAND, OANDNOT, OXOR: - // TODO(mdempsky): Move to typecheck; see #31060. - yyerror("invalid operation: operator %v not defined on untyped complex", op) - return Val{} default: break Outer } @@ -932,15 +924,6 @@ func unaryOp(op Op, x Val, t *types.Type) Val { } u.Xor(x) return Val{U: u} - - case CTFLT: - // TODO(mdempsky): Move to typecheck; see #31060. - yyerror("invalid operation: operator %v not defined on untyped float", op) - return Val{} - case CTCPLX: - // TODO(mdempsky): Move to typecheck; see #31060. - yyerror("invalid operation: operator %v not defined on untyped complex", op) - return Val{} } case ONOT: @@ -1120,7 +1103,7 @@ func ctype(t *types.Type) Ctype { } func defaultType(t *types.Type) *types.Type { - if !t.IsUntyped() { + if !t.IsUntyped() || t.Etype == TNIL { return t } diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 274787a22b..faa13d72f9 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -716,8 +716,22 @@ func typecheck1(n *Node, top int) (res *Node) { } } - if !okfor[op][et] { - yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(t)) + if t.Etype == TIDEAL { + switch { + case l.Type == types.Idealcomplex || r.Type == types.Idealcomplex: + t = types.Idealcomplex + case l.Type == types.Idealfloat || r.Type == types.Idealfloat: + t = types.Idealfloat + case l.Type == types.Idealrune || r.Type == types.Idealrune: + t = types.Idealrune + case l.Type == types.Idealint || r.Type == types.Idealint: + t = types.Idealint + default: + Fatalf("bad untyped type: %v", t) + } + } + if dt := defaultType(t); !okfor[op][dt.Etype] { + yyerror("invalid operation: %v (operator %v not defined on %v)", n, op, t) n.Type = nil return n } @@ -756,15 +770,7 @@ func typecheck1(n *Node, top int) (res *Node) { } } - t = l.Type if iscmp[n.Op] { - // TIDEAL includes complex constant, but only OEQ and ONE are defined for complex, - // so check that the n.op is available for complex here before doing evconst. - if !okfor[n.Op][TCOMPLEX128] && (Isconst(l, CTCPLX) || Isconst(r, CTCPLX)) { - yyerror("invalid operation: %v (operator %v not defined on untyped complex)", n, n.Op) - n.Type = nil - return n - } evconst(n) t = types.Idealbool if n.Op != OLITERAL { @@ -801,20 +807,6 @@ func typecheck1(n *Node, top int) (res *Node) { } n.Type = t - if t.Etype == TIDEAL { - switch { - case l.Type == types.Idealcomplex || r.Type == types.Idealcomplex: - n.Type = types.Idealcomplex - case l.Type == types.Idealfloat || r.Type == types.Idealfloat: - n.Type = types.Idealfloat - case l.Type == types.Idealrune || r.Type == types.Idealrune: - n.Type = types.Idealrune - case l.Type == types.Idealint || r.Type == types.Idealint: - n.Type = types.Idealint - default: - Fatalf("bad untyped type: %v", t) - } - } case OBITNOT, ONEG, ONOT, OPLUS: ok |= ctxExpr @@ -825,8 +817,8 @@ func typecheck1(n *Node, top int) (res *Node) { n.Type = nil return n } - if !okfor[n.Op][t.Etype] { - yyerror("invalid operation: %v %v", n.Op, t) + if !okfor[n.Op][defaultType(t).Etype] { + yyerror("invalid operation: %v (operator %v not defined on %s)", n, n.Op, typekind(t)) n.Type = nil return n } -- cgit v1.2.3-54-g00ecf From 0b1cec7ad33a073be15db89da90efcba9797df83 Mon Sep 17 00:00:00 2001 From: root <2863768433@qq.com> Date: Sun, 30 Aug 2020 11:36:45 +0800 Subject: cmd/compile: rotate phase's title 180 degrees in ssa/html.go Modify phase's title according to html.go:122 TODO. Fixes #41098 Change-Id: I58fa365e718600aaaa0a72cce72d35a484cde8b8 Reviewed-on: https://go-review.googlesource.com/c/go/+/251657 Reviewed-by: Bradford Lamson-Scribner Reviewed-by: Keith Randall Trust: Josh Bleecher Snyder --- src/cmd/compile/internal/ssa/html.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go index ba37a80412..1c70b64708 100644 --- a/src/cmd/compile/internal/ssa/html.go +++ b/src/cmd/compile/internal/ssa/html.go @@ -119,7 +119,8 @@ td.collapsed { } td.collapsed div { - /* TODO: Flip the direction of the phase's title 90 degrees on a collapsed column. */ + text-align: right; + transform: rotate(180deg); writing-mode: vertical-lr; white-space: pre; } -- cgit v1.2.3-54-g00ecf From 234e23d76351c31b191e25b688aa43248d9b3d5b Mon Sep 17 00:00:00 2001 From: root <2863768433@qq.com> Date: Mon, 17 Aug 2020 10:03:06 +0800 Subject: cmd/compile: make expanded/hidden columns in GOSSAFUNC persist across reloads use pushState with updated state and read it on page load,so that state can survive across reloads. Change-Id: I6c5e80e9747576245b979a62cb96d231d8f27d57 Reviewed-on: https://go-review.googlesource.com/c/go/+/248687 Run-TryBot: Alberto Donizetti TryBot-Result: Go Bot Reviewed-by: Bradford Lamson-Scribner Reviewed-by: Keith Randall Trust: Josh Bleecher Snyder --- src/cmd/compile/internal/ssa/html.go | 48 ++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go index 1c70b64708..c781ca92cc 100644 --- a/src/cmd/compile/internal/ssa/html.go +++ b/src/cmd/compile/internal/ssa/html.go @@ -358,6 +358,21 @@ body.darkmode ellipse.outline-black { outline: gray solid 2px; }