aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Dempsky <mdempsky@google.com>2021-01-12 15:56:54 -0800
committerMatthew Dempsky <mdempsky@google.com>2021-01-12 15:56:55 -0800
commitf065ff221b546c9ac550d947f89eb3b44b11fc2f (patch)
tree2957e97b914933fc2d68196539ed2c66998a12ff
parent099599662d15598fc2690e60bd36bc087a3bdec5 (diff)
parentd9acf6f3a3758c3096ee5ef5a24c2bc5df9d9c8b (diff)
downloadgo-f065ff221b546c9ac550d947f89eb3b44b11fc2f.tar.gz
go-f065ff221b546c9ac550d947f89eb3b44b11fc2f.zip
[dev.typeparams] all: merge dev.regabi (d9acf6f) into dev.typeparams
Conflicts: - src/cmd/compile/fmtmap_test.go Merge List: + 2021-01-12 d9acf6f3a3 [dev.regabi] cmd/compile: remove Func.ClosureType + 2021-01-12 41352fd401 [dev.regabi] cmd/compile: transform closures during walk + 2021-01-12 d6ad88b4db [dev.regabi] cmd/compile: compile functions before closures + 2021-01-12 432f9ffb11 [dev.regabi] cmd/compile: unindent compileFunctions + 2021-01-12 cc90e7a51e [dev.regabi] cmd/compile: always use the compile queue + 2021-01-12 cd5b74d2df [dev.regabi] cmd/compile: call NeedFuncSym in InitLSym + 2021-01-12 95acd8121b [dev.regabi] cmd/compile: remove Name.Typegen + 2021-01-12 12ee55ba7b [dev.regabi] cmd/compile: stop using Vargen for import/export + 2021-01-12 b4d2a0445b [dev.regabi] cmd/compile: refactor closure var setup/teardown + 2021-01-12 f57f484053 [dev.regabi] cmd/compile: decouple escape analysis from Name.Vargen + 2021-01-10 7fd84c6e46 [dev.regabi] cmd/compile: remove OCLOSUREREAD + 2021-01-10 c9c26d7ffb [dev.regabi] cmd/compile: use ClosureVars for method value wrappers + 2021-01-10 950cf4d46c [dev.regabi] cmd/compile: bind closure vars during SSA constructions + 2021-01-10 8b2efa990b [dev.regabi] cmd/compile: deref PAUTOHEAPs during SSA construction + 2021-01-08 6ee9b118a2 [dev.regabi] cmd/compile: remove fmt_test code; it has outlived its usefulness + 2021-01-08 b241938e04 [dev.regabi] cmd/compile: fix some methods error text Change-Id: I9a530f9a78b16e2bb14ea0a4ecbd9a75f9350342
-rw-r--r--src/cmd/compile/fmt_test.go615
-rw-r--r--src/cmd/compile/fmtmap_test.go102
-rw-r--r--src/cmd/compile/internal/base/debug.go1
-rw-r--r--src/cmd/compile/internal/escape/escape.go26
-rw-r--r--src/cmd/compile/internal/gc/compile.go184
-rw-r--r--src/cmd/compile/internal/gc/main.go22
-rw-r--r--src/cmd/compile/internal/gc/obj.go5
-rw-r--r--src/cmd/compile/internal/ir/expr.go17
-rw-r--r--src/cmd/compile/internal/ir/func.go12
-rw-r--r--src/cmd/compile/internal/ir/name.go86
-rw-r--r--src/cmd/compile/internal/ir/node.go27
-rw-r--r--src/cmd/compile/internal/ir/node_gen.go16
-rw-r--r--src/cmd/compile/internal/ir/op_string.go27
-rw-r--r--src/cmd/compile/internal/ir/sizeof_test.go4
-rw-r--r--src/cmd/compile/internal/liveness/plive.go1
-rw-r--r--src/cmd/compile/internal/noder/noder.go66
-rw-r--r--src/cmd/compile/internal/reflectdata/alg.go4
-rw-r--r--src/cmd/compile/internal/ssagen/abi.go5
-rw-r--r--src/cmd/compile/internal/ssagen/ssa.go59
-rw-r--r--src/cmd/compile/internal/staticdata/data.go13
-rw-r--r--src/cmd/compile/internal/typecheck/dcl.go33
-rw-r--r--src/cmd/compile/internal/typecheck/func.go33
-rw-r--r--src/cmd/compile/internal/typecheck/iexport.go58
-rw-r--r--src/cmd/compile/internal/typecheck/iimport.go103
-rw-r--r--src/cmd/compile/internal/typecheck/typecheck.go21
-rw-r--r--src/cmd/compile/internal/types/alg.go2
-rw-r--r--src/cmd/compile/internal/walk/closure.go178
-rw-r--r--src/cmd/compile/internal/walk/expr.go30
-rw-r--r--src/cmd/compile/internal/walk/walk.go2
-rw-r--r--test/fixedbugs/issue20250.go2
-rw-r--r--test/fixedbugs/issue20780.go16
-rw-r--r--test/fixedbugs/issue20780b.go62
-rw-r--r--test/fixedbugs/issue43633.dir/a.go28
-rw-r--r--test/fixedbugs/issue43633.dir/main.go18
-rw-r--r--test/fixedbugs/issue43633.go7
-rw-r--r--test/fixedbugs/issue7921.go2
-rw-r--r--test/nilptr3.go8
37 files changed, 635 insertions, 1260 deletions
diff --git a/src/cmd/compile/fmt_test.go b/src/cmd/compile/fmt_test.go
deleted file mode 100644
index 6398a84f8f..0000000000
--- a/src/cmd/compile/fmt_test.go
+++ /dev/null
@@ -1,615 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements TestFormats; a test that verifies
-// format strings in the compiler (this directory and all
-// subdirectories, recursively).
-//
-// TestFormats finds potential (Printf, etc.) format strings.
-// If they are used in a call, the format verbs are verified
-// based on the matching argument type against a precomputed
-// map of valid formats (knownFormats). This map can be used to
-// automatically rewrite format strings across all compiler
-// files with the -r flag.
-//
-// The format map needs to be updated whenever a new (type,
-// format) combination is found and the format verb is not
-// 'v' or 'T' (as in "%v" or "%T"). To update the map auto-
-// matically from the compiler source's use of format strings,
-// use the -u flag. (Whether formats are valid for the values
-// to be formatted must be verified manually, of course.)
-//
-// The -v flag prints out the names of all functions called
-// with a format string, the names of files that were not
-// processed, and any format rewrites made (with -r).
-//
-// Run as: go test -run Formats [-r][-u][-v]
-//
-// Known shortcomings:
-// - indexed format strings ("%[2]s", etc.) are not supported
-// (the test will fail)
-// - format strings that are not simple string literals cannot
-// be updated automatically
-// (the test will fail with respective warnings)
-// - format strings in _test packages outside the current
-// package are not processed
-// (the test will report those files)
-//
-package main_test
-
-import (
- "bytes"
- "flag"
- "fmt"
- "go/ast"
- "go/build"
- "go/constant"
- "go/format"
- "go/importer"
- "go/parser"
- "go/token"
- "go/types"
- "internal/testenv"
- "io"
- "io/fs"
- "io/ioutil"
- "log"
- "os"
- "path/filepath"
- "sort"
- "strconv"
- "strings"
- "testing"
- "unicode/utf8"
-)
-
-var (
- rewrite = flag.Bool("r", false, "rewrite format strings")
- update = flag.Bool("u", false, "update known formats")
-)
-
-// The following variables collect information across all processed files.
-var (
- fset = token.NewFileSet()
- formatStrings = make(map[*ast.BasicLit]bool) // set of all potential format strings found
- foundFormats = make(map[string]bool) // set of all formats found
- callSites = make(map[*ast.CallExpr]*callSite) // map of all calls
-)
-
-// A File is a corresponding (filename, ast) pair.
-type File struct {
- name string
- ast *ast.File
-}
-
-func TestFormats(t *testing.T) {
- if testing.Short() && testenv.Builder() == "" {
- t.Skip("Skipping in short mode")
- }
- testenv.MustHaveGoBuild(t) // more restrictive than necessary, but that's ok
-
- // process all directories
- filepath.WalkDir(".", func(path string, info fs.DirEntry, err error) error {
- if info.IsDir() {
- if info.Name() == "testdata" {
- return filepath.SkipDir
- }
-
- importPath := filepath.Join("cmd/compile", path)
- if ignoredPackages[filepath.ToSlash(importPath)] {
- return filepath.SkipDir
- }
-
- pkg, err := build.Import(importPath, path, 0)
- if err != nil {
- if _, ok := err.(*build.NoGoError); ok {
- return nil // nothing to do here
- }
- t.Fatal(err)
- }
- collectPkgFormats(t, pkg)
- }
- return nil
- })
-
- // test and rewrite formats
- updatedFiles := make(map[string]File) // files that were rewritten
- for _, p := range callSites {
- // test current format literal and determine updated one
- out := formatReplace(p.str, func(index int, in string) string {
- if in == "*" {
- return in // cannot rewrite '*' (as in "%*d")
- }
- // in != '*'
- typ := p.types[index]
- format := typ + " " + in // e.g., "*Node %n"
-
- // Do not bother reporting basic types, nor %v, %T, %p.
- // Vet handles basic types, and those three formats apply to all types.
- if !strings.Contains(typ, ".") || (in == "%v" || in == "%T" || in == "%p") {
- return in
- }
-
- // check if format is known
- out, known := knownFormats[format]
-
- // record format if not yet found
- _, found := foundFormats[format]
- if !found {
- foundFormats[format] = true
- }
-
- // report an error if the format is unknown and this is the first
- // time we see it; ignore "%v" and "%T" which are always valid
- if !known && !found && in != "%v" && in != "%T" {
- t.Errorf("%s: unknown format %q for %s argument", posString(p.arg), in, typ)
- }
-
- if out == "" {
- out = in
- }
- return out
- })
-
- // replace existing format literal if it changed
- if out != p.str {
- // we cannot replace the argument if it's not a string literal for now
- // (e.g., it may be "foo" + "bar")
- lit, ok := p.arg.(*ast.BasicLit)
- if !ok {
- delete(callSites, p.call) // treat as if we hadn't found this site
- continue
- }
-
- if testing.Verbose() {
- fmt.Printf("%s:\n\t- %q\n\t+ %q\n", posString(p.arg), p.str, out)
- }
-
- // find argument index of format argument
- index := -1
- for i, arg := range p.call.Args {
- if p.arg == arg {
- index = i
- break
- }
- }
- if index < 0 {
- // we may have processed the same call site twice,
- // but that shouldn't happen
- panic("internal error: matching argument not found")
- }
-
- // replace literal
- new := *lit // make a copy
- new.Value = strconv.Quote(out) // this may introduce "-quotes where there were `-quotes
- p.call.Args[index] = &new
- updatedFiles[p.file.name] = p.file
- }
- }
-
- // write dirty files back
- var filesUpdated bool
- if len(updatedFiles) > 0 && *rewrite {
- for _, file := range updatedFiles {
- var buf bytes.Buffer
- if err := format.Node(&buf, fset, file.ast); err != nil {
- t.Errorf("WARNING: gofmt %s failed: %v", file.name, err)
- continue
- }
- if err := ioutil.WriteFile(file.name, buf.Bytes(), 0x666); err != nil {
- t.Errorf("WARNING: writing %s failed: %v", file.name, err)
- continue
- }
- fmt.Printf("updated %s\n", file.name)
- filesUpdated = true
- }
- }
-
- // report the names of all functions called with a format string
- if len(callSites) > 0 && testing.Verbose() {
- set := make(map[string]bool)
- for _, p := range callSites {
- set[nodeString(p.call.Fun)] = true
- }
- var list []string
- for s := range set {
- list = append(list, s)
- }
- fmt.Println("\nFunctions called with a format string")
- writeList(os.Stdout, list)
- }
-
- // update formats
- if len(foundFormats) > 0 && *update {
- var list []string
- for s := range foundFormats {
- list = append(list, fmt.Sprintf("%q: \"\",", s))
- }
- var buf bytes.Buffer
- buf.WriteString(knownFormatsHeader)
- writeList(&buf, list)
- buf.WriteString("}\n")
- out, err := format.Source(buf.Bytes())
- const outfile = "fmtmap_test.go"
- if err != nil {
- t.Errorf("WARNING: gofmt %s failed: %v", outfile, err)
- out = buf.Bytes() // continue with unformatted source
- }
- if err = ioutil.WriteFile(outfile, out, 0644); err != nil {
- t.Errorf("WARNING: updating format map failed: %v", err)
- }
- }
-
- // check that knownFormats is up to date
- if !*rewrite && !*update {
- var mismatch bool
- for s := range foundFormats {
- if _, ok := knownFormats[s]; !ok {
- mismatch = true
- break
- }
- }
- if !mismatch {
- for s := range knownFormats {
- if _, ok := foundFormats[s]; !ok {
- mismatch = true
- break
- }
- }
- }
- if mismatch {
- t.Errorf("format map is out of date; run 'go test -u' to update and manually verify correctness of change'")
- }
- }
-
- // all format strings of calls must be in the formatStrings set (self-verification)
- for _, p := range callSites {
- if lit, ok := p.arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
- if formatStrings[lit] {
- // ok
- delete(formatStrings, lit)
- } else {
- // this should never happen
- panic(fmt.Sprintf("internal error: format string not found (%s)", posString(lit)))
- }
- }
- }
-
- // if we have any strings left, we may need to update them manually
- if len(formatStrings) > 0 && filesUpdated {
- var list []string
- for lit := range formatStrings {
- list = append(list, fmt.Sprintf("%s: %s", posString(lit), nodeString(lit)))
- }
- fmt.Println("\nWARNING: Potentially missed format strings")
- writeList(os.Stdout, list)
- t.Fail()
- }
-
- fmt.Println()
-}
-
-// A callSite describes a function call that appears to contain
-// a format string.
-type callSite struct {
- file File
- call *ast.CallExpr // call containing the format string
- arg ast.Expr // format argument (string literal or constant)
- str string // unquoted format string
- types []string // argument types
-}
-
-func collectPkgFormats(t *testing.T, pkg *build.Package) {
- // collect all files
- var filenames []string
- filenames = append(filenames, pkg.GoFiles...)
- filenames = append(filenames, pkg.CgoFiles...)
- filenames = append(filenames, pkg.TestGoFiles...)
-
- // TODO(gri) verify _test files outside package
- for _, name := range pkg.XTestGoFiles {
- // don't process this test itself
- if name != "fmt_test.go" && testing.Verbose() {
- fmt.Printf("WARNING: %s not processed\n", filepath.Join(pkg.Dir, name))
- }
- }
-
- // make filenames relative to .
- for i, name := range filenames {
- filenames[i] = filepath.Join(pkg.Dir, name)
- }
-
- // parse all files
- files := make([]*ast.File, len(filenames))
- for i, filename := range filenames {
- f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
- if err != nil {
- t.Fatal(err)
- }
- files[i] = f
- }
-
- // typecheck package
- conf := types.Config{Importer: importer.Default()}
- etypes := make(map[ast.Expr]types.TypeAndValue)
- if _, err := conf.Check(pkg.ImportPath, fset, files, &types.Info{Types: etypes}); err != nil {
- t.Fatal(err)
- }
-
- // collect all potential format strings (for extra verification later)
- for _, file := range files {
- ast.Inspect(file, func(n ast.Node) bool {
- if s, ok := stringLit(n); ok && isFormat(s) {
- formatStrings[n.(*ast.BasicLit)] = true
- }
- return true
- })
- }
-
- // collect all formats/arguments of calls with format strings
- for index, file := range files {
- ast.Inspect(file, func(n ast.Node) bool {
- if call, ok := n.(*ast.CallExpr); ok {
- if ignoredFunctions[nodeString(call.Fun)] {
- return true
- }
- // look for an arguments that might be a format string
- for i, arg := range call.Args {
- if s, ok := stringVal(etypes[arg]); ok && isFormat(s) {
- // make sure we have enough arguments
- n := numFormatArgs(s)
- if i+1+n > len(call.Args) {
- t.Errorf("%s: not enough format args (ignore %s?)", posString(call), nodeString(call.Fun))
- break // ignore this call
- }
- // assume last n arguments are to be formatted;
- // determine their types
- argTypes := make([]string, n)
- for i, arg := range call.Args[len(call.Args)-n:] {
- if tv, ok := etypes[arg]; ok {
- argTypes[i] = typeString(tv.Type)
- }
- }
- // collect call site
- if callSites[call] != nil {
- panic("internal error: file processed twice?")
- }
- callSites[call] = &callSite{
- file: File{filenames[index], file},
- call: call,
- arg: arg,
- str: s,
- types: argTypes,
- }
- break // at most one format per argument list
- }
- }
- }
- return true
- })
- }
-}
-
-// writeList writes list in sorted order to w.
-func writeList(w io.Writer, list []string) {
- sort.Strings(list)
- for _, s := range list {
- fmt.Fprintln(w, "\t", s)
- }
-}
-
-// posString returns a string representation of n's position
-// in the form filename:line:col: .
-func posString(n ast.Node) string {
- if n == nil {
- return ""
- }
- return fset.Position(n.Pos()).String()
-}
-
-// nodeString returns a string representation of n.
-func nodeString(n ast.Node) string {
- var buf bytes.Buffer
- if err := format.Node(&buf, fset, n); err != nil {
- log.Fatal(err) // should always succeed
- }
- return buf.String()
-}
-
-// typeString returns a string representation of n.
-func typeString(typ types.Type) string {
- s := filepath.ToSlash(typ.String())
-
- // Report all the concrete IR types as Node, to shorten fmtmap.
- const ir = "cmd/compile/internal/ir."
- if s == "*"+ir+"Name" || s == "*"+ir+"Func" || s == "*"+ir+"Decl" ||
- s == ir+"Ntype" || s == ir+"Expr" || s == ir+"Stmt" ||
- strings.HasPrefix(s, "*"+ir) && (strings.HasSuffix(s, "Expr") || strings.HasSuffix(s, "Stmt")) {
- return "cmd/compile/internal/ir.Node"
- }
-
- return s
-}
-
-// stringLit returns the unquoted string value and true if
-// n represents a string literal; otherwise it returns ""
-// and false.
-func stringLit(n ast.Node) (string, bool) {
- if lit, ok := n.(*ast.BasicLit); ok && lit.Kind == token.STRING {
- s, err := strconv.Unquote(lit.Value)
- if err != nil {
- log.Fatal(err) // should not happen with correct ASTs
- }
- return s, true
- }
- return "", false
-}
-
-// stringVal returns the (unquoted) string value and true if
-// tv is a string constant; otherwise it returns "" and false.
-func stringVal(tv types.TypeAndValue) (string, bool) {
- if tv.IsValue() && tv.Value != nil && tv.Value.Kind() == constant.String {
- return constant.StringVal(tv.Value), true
- }
- return "", false
-}
-
-// formatIter iterates through the string s in increasing
-// index order and calls f for each format specifier '%..v'.
-// The arguments for f describe the specifier's index range.
-// If a format specifier contains a "*", f is called with
-// the index range for "*" alone, before being called for
-// the entire specifier. The result of f is the index of
-// the rune at which iteration continues.
-func formatIter(s string, f func(i, j int) int) {
- i := 0 // index after current rune
- var r rune // current rune
-
- next := func() {
- r1, w := utf8.DecodeRuneInString(s[i:])
- if w == 0 {
- r1 = -1 // signal end-of-string
- }
- r = r1
- i += w
- }
-
- flags := func() {
- for r == ' ' || r == '#' || r == '+' || r == '-' || r == '0' {
- next()
- }
- }
-
- index := func() {
- if r == '[' {
- log.Fatalf("cannot handle indexed arguments: %s", s)
- }
- }
-
- digits := func() {
- index()
- if r == '*' {
- i = f(i-1, i)
- next()
- return
- }
- for '0' <= r && r <= '9' {
- next()
- }
- }
-
- for next(); r >= 0; next() {
- if r == '%' {
- i0 := i
- next()
- flags()
- digits()
- if r == '.' {
- next()
- digits()
- }
- index()
- // accept any letter (a-z, A-Z) as format verb;
- // ignore anything else
- if 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' {
- i = f(i0-1, i)
- }
- }
- }
-}
-
-// isFormat reports whether s contains format specifiers.
-func isFormat(s string) (yes bool) {
- formatIter(s, func(i, j int) int {
- yes = true
- return len(s) // stop iteration
- })
- return
-}
-
-// oneFormat reports whether s is exactly one format specifier.
-func oneFormat(s string) (yes bool) {
- formatIter(s, func(i, j int) int {
- yes = i == 0 && j == len(s)
- return j
- })
- return
-}
-
-// numFormatArgs returns the number of format specifiers in s.
-func numFormatArgs(s string) int {
- count := 0
- formatIter(s, func(i, j int) int {
- count++
- return j
- })
- return count
-}
-
-// formatReplace replaces the i'th format specifier s in the incoming
-// string in with the result of f(i, s) and returns the new string.
-func formatReplace(in string, f func(i int, s string) string) string {
- var buf []byte
- i0 := 0
- index := 0
- formatIter(in, func(i, j int) int {
- if sub := in[i:j]; sub != "*" { // ignore calls for "*" width/length specifiers
- buf = append(buf, in[i0:i]...)
- buf = append(buf, f(index, sub)...)
- i0 = j
- }
- index++
- return j
- })
- return string(append(buf, in[i0:]...))
-}
-
-// ignoredPackages is the set of packages which can
-// be ignored.
-var ignoredPackages = map[string]bool{}
-
-// ignoredFunctions is the set of functions which may have
-// format-like arguments but which don't do any formatting and
-// thus may be ignored.
-var ignoredFunctions = map[string]bool{}
-
-func init() {
- // verify that knownFormats entries are correctly formatted
- for key, val := range knownFormats {
- // key must be "typename format", and format starts with a '%'
- // (formats containing '*' alone are not collected in this map)
- i := strings.Index(key, "%")
- if i < 0 || !oneFormat(key[i:]) {
- log.Fatalf("incorrect knownFormats key: %q", key)
- }
- // val must be "format" or ""
- if val != "" && !oneFormat(val) {
- log.Fatalf("incorrect knownFormats value: %q (key = %q)", val, key)
- }
- }
-}
-
-const knownFormatsHeader = `// Copyright 2018 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 file implements the knownFormats map which records the valid
-// formats for a given type. The valid formats must correspond to
-// supported compiler formats implemented in fmt.go, or whatever
-// other format verbs are implemented for the given type. The map may
-// also be used to change the use of a format verb across all compiler
-// sources automatically (for instance, if the implementation of fmt.go
-// changes), by using the -r option together with the new formats in the
-// map. To generate this file automatically from the existing source,
-// run: go test -run Formats -u.
-//
-// See the package comment in fmt_test.go for additional information.
-
-package main_test
-
-// knownFormats entries are of the form "typename format" -> "newformat".
-// An absent entry means that the format is not recognized as valid.
-// An empty new format means that the format should remain unchanged.
-var knownFormats = map[string]string{
-`
diff --git a/src/cmd/compile/fmtmap_test.go b/src/cmd/compile/fmtmap_test.go
deleted file mode 100644
index 7663d1e23b..0000000000
--- a/src/cmd/compile/fmtmap_test.go
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2018 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 file implements the knownFormats map which records the valid
-// formats for a given type. The valid formats must correspond to
-// supported compiler formats implemented in fmt.go, or whatever
-// other format verbs are implemented for the given type. The map may
-// also be used to change the use of a format verb across all compiler
-// sources automatically (for instance, if the implementation of fmt.go
-// changes), by using the -r option together with the new formats in the
-// map. To generate this file automatically from the existing source,
-// run: go test -run Formats -u.
-//
-// See the package comment in fmt_test.go for additional information.
-
-package main_test
-
-// knownFormats entries are of the form "typename format" -> "newformat".
-// An absent entry means that the format is not recognized as valid.
-// An empty new format means that the format should remain unchanged.
-var knownFormats = map[string]string{
- "*bytes.Buffer %s": "",
- "*cmd/compile/internal/ssa.Block %s": "",
- "*cmd/compile/internal/ssa.Func %s": "",
- "*cmd/compile/internal/ssa.Register %s": "",
- "*cmd/compile/internal/ssa.Value %s": "",
- "*cmd/compile/internal/syntax.CallExpr %s": "",
- "*cmd/compile/internal/syntax.FuncLit %s": "",
- "*cmd/compile/internal/syntax.IndexExpr %s": "",
- "*cmd/compile/internal/types.Sym %+v": "",
- "*cmd/compile/internal/types.Sym %S": "",
- "*cmd/compile/internal/types.Type %+v": "",
- "*cmd/compile/internal/types.Type %-S": "",
- "*cmd/compile/internal/types.Type %L": "",
- "*cmd/compile/internal/types.Type %S": "",
- "*cmd/compile/internal/types.Type %s": "",
- "*cmd/compile/internal/types2.Basic %s": "",
- "*cmd/compile/internal/types2.Chan %s": "",
- "*cmd/compile/internal/types2.Func %s": "",
- "*cmd/compile/internal/types2.Initializer %s": "",
- "*cmd/compile/internal/types2.Interface %s": "",
- "*cmd/compile/internal/types2.MethodSet %s": "",
- "*cmd/compile/internal/types2.Named %s": "",
- "*cmd/compile/internal/types2.Package %s": "",
- "*cmd/compile/internal/types2.Selection %s": "",
- "*cmd/compile/internal/types2.Signature %s": "",
- "*cmd/compile/internal/types2.TypeName %s": "",
- "*cmd/compile/internal/types2.TypeParam %s": "",
- "*cmd/compile/internal/types2.Var %s": "",
- "*cmd/compile/internal/types2.operand %s": "",
- "*cmd/compile/internal/types2.substMap %s": "",
- "*math/big.Float %f": "",
- "*math/big.Int %s": "",
- "[]*cmd/compile/internal/types2.TypeName %s": "",
- "[]cmd/compile/internal/syntax.token %s": "",
- "[]cmd/compile/internal/types2.Type %s": "",
- "cmd/compile/internal/arm.shift %d": "",
- "cmd/compile/internal/gc.RegIndex %d": "",
- "cmd/compile/internal/ir.Class %d": "",
- "cmd/compile/internal/ir.Node %+v": "",
- "cmd/compile/internal/ir.Node %L": "",
- "cmd/compile/internal/ir.Nodes %+v": "",
- "cmd/compile/internal/ir.Nodes %.v": "",
- "cmd/compile/internal/ir.Op %+v": "",
- "cmd/compile/internal/ssa.Aux %#v": "",
- "cmd/compile/internal/ssa.Aux %q": "",
- "cmd/compile/internal/ssa.Aux %s": "",
- "cmd/compile/internal/ssa.BranchPrediction %d": "",
- "cmd/compile/internal/ssa.ID %d": "",
- "cmd/compile/internal/ssa.LocalSlot %s": "",
- "cmd/compile/internal/ssa.Location %s": "",
- "cmd/compile/internal/ssa.Op %s": "",
- "cmd/compile/internal/ssa.ValAndOff %s": "",
- "cmd/compile/internal/ssa.flagConstant %s": "",
- "cmd/compile/internal/ssa.rbrank %d": "",
- "cmd/compile/internal/ssa.regMask %d": "",
- "cmd/compile/internal/ssa.register %d": "",
- "cmd/compile/internal/ssa.relation %s": "",
- "cmd/compile/internal/syntax.ChanDir %d": "",
- "cmd/compile/internal/syntax.Error %q": "",
- "cmd/compile/internal/syntax.Expr %#v": "",
- "cmd/compile/internal/syntax.Expr %s": "",
- "cmd/compile/internal/syntax.LitKind %d": "",
- "cmd/compile/internal/syntax.Operator %s": "",
- "cmd/compile/internal/syntax.Pos %s": "",
- "cmd/compile/internal/syntax.position %s": "",
- "cmd/compile/internal/syntax.token %q": "",
- "cmd/compile/internal/syntax.token %s": "",
- "cmd/compile/internal/types.Kind %d": "",
- "cmd/compile/internal/types.Kind %s": "",
- "cmd/compile/internal/types2.Object %s": "",
- "cmd/compile/internal/types2.Type %s": "",
- "cmd/compile/internal/types2.color %s": "",
- "cmd/compile/internal/walk.initKind %d": "",
- "go/constant.Value %#v": "",
- "go/constant.Value %s": "",
- "map[*cmd/compile/internal/types2.TypeParam]cmd/compile/internal/types2.Type %s": "",
- "math/big.Accuracy %s": "",
- "reflect.Type %s": "",
- "time.Duration %d": "",
-}
diff --git a/src/cmd/compile/internal/base/debug.go b/src/cmd/compile/internal/base/debug.go
index 3acdcea846..164941bb26 100644
--- a/src/cmd/compile/internal/base/debug.go
+++ b/src/cmd/compile/internal/base/debug.go
@@ -32,7 +32,6 @@ type DebugFlags struct {
Append int `help:"print information about append compilation"`
Checkptr int `help:"instrument unsafe pointer conversions"`
Closure int `help:"print information about closure compilation"`
- CompileLater int `help:"compile functions as late as possible"`
DclStack int `help:"run internal dclstack check"`
Defer int `help:"print information about defer compilation"`
DisableNil int `help:"disable nil checks"`
diff --git a/src/cmd/compile/internal/escape/escape.go b/src/cmd/compile/internal/escape/escape.go
index 5df82d8cdc..bee3878f10 100644
--- a/src/cmd/compile/internal/escape/escape.go
+++ b/src/cmd/compile/internal/escape/escape.go
@@ -126,6 +126,11 @@ type location struct {
edges []edge // incoming edges
loopDepth int // loopDepth at declaration
+ // resultIndex records the tuple index (starting at 1) for
+ // PPARAMOUT variables within their function's result type.
+ // For non-PPARAMOUT variables it's 0.
+ resultIndex int
+
// derefs and walkgen are used during walkOne to track the
// minimal dereferences from the walk root.
derefs int // >= -1
@@ -259,11 +264,16 @@ func (b *batch) initFunc(fn *ir.Func) {
}
// Allocate locations for local variables.
- for _, dcl := range fn.Dcl {
- if dcl.Op() == ir.ONAME {
- e.newLoc(dcl, false)
+ for _, n := range fn.Dcl {
+ if n.Op() == ir.ONAME {
+ e.newLoc(n, false)
}
}
+
+ // Initialize resultIndex for result parameters.
+ for i, f := range fn.Type().Results().FieldSlice() {
+ e.oldLoc(f.Nname.(*ir.Name)).resultIndex = 1 + i
+ }
}
func (b *batch) walkFunc(fn *ir.Func) {
@@ -575,7 +585,7 @@ func (e *escape) exprSkipInit(k hole, n ir.Node) {
default:
base.Fatalf("unexpected expr: %v", n)
- case ir.OLITERAL, ir.ONIL, ir.OGETG, ir.OCLOSUREREAD, ir.OTYPE, ir.OMETHEXPR:
+ case ir.OLITERAL, ir.ONIL, ir.OGETG, ir.OTYPE, ir.OMETHEXPR:
// nop
case ir.ONAME:
@@ -583,6 +593,9 @@ func (e *escape) exprSkipInit(k hole, n ir.Node) {
if n.Class == ir.PFUNC || n.Class == ir.PEXTERN {
return
}
+ if n.IsClosureVar() && n.Defn == nil {
+ return // ".this" from method value wrapper
+ }
e.flow(k, e.oldLoc(n))
case ir.ONAMEOFFSET:
@@ -1606,8 +1619,7 @@ func (l *location) leakTo(sink *location, derefs int) {
// If sink is a result parameter and we can fit return bits
// into the escape analysis tag, then record a return leak.
if sink.isName(ir.PPARAMOUT) && sink.curfn == l.curfn {
- // TODO(mdempsky): Eliminate dependency on Vargen here.
- ri := int(sink.n.Name().Vargen) - 1
+ ri := sink.resultIndex - 1
if ri < numEscResults {
// Leak to result parameter.
l.paramEsc.AddResult(ri, derefs)
@@ -1923,7 +1935,7 @@ func mayAffectMemory(n ir.Node) bool {
// an ir.Any looking for any op that's not the ones in the case statement.
// But that produces changes in the compiled output detected by buildall.
switch n.Op() {
- case ir.ONAME, ir.OCLOSUREREAD, ir.OLITERAL, ir.ONIL:
+ case ir.ONAME, ir.OLITERAL, ir.ONIL:
return false
case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OMOD:
diff --git a/src/cmd/compile/internal/gc/compile.go b/src/cmd/compile/internal/gc/compile.go
index 25b1c76737..410b3e90ea 100644
--- a/src/cmd/compile/internal/gc/compile.go
+++ b/src/cmd/compile/internal/gc/compile.go
@@ -26,20 +26,20 @@ var (
compilequeue []*ir.Func // functions waiting to be compiled
)
-func funccompile(fn *ir.Func) {
+func enqueueFunc(fn *ir.Func) {
if ir.CurFunc != nil {
- base.Fatalf("funccompile %v inside %v", fn.Sym(), ir.CurFunc.Sym())
+ base.FatalfAt(fn.Pos(), "enqueueFunc %v inside %v", fn, ir.CurFunc)
}
- if fn.Type() == nil {
- if base.Errors() == 0 {
- base.Fatalf("funccompile missing type")
- }
+ if ir.FuncName(fn) == "_" {
+ // Skip compiling blank functions.
+ // Frontend already reported any spec-mandated errors (#29870).
return
}
- // assign parameter offsets
- types.CalcSize(fn.Type())
+ if clo := fn.OClosure; clo != nil && !ir.IsTrivialClosure(clo) {
+ return // we'll get this as part of its enclosing function
+ }
if len(fn.Body) == 0 {
// Initialize ABI wrappers if necessary.
@@ -48,35 +48,42 @@ func funccompile(fn *ir.Func) {
return
}
- typecheck.DeclContext = ir.PAUTO
- ir.CurFunc = fn
- compile(fn)
- ir.CurFunc = nil
- typecheck.DeclContext = ir.PEXTERN
+ errorsBefore := base.Errors()
+
+ todo := []*ir.Func{fn}
+ for len(todo) > 0 {
+ next := todo[len(todo)-1]
+ todo = todo[:len(todo)-1]
+
+ prepareFunc(next)
+ todo = append(todo, next.Closures...)
+ }
+
+ if base.Errors() > errorsBefore {
+ return
+ }
+
+ // Enqueue just fn itself. compileFunctions will handle
+ // scheduling compilation of its closures after it's done.
+ compilequeue = append(compilequeue, fn)
}
-func compile(fn *ir.Func) {
+// prepareFunc handles any remaining frontend compilation tasks that
+// aren't yet safe to perform concurrently.
+func prepareFunc(fn *ir.Func) {
// Set up the function's LSym early to avoid data races with the assemblers.
// Do this before walk, as walk needs the LSym to set attributes/relocations
// (e.g. in markTypeUsedInInterface).
ssagen.InitLSym(fn, true)
- errorsBefore := base.Errors()
- walk.Walk(fn)
- if base.Errors() > errorsBefore {
- return
- }
-
- // From this point, there should be no uses of Curfn. Enforce that.
- ir.CurFunc = nil
+ // Calculate parameter offsets.
+ types.CalcSize(fn.Type())
- if ir.FuncName(fn) == "_" {
- // We don't need to generate code for this function, just report errors in its body.
- // At this point we've generated any errors needed.
- // (Beyond here we generate only non-spec errors, like "stack frame too large".)
- // See issue 29870.
- return
- }
+ typecheck.DeclContext = ir.PAUTO
+ ir.CurFunc = fn
+ walk.Walk(fn)
+ ir.CurFunc = nil // enforce no further uses of CurFunc
+ typecheck.DeclContext = ir.PEXTERN
// Make sure type syms are declared for all types that might
// be types of stack objects. We need to do this here
@@ -95,84 +102,65 @@ func compile(fn *ir.Func) {
}
}
}
-
- if compilenow(fn) {
- ssagen.Compile(fn, 0)
- } else {
- compilequeue = append(compilequeue, fn)
- }
-}
-
-// compilenow reports whether to compile immediately.
-// If functions are not compiled immediately,
-// they are enqueued in compilequeue,
-// which is drained by compileFunctions.
-func compilenow(fn *ir.Func) bool {
- // Issue 38068: if this function is a method AND an inline
- // candidate AND was not inlined (yet), put it onto the compile
- // queue instead of compiling it immediately. This is in case we
- // wind up inlining it into a method wrapper that is generated by
- // compiling a function later on in the Target.Decls list.
- if ir.IsMethod(fn) && isInlinableButNotInlined(fn) {
- return false
- }
- return base.Flag.LowerC == 1 && base.Debug.CompileLater == 0
}
// compileFunctions compiles all functions in compilequeue.
// It fans out nBackendWorkers to do the work
// and waits for them to complete.
func compileFunctions() {
- if len(compilequeue) != 0 {
- types.CalcSizeDisabled = true // not safe to calculate sizes concurrently
- if race.Enabled {
- // Randomize compilation order to try to shake out races.
- tmp := make([]*ir.Func, len(compilequeue))
- perm := rand.Perm(len(compilequeue))
- for i, v := range perm {
- tmp[v] = compilequeue[i]
- }
- copy(compilequeue, tmp)
- } else {
- // Compile the longest functions first,
- // since they're most likely to be the slowest.
- // This helps avoid stragglers.
- sort.Slice(compilequeue, func(i, j int) bool {
- return len(compilequeue[i].Body) > len(compilequeue[j].Body)
- })
- }
- var wg sync.WaitGroup
- base.Ctxt.InParallel = true
- c := make(chan *ir.Func, base.Flag.LowerC)
- for i := 0; i < base.Flag.LowerC; i++ {
- wg.Add(1)
- go func(worker int) {
- for fn := range c {
- ssagen.Compile(fn, worker)
- }
- wg.Done()
- }(i)
- }
- for _, fn := range compilequeue {
- c <- fn
+ if len(compilequeue) == 0 {
+ return
+ }
+
+ if race.Enabled {
+ // Randomize compilation order to try to shake out races.
+ tmp := make([]*ir.Func, len(compilequeue))
+ perm := rand.Perm(len(compilequeue))
+ for i, v := range perm {
+ tmp[v] = compilequeue[i]
}
- close(c)
- compilequeue = nil
- wg.Wait()
- base.Ctxt.InParallel = false
- types.CalcSizeDisabled = false
+ copy(compilequeue, tmp)
+ } else {
+ // Compile the longest functions first,
+ // since they're most likely to be the slowest.
+ // This helps avoid stragglers.
+ sort.Slice(compilequeue, func(i, j int) bool {
+ return len(compilequeue[i].Body) > len(compilequeue[j].Body)
+ })
+ }
+
+ // We queue up a goroutine per function that needs to be
+ // compiled, but require them to grab an available worker ID
+ // before doing any substantial work to limit parallelism.
+ workerIDs := make(chan int, base.Flag.LowerC)
+ for i := 0; i < base.Flag.LowerC; i++ {
+ workerIDs <- i
}
-}
-// isInlinableButNotInlined returns true if 'fn' was marked as an
-// inline candidate but then never inlined (presumably because we
-// found no call sites).
-func isInlinableButNotInlined(fn *ir.Func) bool {
- if fn.Inl == nil {
- return false
+ var wg sync.WaitGroup
+ var asyncCompile func(*ir.Func)
+ asyncCompile = func(fn *ir.Func) {
+ wg.Add(1)
+ go func() {
+ worker := <-workerIDs
+ ssagen.Compile(fn, worker)
+ workerIDs <- worker
+
+ // Done compiling fn. Schedule it's closures for compilation.
+ for _, closure := range fn.Closures {
+ asyncCompile(closure)
+ }
+ wg.Done()
+ }()
}
- if fn.Sym() == nil {
- return true
+
+ types.CalcSizeDisabled = true // not safe to calculate sizes concurrently
+ base.Ctxt.InParallel = true
+ for _, fn := range compilequeue {
+ asyncCompile(fn)
}
- return !fn.Linksym().WasInlined()
+ compilequeue = nil
+ wg.Wait()
+ base.Ctxt.InParallel = false
+ types.CalcSizeDisabled = false
}
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index c3756309ea..9ecdd510b1 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -20,10 +20,8 @@ import (
"cmd/compile/internal/reflectdata"
"cmd/compile/internal/ssa"
"cmd/compile/internal/ssagen"
- "cmd/compile/internal/staticdata"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
- "cmd/compile/internal/walk"
"cmd/internal/dwarf"
"cmd/internal/obj"
"cmd/internal/objabi"
@@ -194,7 +192,6 @@ func Main(archInit func(*ssagen.ArchInfo)) {
typecheck.Target = new(ir.Package)
- typecheck.NeedFuncSym = staticdata.NeedFuncSym
typecheck.NeedITab = func(t, iface *types.Type) { reflectdata.ITabAddr(t, iface) }
typecheck.NeedRuntimeType = reflectdata.NeedRuntimeType // TODO(rsc): typenamesym for lock?
@@ -271,20 +268,6 @@ func Main(archInit func(*ssagen.ArchInfo)) {
ssagen.EnableNoWriteBarrierRecCheck()
}
- // Transform closure bodies to properly reference captured variables.
- // This needs to happen before walk, because closures must be transformed
- // before walk reaches a call of a closure.
- base.Timer.Start("fe", "xclosures")
- for _, n := range typecheck.Target.Decls {
- if n.Op() == ir.ODCLFUNC {
- n := n.(*ir.Func)
- if n.OClosure != nil {
- ir.CurFunc = n
- walk.Closure(n)
- }
- }
- }
-
// Prepare for SSA compilation.
// This must be before peekitabs, because peekitabs
// can trigger function compilation.
@@ -302,9 +285,8 @@ func Main(archInit func(*ssagen.ArchInfo)) {
base.Timer.Start("be", "compilefuncs")
fcount := int64(0)
for i := 0; i < len(typecheck.Target.Decls); i++ {
- n := typecheck.Target.Decls[i]
- if n.Op() == ir.ODCLFUNC {
- funccompile(n.(*ir.Func))
+ if fn, ok := typecheck.Target.Decls[i].(*ir.Func); ok {
+ enqueueFunc(fn)
fcount++
}
}
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index fbb2145e1b..753db80f76 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -131,9 +131,8 @@ func dumpdata() {
// It was not until issue 24761 that we found any code that required a loop at all.
for {
for i := numDecls; i < len(typecheck.Target.Decls); i++ {
- n := typecheck.Target.Decls[i]
- if n.Op() == ir.ODCLFUNC {
- funccompile(n.(*ir.Func))
+ if n, ok := typecheck.Target.Decls[i].(*ir.Func); ok {
+ enqueueFunc(n)
}
}
numDecls = len(typecheck.Target.Decls)
diff --git a/src/cmd/compile/internal/ir/expr.go b/src/cmd/compile/internal/ir/expr.go
index e7aa9c6a8f..51425db42d 100644
--- a/src/cmd/compile/internal/ir/expr.go
+++ b/src/cmd/compile/internal/ir/expr.go
@@ -203,19 +203,6 @@ func NewClosureExpr(pos src.XPos, fn *Func) *ClosureExpr {
return n
}
-// A ClosureRead denotes reading a variable stored within a closure struct.
-type ClosureReadExpr struct {
- miniExpr
- Offset int64
-}
-
-func NewClosureRead(typ *types.Type, offset int64) *ClosureReadExpr {
- n := &ClosureReadExpr{Offset: offset}
- n.typ = typ
- n.op = OCLOSUREREAD
- return n
-}
-
// A CompLitExpr is a composite literal Type{Vals}.
// Before type-checking, the type is Ntype.
type CompLitExpr struct {
@@ -727,7 +714,7 @@ func IsAddressable(n Node) bool {
return false
}
fallthrough
- case ODEREF, ODOTPTR, OCLOSUREREAD:
+ case ODEREF, ODOTPTR:
return true
case ODOT:
@@ -889,7 +876,7 @@ func SameSafeExpr(l Node, r Node) bool {
}
switch l.Op() {
- case ONAME, OCLOSUREREAD:
+ case ONAME:
return l == r
case ODOT, ODOTPTR:
diff --git a/src/cmd/compile/internal/ir/func.go b/src/cmd/compile/internal/ir/func.go
index 12ef083c19..30cddd298e 100644
--- a/src/cmd/compile/internal/ir/func.go
+++ b/src/cmd/compile/internal/ir/func.go
@@ -61,12 +61,16 @@ type Func struct {
// memory for escaping parameters.
Enter Nodes
Exit Nodes
+
// ONAME nodes for all params/locals for this func/closure, does NOT
// include closurevars until transformclosure runs.
+ // Names must be listed PPARAMs, PPARAMOUTs, then PAUTOs,
+ // with PPARAMs and PPARAMOUTs in order corresponding to the function signature.
+ // However, as anonymous or blank PPARAMs are not actually declared,
+ // they are omitted from Dcl.
+ // Anonymous and blank PPARAMOUTs are declared as ~rNN and ~bNN Names, respectively.
Dcl []*Name
- ClosureType Ntype // closure representation type
-
// ClosureVars lists the free variables that are used within a
// function literal, but formally declared in an enclosing
// function. The variables in this slice are the closure function's
@@ -75,6 +79,10 @@ type Func struct {
// Byval set if they're captured by value.
ClosureVars []*Name
+ // Enclosed functions that need to be compiled.
+ // Populated during walk.
+ Closures []*Func
+
// Parents records the parent scope of each scope within a
// function. The root scope (0) has no parent, so the i'th
// scope's parent is stored at Parents[i-1].
diff --git a/src/cmd/compile/internal/ir/name.go b/src/cmd/compile/internal/ir/name.go
index a51cf79929..514b303893 100644
--- a/src/cmd/compile/internal/ir/name.go
+++ b/src/cmd/compile/internal/ir/name.go
@@ -55,12 +55,6 @@ type Name struct {
// The function, method, or closure in which local variable or param is declared.
Curfn *Func
- // Unique number for ONAME nodes within a function. Function outputs
- // (results) are numbered starting at one, followed by function inputs
- // (parameters), and then local variables. Vargen is used to distinguish
- // local variables/params with the same name.
- Vargen int32
-
Ntype Ntype
Heapaddr *Name // temp holding heap address of param
@@ -264,7 +258,7 @@ const (
nameNeedzero // if it contains pointers, needs to be zeroed on function entry
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
+ nameIsClosureVar // PAUTOHEAP closure pseudo-variable; original (if any) at n.Defn
nameIsOutputParamHeapAddr // pointer to a result parameter's heap copy
nameAddrtaken // address taken, even if not moved to heap
nameInlFormal // PAUTO created by inliner, derived from callee formal
@@ -332,7 +326,7 @@ func (n *Name) SetVal(v constant.Value) {
// it appears in the function that immediately contains the
// declaration. Otherwise, Canonical simply returns n itself.
func (n *Name) Canonical() *Name {
- if n.IsClosureVar() {
+ if n.IsClosureVar() && n.Defn != nil {
n = n.Defn.(*Name)
}
return n
@@ -351,6 +345,82 @@ func (n *Name) Byval() bool {
return n.Canonical().flags&nameByval != 0
}
+// CaptureName returns a Name suitable for referring to n from within function
+// fn or from the package block if fn is nil. If n is a free variable declared
+// within a function that encloses fn, then CaptureName returns a closure
+// variable that refers to n and adds it to fn.ClosureVars. Otherwise, it simply
+// returns n.
+func CaptureName(pos src.XPos, fn *Func, n *Name) *Name {
+ if n.IsClosureVar() {
+ base.FatalfAt(pos, "misuse of CaptureName on closure variable: %v", n)
+ }
+ if n.Op() != ONAME || n.Curfn == nil || n.Curfn == fn {
+ return n // okay to use directly
+ }
+ if fn == nil {
+ base.FatalfAt(pos, "package-block reference to %v, declared in %v", n, n.Curfn)
+ }
+
+ c := n.Innermost
+ if c != nil && c.Curfn == fn {
+ return c
+ }
+
+ // Do not have a closure var for the active closure yet; make one.
+ c = NewNameAt(pos, n.Sym())
+ c.Curfn = fn
+ c.Class = PAUTOHEAP
+ c.SetIsClosureVar(true)
+ c.Defn = n
+
+ // Link into list of active closure variables.
+ // Popped from list in FinishCaptureNames.
+ c.Outer = n.Innermost
+ n.Innermost = c
+ fn.ClosureVars = append(fn.ClosureVars, c)
+
+ return c
+}
+
+// FinishCaptureNames handles any work leftover from calling CaptureName
+// earlier. outerfn should be the function that immediately encloses fn.
+func FinishCaptureNames(pos src.XPos, outerfn, fn *Func) {
+ // closure-specific variables are hanging off the
+ // ordinary ones; see CaptureName above.
+ // unhook them.
+ // make the list of pointers for the closure call.
+ for _, cv := range fn.ClosureVars {
+ // Unlink from n; see comment in syntax.go type Param for these fields.
+ n := cv.Defn.(*Name)
+ n.Innermost = cv.Outer
+
+ // If the closure usage of n is not dense, we need to make it
+ // dense by recapturing n within the enclosing function.
+ //
+ // That is, suppose we just finished parsing the innermost
+ // closure f4 in this code:
+ //
+ // func f() {
+ // n := 1
+ // func() { // f2
+ // use(n)
+ // func() { // f3
+ // func() { // f4
+ // use(n)
+ // }()
+ // }()
+ // }()
+ // }
+ //
+ // At this point cv.Outer is f2's n; there is no n for f3. To
+ // construct the closure f4 from within f3, we need to use f3's
+ // n and in this case we need to create f3's n with CaptureName.
+ //
+ // We'll decide later in walk whether to use v directly or &v.
+ cv.Outer = CaptureName(pos, outerfn, n)
+ }
+}
+
// SameSource reports whether two nodes refer to the same source
// element.
//
diff --git a/src/cmd/compile/internal/ir/node.go b/src/cmd/compile/internal/ir/node.go
index 850d7343aa..a2b6e7203b 100644
--- a/src/cmd/compile/internal/ir/node.go
+++ b/src/cmd/compile/internal/ir/node.go
@@ -294,20 +294,19 @@ const (
OTSLICE // []int
// misc
- OINLCALL // intermediary representation of an inlined call.
- OEFACE // itable and data words of an empty-interface value.
- OITAB // itable word of an interface value.
- OIDATA // data word of an interface value in Left
- OSPTR // base pointer of a slice or string.
- OCLOSUREREAD // read from inside closure struct at beginning of closure function
- OCFUNC // reference to c function pointer (not go func value)
- OCHECKNIL // emit code to ensure pointer/interface not nil
- OVARDEF // variable is about to be fully initialized
- OVARKILL // variable is dead
- OVARLIVE // variable is alive
- ORESULT // result of a function call; Xoffset is stack offset
- OINLMARK // start of an inlined body, with file/line of caller. Xoffset is an index into the inline tree.
- ONAMEOFFSET // offset within a name
+ OINLCALL // intermediary representation of an inlined call.
+ OEFACE // itable and data words of an empty-interface value.
+ OITAB // itable word of an interface value.
+ OIDATA // data word of an interface value in Left
+ OSPTR // base pointer of a slice or string.
+ OCFUNC // reference to c function pointer (not go func value)
+ OCHECKNIL // emit code to ensure pointer/interface not nil
+ OVARDEF // variable is about to be fully initialized
+ OVARKILL // variable is dead
+ OVARLIVE // variable is alive
+ ORESULT // result of a function call; Xoffset is stack offset
+ OINLMARK // start of an inlined body, with file/line of caller. Xoffset is an index into the inline tree.
+ ONAMEOFFSET // offset within a name
// arch-specific opcodes
ORETJMP // return to other function
diff --git a/src/cmd/compile/internal/ir/node_gen.go b/src/cmd/compile/internal/ir/node_gen.go
index 7f494b16cd..f1b0a21628 100644
--- a/src/cmd/compile/internal/ir/node_gen.go
+++ b/src/cmd/compile/internal/ir/node_gen.go
@@ -353,22 +353,6 @@ func (n *ClosureExpr) editChildren(edit func(Node) Node) {
}
}
-func (n *ClosureReadExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
-func (n *ClosureReadExpr) copy() Node {
- c := *n
- c.init = copyNodes(c.init)
- return &c
-}
-func (n *ClosureReadExpr) doChildren(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- return false
-}
-func (n *ClosureReadExpr) editChildren(edit func(Node) Node) {
- editNodes(n.init, edit)
-}
-
func (n *CommClause) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
func (n *CommClause) copy() Node {
c := *n
diff --git a/src/cmd/compile/internal/ir/op_string.go b/src/cmd/compile/internal/ir/op_string.go
index 0339444132..b54b4785a2 100644
--- a/src/cmd/compile/internal/ir/op_string.go
+++ b/src/cmd/compile/internal/ir/op_string.go
@@ -150,23 +150,22 @@ func _() {
_ = x[OITAB-139]
_ = x[OIDATA-140]
_ = x[OSPTR-141]
- _ = x[OCLOSUREREAD-142]
- _ = x[OCFUNC-143]
- _ = x[OCHECKNIL-144]
- _ = x[OVARDEF-145]
- _ = x[OVARKILL-146]
- _ = x[OVARLIVE-147]
- _ = x[ORESULT-148]
- _ = x[OINLMARK-149]
- _ = x[ONAMEOFFSET-150]
- _ = x[ORETJMP-151]
- _ = x[OGETG-152]
- _ = x[OEND-153]
+ _ = x[OCFUNC-142]
+ _ = x[OCHECKNIL-143]
+ _ = x[OVARDEF-144]
+ _ = x[OVARKILL-145]
+ _ = x[OVARLIVE-146]
+ _ = x[ORESULT-147]
+ _ = x[OINLMARK-148]
+ _ = x[ONAMEOFFSET-149]
+ _ = x[ORETJMP-150]
+ _ = x[OGETG-151]
+ _ = x[OEND-152]
}
-const _Op_name = "XXXNAMENONAMETYPEPACKLITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNEWOBJNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFMETHEXPRSTMTEXPRBLOCKBREAKCASECONTINUEDEFERFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYTSLICEINLCALLEFACEITABIDATASPTRCLOSUREREADCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKNAMEOFFSETRETJMPGETGEND"
+const _Op_name = "XXXNAMENONAMETYPEPACKLITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNEWOBJNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFMETHEXPRSTMTEXPRBLOCKBREAKCASECONTINUEDEFERFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYTSLICEINLCALLEFACEITABIDATASPTRCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKNAMEOFFSETRETJMPGETGEND"
-var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 37, 39, 42, 48, 52, 58, 64, 73, 85, 94, 103, 115, 124, 126, 129, 139, 146, 153, 160, 164, 168, 176, 184, 193, 201, 204, 209, 216, 223, 229, 238, 246, 254, 260, 264, 273, 280, 284, 287, 294, 302, 309, 315, 318, 324, 331, 339, 343, 350, 358, 360, 362, 364, 366, 368, 370, 375, 380, 388, 391, 400, 403, 407, 415, 422, 431, 444, 447, 450, 453, 456, 459, 462, 468, 471, 477, 480, 486, 490, 493, 497, 502, 507, 513, 518, 522, 527, 535, 543, 549, 558, 569, 576, 580, 587, 595, 599, 603, 607, 614, 621, 629, 635, 643, 651, 656, 661, 665, 673, 678, 682, 685, 693, 697, 699, 704, 706, 711, 717, 723, 729, 735, 740, 744, 751, 757, 762, 768, 774, 781, 786, 790, 795, 799, 810, 815, 823, 829, 836, 843, 849, 856, 866, 872, 876, 879}
+var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 37, 39, 42, 48, 52, 58, 64, 73, 85, 94, 103, 115, 124, 126, 129, 139, 146, 153, 160, 164, 168, 176, 184, 193, 201, 204, 209, 216, 223, 229, 238, 246, 254, 260, 264, 273, 280, 284, 287, 294, 302, 309, 315, 318, 324, 331, 339, 343, 350, 358, 360, 362, 364, 366, 368, 370, 375, 380, 388, 391, 400, 403, 407, 415, 422, 431, 444, 447, 450, 453, 456, 459, 462, 468, 471, 477, 480, 486, 490, 493, 497, 502, 507, 513, 518, 522, 527, 535, 543, 549, 558, 569, 576, 580, 587, 595, 599, 603, 607, 614, 621, 629, 635, 643, 651, 656, 661, 665, 673, 678, 682, 685, 693, 697, 699, 704, 706, 711, 717, 723, 729, 735, 740, 744, 751, 757, 762, 768, 774, 781, 786, 790, 795, 799, 804, 812, 818, 825, 832, 838, 845, 855, 861, 865, 868}
func (i Op) String() string {
if i >= Op(len(_Op_index)-1) {
diff --git a/src/cmd/compile/internal/ir/sizeof_test.go b/src/cmd/compile/internal/ir/sizeof_test.go
index 1a4d2e5c7a..553dc53760 100644
--- a/src/cmd/compile/internal/ir/sizeof_test.go
+++ b/src/cmd/compile/internal/ir/sizeof_test.go
@@ -20,8 +20,8 @@ func TestSizeof(t *testing.T) {
_32bit uintptr // size on 32bit platforms
_64bit uintptr // size on 64bit platforms
}{
- {Func{}, 184, 320},
- {Name{}, 120, 216},
+ {Func{}, 188, 328},
+ {Name{}, 116, 208},
}
for _, tt := range tests {
diff --git a/src/cmd/compile/internal/liveness/plive.go b/src/cmd/compile/internal/liveness/plive.go
index 26d90824b2..8d1754c813 100644
--- a/src/cmd/compile/internal/liveness/plive.go
+++ b/src/cmd/compile/internal/liveness/plive.go
@@ -1223,6 +1223,7 @@ func WriteFuncMap(fn *ir.Func) {
if ir.FuncName(fn) == "_" || fn.Sym().Linkname != "" {
return
}
+ types.CalcSize(fn.Type())
lsym := base.Ctxt.Lookup(fn.LSym.Name + ".args_stackmap")
nptr := int(fn.Type().ArgWidth() / int64(types.PtrSize))
bv := bitvec.New(int32(nptr) * 2)
diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go
index 5a9e37af7d..6a0ee831d7 100644
--- a/src/cmd/compile/internal/noder/noder.go
+++ b/src/cmd/compile/internal/noder/noder.go
@@ -1888,7 +1888,6 @@ func fakeRecv() *ir.Field {
func (p *noder) funcLit(expr *syntax.FuncLit) ir.Node {
xtype := p.typeExpr(expr.Type)
- ntype := p.typeExpr(expr.Type)
fn := ir.NewFunc(p.pos(expr))
fn.SetIsHiddenClosure(ir.CurFunc != nil)
@@ -1899,50 +1898,11 @@ func (p *noder) funcLit(expr *syntax.FuncLit) ir.Node {
fn.Nname.Defn = fn
clo := ir.NewClosureExpr(p.pos(expr), fn)
- fn.ClosureType = ntype
fn.OClosure = clo
p.funcBody(fn, expr.Body)
- // closure-specific variables are hanging off the
- // ordinary ones in the symbol table; see oldname.
- // unhook them.
- // make the list of pointers for the closure call.
- for _, v := range fn.ClosureVars {
- // Unlink from v1; see comment in syntax.go type Param for these fields.
- v1 := v.Defn
- v1.Name().Innermost = v.Outer
-
- // If the closure usage of v is not dense,
- // we need to make it dense; now that we're out
- // of the function in which v appeared,
- // look up v.Sym in the enclosing function
- // and keep it around for use in the compiled code.
- //
- // That is, suppose we just finished parsing the innermost
- // closure f4 in this code:
- //
- // func f() {
- // v := 1
- // func() { // f2
- // use(v)
- // func() { // f3
- // func() { // f4
- // use(v)
- // }()
- // }()
- // }()
- // }
- //
- // At this point v.Outer is f2's v; there is no f3's v.
- // To construct the closure f4 from within f3,
- // we need to use f3's v and in this case we need to create f3's v.
- // We are now in the context of f3, so calling oldname(v.Sym)
- // obtains f3's v, creating it if necessary (as it is in the example).
- //
- // capturevars will decide whether to use v directly or &v.
- v.Outer = oldname(v.Sym()).(*ir.Name)
- }
+ ir.FinishCaptureNames(base.Pos, ir.CurFunc, fn)
return clo
}
@@ -1976,32 +1936,12 @@ func oldname(s *types.Sym) ir.Node {
return ir.NewIdent(base.Pos, s)
}
- if ir.CurFunc != nil && n.Op() == ir.ONAME && n.Name().Curfn != nil && n.Name().Curfn != ir.CurFunc {
- // Inner func is referring to var in outer func.
- //
+ if n, ok := n.(*ir.Name); ok {
// TODO(rsc): If there is an outer variable x and we
// are parsing x := 5 inside the closure, until we get to
// the := it looks like a reference to the outer x so we'll
// make x a closure variable unnecessarily.
- n := n.(*ir.Name)
- c := n.Innermost
- if c == nil || c.Curfn != ir.CurFunc {
- // Do not have a closure var for the active closure yet; make one.
- c = typecheck.NewName(s)
- c.Class = ir.PAUTOHEAP
- c.SetIsClosureVar(true)
- c.Defn = n
-
- // Link into list of active closure variables.
- // Popped from list in func funcLit.
- c.Outer = n.Innermost
- n.Innermost = c
-
- ir.CurFunc.ClosureVars = append(ir.CurFunc.ClosureVars, c)
- }
-
- // return ref to closure var, not original
- return c
+ return ir.CaptureName(base.Pos, ir.CurFunc, n)
}
return n
diff --git a/src/cmd/compile/internal/reflectdata/alg.go b/src/cmd/compile/internal/reflectdata/alg.go
index d23ca6c7aa..d576053753 100644
--- a/src/cmd/compile/internal/reflectdata/alg.go
+++ b/src/cmd/compile/internal/reflectdata/alg.go
@@ -42,8 +42,8 @@ func eqCanPanic(t *types.Type) bool {
}
}
-// AlgType is like algtype1, except it returns the fixed-width AMEMxx variants
-// instead of the general AMEM kind when possible.
+// AlgType returns the fixed-width AMEMxx variants instead of the general
+// AMEM kind when possible.
func AlgType(t *types.Type) types.AlgKind {
a, _ := types.AlgType(t)
if a == types.AMEM {
diff --git a/src/cmd/compile/internal/ssagen/abi.go b/src/cmd/compile/internal/ssagen/abi.go
index 1c013dd2d8..dc27ec3a29 100644
--- a/src/cmd/compile/internal/ssagen/abi.go
+++ b/src/cmd/compile/internal/ssagen/abi.go
@@ -14,6 +14,7 @@ import (
"cmd/compile/internal/base"
"cmd/compile/internal/escape"
"cmd/compile/internal/ir"
+ "cmd/compile/internal/staticdata"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/obj"
@@ -137,6 +138,8 @@ func ReadSymABIs(file, myimportpath string) {
// For body-less functions, we only create the LSym; for functions
// with bodies call a helper to setup up / populate the LSym.
func InitLSym(f *ir.Func, hasBody bool) {
+ staticdata.NeedFuncSym(f.Sym())
+
// FIXME: for new-style ABI wrappers, we set up the lsym at the
// point the wrapper is created.
if f.LSym != nil && base.Flag.ABIWrap {
@@ -152,7 +155,7 @@ func InitLSym(f *ir.Func, hasBody bool) {
// makes calls to helpers to create ABI wrappers if needed.
func selectLSym(f *ir.Func, hasBody bool) {
if f.LSym != nil {
- base.Fatalf("Func.initLSym called twice")
+ base.FatalfAt(f.Pos(), "Func.initLSym called twice on %v", f)
}
if nam := f.Nname; !ir.IsBlank(nam) {
diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go
index 5998c42012..54bde20f1c 100644
--- a/src/cmd/compile/internal/ssagen/ssa.go
+++ b/src/cmd/compile/internal/ssagen/ssa.go
@@ -470,6 +470,47 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func {
}
}
+ // Populate closure variables.
+ if !fn.ClosureCalled() {
+ clo := s.entryNewValue0(ssa.OpGetClosurePtr, s.f.Config.Types.BytePtr)
+ offset := int64(types.PtrSize) // PtrSize to skip past function entry PC field
+ for _, n := range fn.ClosureVars {
+ typ := n.Type()
+ if !n.Byval() {
+ typ = types.NewPtr(typ)
+ }
+
+ offset = types.Rnd(offset, typ.Alignment())
+ r := s.newValue1I(ssa.OpOffPtr, types.NewPtr(typ), offset, clo)
+ offset += typ.Size()
+
+ if n.Byval() && TypeOK(n.Type()) {
+ // If it is a small variable captured by value, downgrade it to PAUTO.
+ r = s.load(n.Type(), r)
+
+ n.Class = ir.PAUTO
+ } else {
+ if !n.Byval() {
+ r = s.load(typ, r)
+ }
+
+ // Declare variable holding address taken from closure.
+ addr := ir.NewNameAt(fn.Pos(), &types.Sym{Name: "&" + n.Sym().Name, Pkg: types.LocalPkg})
+ addr.SetType(types.NewPtr(n.Type()))
+ addr.Class = ir.PAUTO
+ addr.SetUsed(true)
+ addr.Curfn = fn
+ types.CalcSize(addr.Type())
+
+ n.Heapaddr = addr
+ n = addr
+ }
+
+ fn.Dcl = append(fn.Dcl, n)
+ s.assign(n, r, false, 0)
+ }
+ }
+
// Convert the AST-based IR to the SSA-based IR
s.stmtList(fn.Enter)
s.stmtList(fn.Body)
@@ -2127,9 +2168,6 @@ func (s *state) expr(n ir.Node) *ssa.Value {
}
addr := s.addr(n)
return s.load(n.Type(), addr)
- case ir.OCLOSUREREAD:
- addr := s.addr(n)
- return s.load(n.Type(), addr)
case ir.ONIL:
n := n.(*ir.NilExpr)
t := n.Type()
@@ -3222,8 +3260,8 @@ func (s *state) assign(left ir.Node, right *ssa.Value, deref bool, skip skipMask
// If this assignment clobbers an entire local variable, then emit
// OpVarDef so liveness analysis knows the variable is redefined.
- if base := clobberBase(left); base.Op() == ir.ONAME && base.(*ir.Name).Class != ir.PEXTERN && skip == 0 {
- s.vars[memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, base.(*ir.Name), s.mem(), !ir.IsAutoTmp(base))
+ if base, ok := clobberBase(left).(*ir.Name); ok && base.Op() == ir.ONAME && base.Class != ir.PEXTERN && base.Class != ir.PAUTOHEAP && skip == 0 {
+ s.vars[memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, base, s.mem(), !ir.IsAutoTmp(base))
}
// Left is not ssa-able. Compute its address.
@@ -4986,6 +5024,8 @@ func (s *state) addr(n ir.Node) *ssa.Value {
// ensure that we reuse symbols for out parameters so
// that cse works on their addresses
return s.newValue2Apos(ssa.OpLocalAddr, t, n, s.sp, s.mem(), true)
+ case ir.PAUTOHEAP:
+ return s.expr(n.Heapaddr)
default:
s.Fatalf("variable address class %v not implemented", n.Class)
return nil
@@ -5031,10 +5071,6 @@ func (s *state) addr(n ir.Node) *ssa.Value {
n := n.(*ir.SelectorExpr)
p := s.exprPtr(n.X, n.Bounded(), n.Pos())
return s.newValue1I(ssa.OpOffPtr, t, n.Offset(), p)
- case ir.OCLOSUREREAD:
- n := n.(*ir.ClosureReadExpr)
- return s.newValue1I(ssa.OpOffPtr, t, n.Offset,
- s.entryNewValue0(ssa.OpGetClosurePtr, s.f.Config.Types.BytePtr))
case ir.OCONVNOP:
n := n.(*ir.ConvExpr)
if n.Type() == n.X.Type() {
@@ -5096,11 +5132,8 @@ func (s *state) canSSAName(name *ir.Name) bool {
if ir.IsParamHeapCopy(name) {
return false
}
- if name.Class == ir.PAUTOHEAP {
- s.Fatalf("canSSA of PAUTOHEAP %v", name)
- }
switch name.Class {
- case ir.PEXTERN:
+ case ir.PEXTERN, ir.PAUTOHEAP:
return false
case ir.PPARAMOUT:
if s.hasdefer {
diff --git a/src/cmd/compile/internal/staticdata/data.go b/src/cmd/compile/internal/staticdata/data.go
index 94fa6760a0..4b12590fde 100644
--- a/src/cmd/compile/internal/staticdata/data.go
+++ b/src/cmd/compile/internal/staticdata/data.go
@@ -265,7 +265,7 @@ func FuncLinksym(n *ir.Name) *obj.LSym {
return FuncSym(n.Sym()).Linksym()
}
-// NeedFuncSym ensures that s·f is exported.
+// NeedFuncSym ensures that s·f is exported, if needed.
// It is only used with -dynlink.
// When not compiling for dynamic linking,
// the funcsyms are created as needed by
@@ -275,8 +275,13 @@ func FuncLinksym(n *ir.Name) *obj.LSym {
// So instead, when dynamic linking, we only create
// the s·f stubs in s's package.
func NeedFuncSym(s *types.Sym) {
+ if base.Ctxt.InParallel {
+ // The append below probably just needs to lock
+ // funcsymsmu, like in FuncSym.
+ base.Fatalf("NeedFuncSym must be called in serial")
+ }
if !base.Ctxt.Flag_dynlink {
- base.Fatalf("makefuncsym dynlink")
+ return
}
if s.IsBlank() {
return
@@ -287,9 +292,7 @@ func NeedFuncSym(s *types.Sym) {
// get funcsyms.
return
}
- if _, existed := s.Pkg.LookupOK(ir.FuncSymName(s)); !existed {
- funcsyms = append(funcsyms, s)
- }
+ funcsyms = append(funcsyms, s)
}
func WriteFuncSyms() {
diff --git a/src/cmd/compile/internal/typecheck/dcl.go b/src/cmd/compile/internal/typecheck/dcl.go
index ffbf474a58..c7d7506fd1 100644
--- a/src/cmd/compile/internal/typecheck/dcl.go
+++ b/src/cmd/compile/internal/typecheck/dcl.go
@@ -7,7 +7,6 @@ package typecheck
import (
"fmt"
"strconv"
- "strings"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
@@ -47,7 +46,6 @@ func Declare(n *ir.Name, ctxt ir.Class) {
base.ErrorfAt(n.Pos(), "cannot declare name %v", s)
}
- gen := 0
if ctxt == ir.PEXTERN {
if s.Name == "init" {
base.ErrorfAt(n.Pos(), "cannot declare init - must be func")
@@ -64,13 +62,6 @@ func Declare(n *ir.Name, ctxt ir.Class) {
if ir.CurFunc != nil && ctxt != ir.PFUNC && n.Op() == ir.ONAME {
ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
}
- if n.Op() == ir.OTYPE {
- declare_typegen++
- gen = declare_typegen
- } else if n.Op() == ir.ONAME && ctxt == ir.PAUTO && !strings.Contains(s.Name, "·") {
- vargen++
- gen = vargen
- }
types.Pushdcl(s)
n.Curfn = ir.CurFunc
}
@@ -90,7 +81,6 @@ func Declare(n *ir.Name, ctxt ir.Class) {
s.Block = types.Block
s.Lastlineno = base.Pos
s.Def = n
- n.Vargen = int32(gen)
n.Class = ctxt
if ctxt == ir.PFUNC {
n.Sym().SetFunc(true)
@@ -314,10 +304,6 @@ func checkembeddedtype(t *types.Type) {
}
}
-// declare individual names - var, typ, const
-
-var declare_typegen int
-
func fakeRecvField() *types.Field {
return types.NewField(src.NoXPos, nil, types.FakeRecvType())
}
@@ -338,9 +324,6 @@ func funcarg(n *ir.Field, ctxt ir.Class) {
n.Decl = name
name.Ntype = n.Ntype
Declare(name, ctxt)
-
- vargen++
- n.Decl.Vargen = int32(vargen)
}
func funcarg2(f *types.Field, ctxt ir.Class) {
@@ -358,15 +341,6 @@ func funcargs(nt *ir.FuncType) {
base.Fatalf("funcargs %v", nt.Op())
}
- // re-start the variable generation number
- // we want to use small numbers for the return variables,
- // so let them have the chunk starting at 1.
- //
- // TODO(mdempsky): This is ugly, and only necessary because
- // esc.go uses Vargen to figure out result parameters' index
- // within the result tuple.
- vargen = len(nt.Results)
-
// declare the receiver and in arguments.
if nt.Recv != nil {
funcarg(nt.Recv, ir.PPARAM)
@@ -375,9 +349,6 @@ func funcargs(nt *ir.FuncType) {
funcarg(n, ir.PPARAM)
}
- oldvargen := vargen
- vargen = 0
-
// declare the out arguments.
gen := len(nt.Params)
for _, n := range nt.Results {
@@ -399,8 +370,6 @@ func funcargs(nt *ir.FuncType) {
funcarg(n, ir.PPARAMOUT)
}
-
- vargen = oldvargen
}
// Same as funcargs, except run over an already constructed TFUNC.
@@ -422,8 +391,6 @@ func funcargs2(t *types.Type) {
}
}
-var vargen int
-
func Temp(t *types.Type) *ir.Name {
return TempAt(base.Pos, ir.CurFunc, t)
}
diff --git a/src/cmd/compile/internal/typecheck/func.go b/src/cmd/compile/internal/typecheck/func.go
index 8789395ffb..03a10f594a 100644
--- a/src/cmd/compile/internal/typecheck/func.go
+++ b/src/cmd/compile/internal/typecheck/func.go
@@ -246,29 +246,26 @@ func MethodValueWrapper(dot *ir.SelectorExpr) *ir.Func {
fn.SetWrapper(true)
// Declare and initialize variable holding receiver.
- cr := ir.NewClosureRead(rcvrtype, types.Rnd(int64(types.PtrSize), int64(rcvrtype.Align)))
- var ptr *ir.Name
- var body []ir.Node
- if rcvrtype.IsPtr() || rcvrtype.IsInterface() {
- ptr = Temp(rcvrtype)
- body = append(body, ir.NewAssignStmt(base.Pos, ptr, cr))
- } else {
- ptr = Temp(types.NewPtr(rcvrtype))
- body = append(body, ir.NewAssignStmt(base.Pos, ptr, NodAddr(cr)))
- }
+ ptr := ir.NewNameAt(base.Pos, Lookup(".this"))
+ ptr.Class = ir.PAUTOHEAP
+ ptr.SetType(rcvrtype)
+ ptr.Curfn = fn
+ ptr.SetIsClosureVar(true)
+ ptr.SetByval(true)
+ fn.ClosureVars = append(fn.ClosureVars, ptr)
call := ir.NewCallExpr(base.Pos, ir.OCALL, ir.NewSelectorExpr(base.Pos, ir.OXDOT, ptr, meth), nil)
call.Args = ir.ParamNames(tfn.Type())
call.IsDDD = tfn.Type().IsVariadic()
+
+ var body ir.Node = call
if t0.NumResults() != 0 {
ret := ir.NewReturnStmt(base.Pos, nil)
ret.Results = []ir.Node{call}
- body = append(body, ret)
- } else {
- body = append(body, call)
+ body = ret
}
- fn.Body = body
+ fn.Body = []ir.Node{body}
FinishFuncBody()
Func(fn)
@@ -296,20 +293,20 @@ func tcClosure(clo *ir.ClosureExpr, top int) {
fn.Iota = x
}
- fn.ClosureType = typecheckNtype(fn.ClosureType)
- clo.SetType(fn.ClosureType.Type())
fn.SetClosureCalled(top&ctxCallee != 0)
// Do not typecheck fn twice, otherwise, we will end up pushing
// fn to Target.Decls multiple times, causing initLSym called twice.
// See #30709
if fn.Typecheck() == 1 {
+ clo.SetType(fn.Type())
return
}
fn.Nname.SetSym(closurename(ir.CurFunc))
ir.MarkFunc(fn.Nname)
Func(fn)
+ clo.SetType(fn.Type())
// Type check the body now, but only if we're inside a function.
// At top level (in a variable initialization: curfn==nil) we're not
@@ -367,10 +364,6 @@ func tcFunc(n *ir.Func) {
n.Nname.SetSym(ir.MethodSym(rcvr.Type, n.Shortname))
Declare(n.Nname, ir.PFUNC)
}
-
- if base.Ctxt.Flag_dynlink && !inimport && n.Nname != nil {
- NeedFuncSym(n.Sym())
- }
}
// tcCall typechecks an OCALL node.
diff --git a/src/cmd/compile/internal/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go
index a7927c39a3..4d48b80346 100644
--- a/src/cmd/compile/internal/typecheck/iexport.go
+++ b/src/cmd/compile/internal/typecheck/iexport.go
@@ -422,6 +422,10 @@ type exportWriter struct {
prevFile string
prevLine int64
prevColumn int64
+
+ // dclIndex maps function-scoped declarations to their index
+ // within their respective Func's Dcl list.
+ dclIndex map[*ir.Name]int
}
func (p *iexporter) doDecl(n *ir.Name) {
@@ -529,7 +533,8 @@ func (p *iexporter) doInline(f *ir.Name) {
w := p.newWriter()
w.setPkg(fnpkg(f), false)
- w.stmtList(ir.Nodes(f.Func.Inl.Body))
+ w.dclIndex = make(map[*ir.Name]int, len(f.Func.Inl.Dcl))
+ w.funcBody(f.Func)
w.finish("inl", p.inlineIndex, f.Sym())
}
@@ -756,7 +761,7 @@ func (w *exportWriter) paramList(fs []*types.Field) {
func (w *exportWriter) param(f *types.Field) {
w.pos(f.Pos)
- w.localIdent(types.OrigSym(f.Sym), 0)
+ w.localIdent(types.OrigSym(f.Sym))
w.typ(f.Type)
}
@@ -1030,7 +1035,19 @@ func (w *exportWriter) typeExt(t *types.Type) {
// Inline bodies.
-func (w *exportWriter) stmtList(list ir.Nodes) {
+func (w *exportWriter) funcBody(fn *ir.Func) {
+ w.int64(int64(len(fn.Inl.Dcl)))
+ for i, n := range fn.Inl.Dcl {
+ w.pos(n.Pos())
+ w.localIdent(n.Sym())
+ w.typ(n.Type())
+ w.dclIndex[n] = i
+ }
+
+ w.stmtList(fn.Inl.Body)
+}
+
+func (w *exportWriter) stmtList(list []ir.Node) {
for _, n := range list {
w.node(n)
}
@@ -1070,10 +1087,11 @@ func (w *exportWriter) stmt(n ir.Node) {
case ir.ODCL:
n := n.(*ir.Decl)
+ if ir.IsBlank(n.X) {
+ return // blank declarations not useful to importers
+ }
w.op(ir.ODCL)
- w.pos(n.X.Pos())
w.localName(n.X)
- w.typ(n.X.Type())
case ir.OAS:
// Don't export "v = <N>" initializing statements, hope they're always
@@ -1288,7 +1306,7 @@ func (w *exportWriter) expr(n ir.Node) {
}
s = n.Tag.Sym()
}
- w.localIdent(s, 0) // declared pseudo-variable, if any
+ w.localIdent(s) // declared pseudo-variable, if any
w.expr(n.X)
// case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
@@ -1518,22 +1536,19 @@ func (w *exportWriter) fieldList(list ir.Nodes) {
}
func (w *exportWriter) localName(n *ir.Name) {
- // Escape analysis happens after inline bodies are saved, but
- // we're using the same ONAME nodes, so we might still see
- // PAUTOHEAP here.
- //
- // Check for Stackcopy to identify PAUTOHEAP that came from
- // PPARAM/PPARAMOUT, because we only want to include vargen in
- // non-param names.
- var v int32
- if n.Class == ir.PAUTO || (n.Class == ir.PAUTOHEAP && n.Stackcopy == nil) {
- v = n.Vargen
+ if ir.IsBlank(n) {
+ w.int64(-1)
+ return
}
- w.localIdent(n.Sym(), v)
+ i, ok := w.dclIndex[n]
+ if !ok {
+ base.FatalfAt(n.Pos(), "missing from dclIndex: %+v", n)
+ }
+ w.int64(int64(i))
}
-func (w *exportWriter) localIdent(s *types.Sym, v int32) {
+func (w *exportWriter) localIdent(s *types.Sym) {
if w.currPkg == nil {
base.Fatalf("missing currPkg")
}
@@ -1555,13 +1570,6 @@ func (w *exportWriter) localIdent(s *types.Sym, v int32) {
base.Fatalf("unexpected dot in identifier: %v", name)
}
- if v > 0 {
- if strings.Contains(name, "·") {
- base.Fatalf("exporter: unexpected · in symbol name")
- }
- name = fmt.Sprintf("%s·%d", name, v)
- }
-
if s.Pkg != w.currPkg {
base.Fatalf("weird package in name: %v => %v from %q, not %q", s, name, s.Pkg.Path, w.currPkg.Path)
}
diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go
index 15c57b2380..c9effabce0 100644
--- a/src/cmd/compile/internal/typecheck/iimport.go
+++ b/src/cmd/compile/internal/typecheck/iimport.go
@@ -262,6 +262,9 @@ type importReader struct {
prevBase *src.PosBase
prevLine int64
prevColumn int64
+
+ // curfn is the current function we're importing into.
+ curfn *ir.Func
}
func (p *iimporter) newReader(off uint64, pkg *types.Pkg) *importReader {
@@ -715,19 +718,7 @@ func (r *importReader) doInline(fn *ir.Func) {
base.Fatalf("%v already has inline body", fn)
}
- StartFuncBody(fn)
- body := r.stmtList()
- FinishFuncBody()
- if body == nil {
- //
- // Make sure empty body is not interpreted as
- // no inlineable body (see also parser.fnbody)
- // (not doing so can cause significant performance
- // degradation due to unnecessary calls to empty
- // functions).
- body = []ir.Node{}
- }
- fn.Inl.Body = body
+ r.funcBody(fn)
importlist = append(importlist, fn)
@@ -755,6 +746,68 @@ func (r *importReader) doInline(fn *ir.Func) {
// unrefined nodes (since this is what the importer uses). The respective case
// entries are unreachable in the importer.
+func (r *importReader) funcBody(fn *ir.Func) {
+ outerfn := r.curfn
+ r.curfn = fn
+
+ // Import local declarations.
+ dcls := make([]*ir.Name, r.int64())
+ for i := range dcls {
+ n := ir.NewDeclNameAt(r.pos(), ir.ONAME, r.localIdent())
+ n.Class = ir.PAUTO // overwritten below for parameters/results
+ n.Curfn = fn
+ n.SetType(r.typ())
+ dcls[i] = n
+ }
+ fn.Inl.Dcl = dcls
+
+ // Fixup parameter classes and associate with their
+ // signature's type fields.
+ i := 0
+ fix := func(f *types.Field, class ir.Class) {
+ if class == ir.PPARAM && (f.Sym == nil || f.Sym.Name == "_") {
+ return
+ }
+ n := dcls[i]
+ n.Class = class
+ f.Nname = n
+ i++
+ }
+
+ typ := fn.Type()
+ if recv := typ.Recv(); recv != nil {
+ fix(recv, ir.PPARAM)
+ }
+ for _, f := range typ.Params().FieldSlice() {
+ fix(f, ir.PPARAM)
+ }
+ for _, f := range typ.Results().FieldSlice() {
+ fix(f, ir.PPARAMOUT)
+ }
+
+ // Import function body.
+ body := r.stmtList()
+ if body == nil {
+ // Make sure empty body is not interpreted as
+ // no inlineable body (see also parser.fnbody)
+ // (not doing so can cause significant performance
+ // degradation due to unnecessary calls to empty
+ // functions).
+ body = []ir.Node{}
+ }
+ fn.Inl.Body = body
+
+ r.curfn = outerfn
+}
+
+func (r *importReader) localName() *ir.Name {
+ i := r.int64()
+ if i < 0 {
+ return ir.BlankNode.(*ir.Name)
+ }
+ return r.curfn.Inl.Dcl[i]
+}
+
func (r *importReader) stmtList() []ir.Node {
var list []ir.Node
for {
@@ -784,13 +837,8 @@ func (r *importReader) caseList(switchExpr ir.Node) []*ir.CaseClause {
cas := ir.NewCaseStmt(r.pos(), nil, nil)
cas.List = r.stmtList()
if namedTypeSwitch {
- // Note: per-case variables will have distinct, dotted
- // names after import. That's okay: swt.go only needs
- // Sym for diagnostics anyway.
- caseVar := ir.NewNameAt(cas.Pos(), r.localIdent())
- Declare(caseVar, DeclContext)
- cas.Var = caseVar
- caseVar.Defn = switchExpr
+ cas.Var = r.localName()
+ cas.Var.Defn = switchExpr
}
cas.Body = r.stmtList()
cases[i] = cas
@@ -854,7 +902,7 @@ func (r *importReader) node() ir.Node {
return r.qualifiedIdent()
case ir.ONAME:
- return r.localIdent().Def.(*ir.Name)
+ return r.localName()
// case OPACK, ONONAME:
// unreachable - should have been resolved by typechecking
@@ -991,16 +1039,11 @@ func (r *importReader) node() ir.Node {
// --------------------------------------------------------------------
// statements
case ir.ODCL:
- pos := r.pos()
- lhs := ir.NewDeclNameAt(pos, ir.ONAME, r.localIdent())
- lhs.SetType(r.typ())
-
- Declare(lhs, ir.PAUTO)
-
var stmts ir.Nodes
- stmts.Append(ir.NewDecl(base.Pos, ir.ODCL, lhs))
- stmts.Append(ir.NewAssignStmt(base.Pos, lhs, nil))
- return ir.NewBlockStmt(pos, stmts)
+ n := r.localName()
+ stmts.Append(ir.NewDecl(n.Pos(), ir.ODCL, n))
+ stmts.Append(ir.NewAssignStmt(n.Pos(), n, nil))
+ return ir.NewBlockStmt(n.Pos(), stmts)
// case OAS, OASWB:
// unreachable - mapped to OAS case below by exporter
diff --git a/src/cmd/compile/internal/typecheck/typecheck.go b/src/cmd/compile/internal/typecheck/typecheck.go
index 07bbd25105..814af59772 100644
--- a/src/cmd/compile/internal/typecheck/typecheck.go
+++ b/src/cmd/compile/internal/typecheck/typecheck.go
@@ -24,7 +24,6 @@ var inimport bool // set during import
var TypecheckAllowed bool
var (
- NeedFuncSym = func(*types.Sym) {}
NeedITab = func(t, itype *types.Type) {}
NeedRuntimeType = func(*types.Type) {}
)
@@ -789,9 +788,6 @@ func typecheck1(n ir.Node, top int) ir.Node {
n := n.(*ir.UnaryExpr)
return tcSPtr(n)
- case ir.OCLOSUREREAD:
- return n
-
case ir.OCFUNC:
n := n.(*ir.UnaryExpr)
n.X = Expr(n.X)
@@ -1143,12 +1139,6 @@ func typecheckMethodExpr(n *ir.SelectorExpr) (res ir.Node) {
n.SetOp(ir.OMETHEXPR)
n.Selection = m
n.SetType(NewMethodType(m.Type, n.X.Type()))
-
- // Issue 25065. Make sure that we emit the symbol for a local method.
- if base.Ctxt.Flag_dynlink && !inimport && (t.Sym() == nil || t.Sym().Pkg == types.LocalPkg) {
- NeedFuncSym(n.FuncName().Sym())
- }
-
return n
}
@@ -1684,13 +1674,22 @@ func CheckMapKeys() {
mapqueue = nil
}
+// typegen tracks the number of function-scoped defined types that
+// have been declared. It's used to generate unique linker symbols for
+// their runtime type descriptors.
+var typegen int32
+
func typecheckdeftype(n *ir.Name) {
if base.EnableTrace && base.Flag.LowerT {
defer tracePrint("typecheckdeftype", n)(nil)
}
t := types.NewNamed(n)
- t.Vargen = n.Vargen
+ if n.Curfn != nil {
+ typegen++
+ t.Vargen = typegen
+ }
+
if n.Pragma()&ir.NotInHeap != 0 {
t.SetNotInHeap(true)
}
diff --git a/src/cmd/compile/internal/types/alg.go b/src/cmd/compile/internal/types/alg.go
index f1a472cca5..6091ee249c 100644
--- a/src/cmd/compile/internal/types/alg.go
+++ b/src/cmd/compile/internal/types/alg.go
@@ -132,7 +132,7 @@ func AlgType(t *Type) (AlgKind, *Type) {
return ret, nil
}
- base.Fatalf("algtype1: unexpected type %v", t)
+ base.Fatalf("algtype: unexpected type %v", t)
return 0, nil
}
diff --git a/src/cmd/compile/internal/walk/closure.go b/src/cmd/compile/internal/walk/closure.go
index 449df88f9e..694aa99940 100644
--- a/src/cmd/compile/internal/walk/closure.go
+++ b/src/cmd/compile/internal/walk/closure.go
@@ -12,126 +12,108 @@ import (
"cmd/internal/src"
)
-// Closure is called in a separate phase after escape analysis.
-// It transform closure bodies to properly reference captured variables.
-func Closure(fn *ir.Func) {
- lno := base.Pos
- base.Pos = fn.Pos()
-
- if fn.ClosureCalled() {
- // If the closure is directly called, we transform it to a plain function call
- // with variables passed as args. This avoids allocation of a closure object.
- // Here we do only a part of the transformation. Walk of OCALLFUNC(OCLOSURE)
- // will complete the transformation later.
- // For illustration, the following closure:
- // func(a int) {
- // println(byval)
- // byref++
- // }(42)
- // becomes:
- // func(byval int, &byref *int, a int) {
- // println(byval)
- // (*&byref)++
- // }(byval, &byref, 42)
-
- // f is ONAME of the actual function.
- f := fn.Nname
-
- // We are going to insert captured variables before input args.
- var params []*types.Field
- var decls []*ir.Name
- for _, v := range fn.ClosureVars {
- if !v.Byval() {
- // If v of type T is captured by reference,
- // we introduce function param &v *T
- // and v remains PAUTOHEAP with &v heapaddr
- // (accesses will implicitly deref &v).
- addr := typecheck.NewName(typecheck.Lookup("&" + v.Sym().Name))
- addr.SetType(types.NewPtr(v.Type()))
- v.Heapaddr = addr
- v = addr
- }
-
- v.Class = ir.PPARAM
- decls = append(decls, v)
-
- fld := types.NewField(src.NoXPos, v.Sym(), v.Type())
- fld.Nname = v
- params = append(params, fld)
- }
+// directClosureCall rewrites a direct call of a function literal into
+// a normal function call with closure variables passed as arguments.
+// This avoids allocation of a closure object.
+//
+// For illustration, the following call:
+//
+// func(a int) {
+// println(byval)
+// byref++
+// }(42)
+//
+// becomes:
+//
+// func(byval int, &byref *int, a int) {
+// println(byval)
+// (*&byref)++
+// }(byval, &byref, 42)
+func directClosureCall(n *ir.CallExpr) {
+ clo := n.X.(*ir.ClosureExpr)
+ clofn := clo.Func
- if len(params) > 0 {
- // Prepend params and decls.
- f.Type().Params().SetFields(append(params, f.Type().Params().FieldSlice()...))
- fn.Dcl = append(decls, fn.Dcl...)
- }
+ if ir.IsTrivialClosure(clo) {
+ return // leave for walkClosure to handle
+ }
- types.CalcSize(f.Type())
- fn.Nname.SetType(f.Type()) // update type of ODCLFUNC
- } else {
- // The closure is not called, so it is going to stay as closure.
- var body []ir.Node
- offset := int64(types.PtrSize)
- for _, v := range fn.ClosureVars {
- // cv refers to the field inside of closure OSTRUCTLIT.
- typ := v.Type()
- if !v.Byval() {
- typ = types.NewPtr(typ)
- }
- offset = types.Rnd(offset, int64(typ.Align))
- cr := ir.NewClosureRead(typ, offset)
- offset += typ.Width
-
- if v.Byval() && v.Type().Width <= int64(2*types.PtrSize) {
- // If it is a small variable captured by value, downgrade it to PAUTO.
- v.Class = ir.PAUTO
- fn.Dcl = append(fn.Dcl, v)
- body = append(body, ir.NewAssignStmt(base.Pos, v, cr))
- } else {
- // Declare variable holding addresses taken from closure
- // and initialize in entry prologue.
- addr := typecheck.NewName(typecheck.Lookup("&" + v.Sym().Name))
- addr.SetType(types.NewPtr(v.Type()))
- addr.Class = ir.PAUTO
- addr.SetUsed(true)
- addr.Curfn = fn
- fn.Dcl = append(fn.Dcl, addr)
- v.Heapaddr = addr
- var src ir.Node = cr
- if v.Byval() {
- src = typecheck.NodAddr(cr)
- }
- body = append(body, ir.NewAssignStmt(base.Pos, addr, src))
- }
+ // We are going to insert captured variables before input args.
+ var params []*types.Field
+ var decls []*ir.Name
+ for _, v := range clofn.ClosureVars {
+ if !v.Byval() {
+ // If v of type T is captured by reference,
+ // we introduce function param &v *T
+ // and v remains PAUTOHEAP with &v heapaddr
+ // (accesses will implicitly deref &v).
+
+ addr := ir.NewNameAt(clofn.Pos(), typecheck.Lookup("&"+v.Sym().Name))
+ addr.Curfn = clofn
+ addr.SetType(types.NewPtr(v.Type()))
+ v.Heapaddr = addr
+ v = addr
}
- if len(body) > 0 {
- typecheck.Stmts(body)
- fn.Enter = body
- fn.SetNeedctxt(true)
- }
+ v.Class = ir.PPARAM
+ decls = append(decls, v)
+
+ fld := types.NewField(src.NoXPos, v.Sym(), v.Type())
+ fld.Nname = v
+ params = append(params, fld)
+ }
+
+ // f is ONAME of the actual function.
+ f := clofn.Nname
+ typ := f.Type()
+
+ // Create new function type with parameters prepended, and
+ // then update type and declarations.
+ typ = types.NewSignature(typ.Pkg(), nil, append(params, typ.Params().FieldSlice()...), typ.Results().FieldSlice())
+ f.SetType(typ)
+ clofn.Dcl = append(decls, clofn.Dcl...)
+
+ // Rewrite call.
+ n.X = f
+ n.Args.Prepend(closureArgs(clo)...)
+
+ // Update the call expression's type. We need to do this
+ // because typecheck gave it the result type of the OCLOSURE
+ // node, but we only rewrote the ONAME node's type. Logically,
+ // they're the same, but the stack offsets probably changed.
+ if typ.NumResults() == 1 {
+ n.SetType(typ.Results().Field(0).Type)
+ } else {
+ n.SetType(typ.Results())
}
- base.Pos = lno
+ // Add to Closures for enqueueFunc. It's no longer a proper
+ // closure, but we may have already skipped over it in the
+ // functions list as a non-trivial closure, so this just
+ // ensures it's compiled.
+ ir.CurFunc.Closures = append(ir.CurFunc.Closures, clofn)
}
func walkClosure(clo *ir.ClosureExpr, init *ir.Nodes) ir.Node {
- fn := clo.Func
+ clofn := clo.Func
// If no closure vars, don't bother wrapping.
if ir.IsTrivialClosure(clo) {
if base.Debug.Closure > 0 {
base.WarnfAt(clo.Pos(), "closure converted to global")
}
- return fn.Nname
+ return clofn.Nname
}
+
+ // The closure is not trivial or directly called, so it's going to stay a closure.
ir.ClosureDebugRuntimeCheck(clo)
+ clofn.SetNeedctxt(true)
+ ir.CurFunc.Closures = append(ir.CurFunc.Closures, clofn)
typ := typecheck.ClosureType(clo)
clos := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(typ), nil)
clos.SetEsc(clo.Esc())
- clos.List = append([]ir.Node{ir.NewUnaryExpr(base.Pos, ir.OCFUNC, fn.Nname)}, closureArgs(clo)...)
+ clos.List = append([]ir.Node{ir.NewUnaryExpr(base.Pos, ir.OCFUNC, clofn.Nname)}, closureArgs(clo)...)
addr := typecheck.NodAddr(clos)
addr.SetEsc(clo.Esc())
diff --git a/src/cmd/compile/internal/walk/expr.go b/src/cmd/compile/internal/walk/expr.go
index 3dffb496e9..893a95f403 100644
--- a/src/cmd/compile/internal/walk/expr.go
+++ b/src/cmd/compile/internal/walk/expr.go
@@ -52,19 +52,15 @@ func walkExpr(n ir.Node, init *ir.Nodes) ir.Node {
base.Fatalf("expression has untyped type: %+v", n)
}
- if n.Op() == ir.ONAME && n.(*ir.Name).Class == ir.PAUTOHEAP {
- n := n.(*ir.Name)
- nn := ir.NewStarExpr(base.Pos, n.Heapaddr)
- nn.X.MarkNonNil()
- return walkExpr(typecheck.Expr(nn), init)
- }
-
n = walkExpr1(n, init)
// Eagerly compute sizes of all expressions for the back end.
if typ := n.Type(); typ != nil && typ.Kind() != types.TBLANK && !typ.IsFuncArgStruct() {
types.CheckSize(typ)
}
+ if n, ok := n.(*ir.Name); ok && n.Heapaddr != nil {
+ types.CheckSize(n.Heapaddr.Type())
+ }
if ir.IsConst(n, constant.String) {
// Emit string symbol now to avoid emitting
// any concurrently during the backend.
@@ -166,7 +162,7 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node {
n := n.(*ir.CallExpr)
return mkcall("gorecover", n.Type(), init, typecheck.NodAddr(ir.RegFP))
- case ir.OCLOSUREREAD, ir.OCFUNC:
+ case ir.OCFUNC:
return n
case ir.OCALLINTER, ir.OCALLFUNC, ir.OCALLMETH:
@@ -493,23 +489,7 @@ func walkCall(n *ir.CallExpr, init *ir.Nodes) ir.Node {
}
if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.OCLOSURE {
- // Transform direct call of a closure to call of a normal function.
- // transformclosure already did all preparation work.
-
- // Prepend captured variables to argument list.
- clo := n.X.(*ir.ClosureExpr)
- n.Args.Prepend(closureArgs(clo)...)
-
- // Replace OCLOSURE with ONAME/PFUNC.
- n.X = clo.Func.Nname
-
- // Update type of OCALLFUNC node.
- // Output arguments had not changed, but their offsets could.
- if n.X.Type().NumResults() == 1 {
- n.SetType(n.X.Type().Results().Field(0).Type)
- } else {
- n.SetType(n.X.Type().Results())
- }
+ directClosureCall(n)
}
walkCall1(n, init)
diff --git a/src/cmd/compile/internal/walk/walk.go b/src/cmd/compile/internal/walk/walk.go
index 4a89d2201d..4271772fb7 100644
--- a/src/cmd/compile/internal/walk/walk.go
+++ b/src/cmd/compile/internal/walk/walk.go
@@ -481,7 +481,7 @@ func calcHasCall(n ir.Node) bool {
n := n.(*ir.SelectorExpr)
return n.X.HasCall()
- case ir.OGETG, ir.OCLOSUREREAD, ir.OMETHEXPR:
+ case ir.OGETG, ir.OMETHEXPR:
return false
// TODO(rsc): These look wrong in various ways but are what calcHasCall has always done.
diff --git a/test/fixedbugs/issue20250.go b/test/fixedbugs/issue20250.go
index c190515274..1a513bea56 100644
--- a/test/fixedbugs/issue20250.go
+++ b/test/fixedbugs/issue20250.go
@@ -1,4 +1,4 @@
-// errorcheck -0 -live -l -d=compilelater
+// errorcheck -0 -live -l
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
diff --git a/test/fixedbugs/issue20780.go b/test/fixedbugs/issue20780.go
index 53c4f615e1..f73e6d1f79 100644
--- a/test/fixedbugs/issue20780.go
+++ b/test/fixedbugs/issue20780.go
@@ -9,11 +9,17 @@
package main
+type Big = [400e6]byte
+
func f() { // GC_ERROR "stack frame too large"
- var x [800e6]byte
- g(x)
- return
+ // Note: This test relies on the fact that we currently always
+ // spill function-results to the stack, even if they're so
+ // large that we would normally heap allocate them. If we ever
+ // improve the backend to spill temporaries to the heap, this
+ // test will probably need updating to find some new way to
+ // construct an overly large stack frame.
+ g(h(), h())
}
-//go:noinline
-func g([800e6]byte) {}
+func g(Big, Big)
+func h() Big
diff --git a/test/fixedbugs/issue20780b.go b/test/fixedbugs/issue20780b.go
new file mode 100644
index 0000000000..c8bf1f8349
--- /dev/null
+++ b/test/fixedbugs/issue20780b.go
@@ -0,0 +1,62 @@
+// +build cgo,linux,amd64
+// run -race
+
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that CL 281293 doesn't interfere with race detector
+// instrumentation.
+
+package main
+
+import "fmt"
+
+const N = 2e6
+
+type Big = [N]int
+
+var sink interface{}
+
+func main() {
+ g(0, f(0))
+
+ x1 := f(1)
+ sink = &x1
+ g(1, x1)
+ g(7, f(7))
+ g(1, x1)
+
+ x3 := f(3)
+ sink = &x3
+ g(1, x1)
+ g(3, x3)
+
+ h(f(0), x1, f(2), x3, f(4))
+}
+
+//go:noinline
+func f(k int) (x Big) {
+ for i := range x {
+ x[i] = k*N + i
+ }
+ return
+}
+
+//go:noinline
+func g(k int, x Big) {
+ for i := range x {
+ if x[i] != k*N+i {
+ panic(fmt.Sprintf("x%d[%d] = %d", k, i, x[i]))
+ }
+ }
+}
+
+//go:noinline
+func h(x0, x1, x2, x3, x4 Big) {
+ g(0, x0)
+ g(1, x1)
+ g(2, x2)
+ g(3, x3)
+ g(4, x4)
+}
diff --git a/test/fixedbugs/issue43633.dir/a.go b/test/fixedbugs/issue43633.dir/a.go
new file mode 100644
index 0000000000..946a37e87e
--- /dev/null
+++ b/test/fixedbugs/issue43633.dir/a.go
@@ -0,0 +1,28 @@
+// Copyright 2021 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 a
+
+func F() bool {
+ {
+ x := false
+ _ = x
+ }
+ if false {
+ _ = func(x bool) {}
+ }
+ x := true
+ return x
+}
+
+func G() func() bool {
+ x := true
+ return func() bool {
+ {
+ x := false
+ _ = x
+ }
+ return x
+ }
+}
diff --git a/test/fixedbugs/issue43633.dir/main.go b/test/fixedbugs/issue43633.dir/main.go
new file mode 100644
index 0000000000..320e00013c
--- /dev/null
+++ b/test/fixedbugs/issue43633.dir/main.go
@@ -0,0 +1,18 @@
+// Copyright 2021 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 "./a"
+
+var g = a.G()
+
+func main() {
+ if !a.F() {
+ panic("FAIL")
+ }
+ if !g() {
+ panic("FAIL")
+ }
+}
diff --git a/test/fixedbugs/issue43633.go b/test/fixedbugs/issue43633.go
new file mode 100644
index 0000000000..40df49f83b
--- /dev/null
+++ b/test/fixedbugs/issue43633.go
@@ -0,0 +1,7 @@
+// rundir
+
+// Copyright 2021 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 ignored
diff --git a/test/fixedbugs/issue7921.go b/test/fixedbugs/issue7921.go
index 5dce557ca3..a4e7b246d4 100644
--- a/test/fixedbugs/issue7921.go
+++ b/test/fixedbugs/issue7921.go
@@ -41,7 +41,7 @@ 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\]$" "inlining call to bytes.\(\*Buffer\).Grow$"
useBuffer(&b)
return b.Bytes() // ERROR "inlining call to bytes.\(\*Buffer\).Bytes$"
}
diff --git a/test/nilptr3.go b/test/nilptr3.go
index e0f2ed9767..3345cfa5ab 100644
--- a/test/nilptr3.go
+++ b/test/nilptr3.go
@@ -214,14 +214,6 @@ func p1() byte {
return p[5] // ERROR "removed nil check"
}
-// make sure not to do nil check for access of PAUTOHEAP
-//go:noinline
-func (p *Struct) m() {}
-func c1() {
- var x Struct
- func() { x.m() }() // ERROR "removed nil check"
-}
-
type SS struct {
x byte
}